posts - 51, comments - 17, trackbacks - 0, articles - 9
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          Flyweight模式定義:
          避免大量擁有相同內容的小類的開銷(如耗費內存),使大家共享一個類(元類).

          為什么使用?
          面向對象語言的原則就是一切都是對象,但是如果真正使用起來,有時對象數可能顯得很龐大,比如,字處理軟件,如果以每個文字都作為一個對象,幾千個字,對象數就是幾千,無疑耗費內存,那么我們還是要"求同存異",找出這些對象群的共同點,設計一個元類,封裝可以被共享的類,另外,還有一些特性是取決于應用(context),是不可共享的,這也Flyweight中兩個重要概念內部狀態intrinsic和外部狀態extrinsic之分.

          說白點,就是先捏一個的原始模型,然后隨著不同場合和環境,再產生各具特征的具體模型,很顯然,在這里需要產生不同的新對象,所以Flyweight模式中常出現Factory模式.Flyweight的內部狀態是用來共享的,Flyweight factory負責維護一個Flyweight pool(模式池)來存放內部狀態的對象.

          Flyweight模式是一個提高程序效率和性能的模式,會大大加快程序的運行速度.應用場合很多:比如你要從一個數據庫中讀取一系列字符串,這些字符串中有許多是重復的,那么我們可以將這些字符串儲存在Flyweight池(pool)中.

          如何使用?

          我們先從Flyweight抽象接口開始:

          public interface Flyweight
          {
            public void operation( ExtrinsicState state );
          }

          //用于本模式的抽象數據類型(自行設計)
          public interface ExtrinsicState { }

          下面是接口的具體實現(ConcreteFlyweight) ,并為內部狀態增加內存空間, ConcreteFlyweight必須是可共享的,它保存的任何狀態都必須是內部(intrinsic),也就是說,ConcreteFlyweight必須和它的應用環境場合無關.

          public class ConcreteFlyweight implements Flyweight {
            private IntrinsicState state;
            
            public void operation( ExtrinsicState state )
            {
                //具體操作
            }

          }

          當然,并不是所有的Flyweight具體實現子類都需要被共享的,所以還有另外一種不共享的ConcreteFlyweight:

          public class UnsharedConcreteFlyweight implements Flyweight {

            public void operation( ExtrinsicState state ) { }

          }

          Flyweight factory負責維護一個Flyweight池(存放內部狀態),當客戶端請求一個共享Flyweight時,這個factory首先搜索池中是否已經有可適用的,如果有,factory只是簡單返回送出這個對象,否則,創建一個新的對象,加入到池中,再返回送出這個對象.池

          public class FlyweightFactory {
            //Flyweight pool
            private Hashtable flyweights = new Hashtable();

            public Flyweight getFlyweight( Object key ) {

              Flyweight flyweight = (Flyweight) flyweights.get(key);

              if( flyweight == null ) {
                //產生新的ConcreteFlyweight
                flyweight = new ConcreteFlyweight();
                flyweights.put( key, flyweight );
              }

              return flyweight;
            }
          }

          至此,Flyweight模式的基本框架已經就緒,我們看看如何調用:

          FlyweightFactory factory = new FlyweightFactory();
          Flyweight fly1 = factory.getFlyweight( "Fred" );
          Flyweight fly2 = factory.getFlyweight( "Wilma" );
          ......

          從調用上看,好象是個純粹的Factory使用,但奧妙就在于Factory的內部設計上.

          Flyweight模式在XML等數據源中應用
          我們上面已經提到,當大量從數據源中讀取字符串,其中肯定有重復的,那么我們使用Flyweight模式可以提高效率,以唱片CD為例,在一個XML文件中,存放了多個CD的資料.

          每個CD有三個字段:
          1.出片日期(year)
          2.歌唱者姓名等信息(artist)
          3.唱片曲目 (title)

          其中,歌唱者姓名有可能重復,也就是說,可能有同一個演唱者的多個不同時期 不同曲目的CD.我們將"歌唱者姓名"作為可共享的ConcreteFlyweight.其他兩個字段作為UnsharedConcreteFlyweight.

          首先看看數據源XML文件的內容:


          <?xml version="1.0"?>
          <collection>

          <cd>
          <title>Another Green World</title>
          <year>1978</year>
          <artist>Eno, Brian</artist>
          </cd>

          <cd>
          <title>Greatest Hits</title>
          <year>1950</year>
          <artist>Holiday, Billie</artist>
          </cd>

          <cd>
          <title>Taking Tiger Mountain (by strategy)</title>
          <year>1977</year>
          <artist>Eno, Brian</artist>
          </cd>

          .......

          </collection>


          雖然上面舉例CD只有3張,CD可看成是大量重復的小類,因為其中成分只有三個字段,而且有重復的(歌唱者姓名).

          CD就是類似上面接口 Flyweight:


          public class CD {

            private String title;
            private int year;
            private Artist artist;

            public String getTitle() {  return title; }
            public int getYear() {    return year;  }
            public Artist getArtist() {    return artist;  }

            public void setTitle(String t){    title = t;}
            public void setYear(int y){year = y;}
            public void setArtist(Artist a){artist = a;}

          }

          將"歌唱者姓名"作為可共享的ConcreteFlyweight:

          public class Artist {

            //內部狀態
            private String name;

            // note that Artist is immutable.
            String getName(){return name;}

            Artist(String n){
              name = n;
            }

          }

          再看看Flyweight factory,專門用來制造上面的可共享的ConcreteFlyweight:Artist

          public class ArtistFactory {

            Hashtable pool = new Hashtable();

            Artist getArtist(String key){

              Artist result;
              result = (Artist)pool.get(key);
              ////產生新的Artist
              if(result == null) {
                result = new Artist(key);
                pool.put(key,result);
                
              }
              return result;
            }

          }

          當你有幾千張甚至更多CD時,Flyweight模式將節省更多空間,共享的flyweight越多,空間節省也就越大.


          給個例子,coffee商店

          package FlyWeight.coffeeshop;

          public class Table {
          ?
          ?private int number;

          ?public int getNumber() {
          ??return number;
          ?}

          ?public void setNumber(int number) {
          ??this.number = number;
          ?}

          ?public Table(int number) {
          ??super();
          ??// TODO Auto-generated constructor stub
          ??this.number = number;
          ?}

          }


          package FlyWeight.coffeeshop;

          public abstract class Order {
          ?
          ?public abstract void serve(Table table);
          ?
          ?public abstract String getFlavor();

          }


          package FlyWeight.coffeeshop;

          public class Flavor extends Order {

          ?private String flavor;

          ?public Flavor(String flavor) {
          ??super();
          ??// TODO Auto-generated constructor stub
          ??this.flavor = flavor;
          ?}

          ?public String getFlavor() {
          ??return flavor;
          ?}

          ?public void setFlavor(String flavor) {
          ??this.flavor = flavor;
          ?}

          ?public void serve(Table table) {
          ??System.out.println("Serving table " + table.getNumber() + " with flavor " + flavor );
          ?}?
          }

          package FlyWeight.coffeeshop;

          public class FlavorFactory {

          ?private Order[] flavors = new Flavor[10];
          ?private int ordersMade = 0;//已經處理好的訂單數
          ?private int totalFlavors = 0;//已購買的coffee風味種類數
          ?
          ?public Order getOrder(String flavorToGet){
          ??if(ordersMade > 0){
          ???for(int i=0; i<ordersMade; i++){
          ????if(flavorToGet.equalsIgnoreCase(flavors[i].getFlavor()))
          ?????return flavors[i];
          ???}
          ??}
          ??flavors[ordersMade] = new Flavor(flavorToGet);
          ??totalFlavors++;
          ??return flavors[ordersMade++];
          ?}
          ?
          ?public int getTotalFlavorsMade(){
          ??return totalFlavors;
          ?}
          }


          package FlyWeight.coffeeshop;

          public class Client {
          ?
          ?private static Order[] flavors = new Flavor[100];
          ?
          ?private static int ordersMade = 0;
          ?private static FlavorFactory flavorFactory;
          ?
          ?private static void takeOrders(String aFlavor){
          ??flavors[ordersMade++] = flavorFactory.getOrder(aFlavor);
          ?}
          ?
          ?public static void main(String[] args){
          ??flavorFactory = new FlavorFactory();
          ??
          ???? takeOrders("Black Coffee");
          ???? takeOrders("Capucino");
          ???? takeOrders("Espresso");
          ???? takeOrders("Espresso");
          ???? takeOrders("Capucino");
          ???? takeOrders("Capucino");
          ???? takeOrders("Black Coffee");
          ???? takeOrders("Espresso");
          ???? takeOrders("Capucino");
          ???? takeOrders("Black Coffee");
          ???? takeOrders("Espresso");
          ????
          ???? for(int i=0; i<ordersMade; i++){
          ???? ?flavors[i].serve(new Table(i));
          ???? }
          ????
          ???? System.out.println("\nTotal Flavor objrcts made: " +
          ???? ??flavorFactory.getTotalFlavorsMade());
          ?}
          ?
          }

          //-------------------------------------------------------------------

          運行結果:

          Serving table 0 with flavor Black Coffee
          Serving table 1 with flavor Capucino
          Serving table 2 with flavor Espresso
          Serving table 3 with flavor Espresso
          Serving table 4 with flavor Capucino
          Serving table 5 with flavor Capucino
          Serving table 6 with flavor Black Coffee
          Serving table 7 with flavor Espresso
          Serving table 8 with flavor Capucino
          Serving table 9 with flavor Black Coffee
          Serving table 10 with flavor Espresso

          Total Flavor objrcts made: 3

          posted @ 2007-03-23 19:45 chenweicai 閱讀(294) | 評論 (0)編輯 收藏

          關 鍵 詞: Java??模式
          閱讀提示:裝飾模式是在不必改變原類文件和使用繼承的情況下,動態的擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。

          說明:

          裝飾模式是在不必改變原類文件和使用繼承的情況下,動態的擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。

          裝飾模式的特點;

          (1) 裝飾對象和真實對象有相同的接口。這樣客戶端對象就可以以和真實對象相同的方式和裝飾對象交互。

          (2) 裝飾對象包含一個真實對象的索引(reference)

          (3) 裝飾對象接受所有的來自客戶端的請求。它把這些請求轉發給真實的對象。

          (4) 裝飾對象可以在轉發這些請求以前或以后增加一些附加功能。這樣就確保了在運行時,不用修改給定對象的結構就可以在外部增加附加的功能。在面向對象的設計中,通常是通過繼承來實現對給定類的功能擴展。

          下表格列舉了裝飾模式和繼承的不同:

          裝飾模式 VS 繼承

          裝飾模式 繼承

          用來擴展特定對象的功能 用來擴展一類對象的功能

          不需要子類 需要子類

          動態地 靜態地

          運行時分配職責 編譯時分派職責

          防止由于子類而導致的復雜和混亂 導致很多子類產生,在一些場合,報漏類的層次

          更多的靈活性 缺乏靈活性

          對于一個給定的對象,同時可能有不同的裝飾對象,客戶端可以通過它的需要選擇合適的裝飾對象發送消息。 對于所有可能的聯合,客戶期望

          很容易增加任何的 困難

          給個實例,打印發票



          package Decorator.printorder;
          import java.text.NumberFormat;
          public class OrderLine {
          ?private String itemName;
          ?private int units;
          ?private double unitPrice;
          ?
          ?public String getItemName() {
          ??return itemName;
          ?}
          ?
          ?public void setItemName(String itemName) {
          ??this.itemName = itemName;
          ?}
          ?
          ?public double getUnitPrice() {
          ??return unitPrice;
          ?}
          ?
          ?public void setUnitPrice(double unitPrice) {
          ??this.unitPrice = unitPrice;
          ?}
          ?
          ?public int getUnits() {
          ??return units;
          ?}
          ?
          ?public void setUnits(int units) {
          ??this.units = units;
          ?}
          ?
          ?public void printLine(){
          ??System.out.println(itemName + "\t" + units
          ????+ "\t" + formatCurrency(unitPrice)
          ????+ "\t\t" + formatCurrency(getSubtotal()));
          ?}
          ?
          ?public double getSubtotal(){
          ??return units * unitPrice;
          ?}
          ?
          ?private String formatCurrency(double amnt){
          ??return NumberFormat.getCurrencyInstance().format(amnt);
          ?}
          }


          package Decorator.printorder;
          import java.text.NumberFormat;
          import java.util.Date;
          import java.util.Vector;
          abstract public class Order {
          ?
          ?private OrderLine lnkOrderLine;
          ?protected String customerName;
          ?protected Date salesDate;
          ?protected Vector items = new Vector(10);
          ?
          ?public void print(){
          ??for(int i=0; i<items.size(); i++){
          ???OrderLine item = (OrderLine) items.get(i);
          ???item.printLine();
          ??}
          ?}
          ?public String getCustomerName() {
          ??return customerName;
          ?}
          ?public void setCustomerName(String customerName) {
          ??this.customerName = customerName;
          ?}
          ?public Date getSalesDate() {
          ??return salesDate;
          ?}
          ?public void setSalesDate(Date salesDate) {
          ??this.salesDate = salesDate;
          ?}
          ?
          ?public void addItem(OrderLine item){
          ??items.add(item);
          ?}
          ?
          ?public void remove(OrderLine item){
          ??items.remove(item);
          ?}
          ?
          ?public double getGrandTotal(){
          ??double amnt = 0.0D;
          ??for(int i=0; i<items.size(); i++){
          ???OrderLine item = (OrderLine) items.get(i);
          ???amnt += item.getSubtotal();
          ??}
          ??return amnt;
          ?}
          ?
          ?protected String formatCurrency(double amnt){
          ??return NumberFormat.getCurrencyInstance().format(amnt);
          ?}
          }


          package Decorator.printorder;
          public class SalesOrder extends Order {
          ?public SalesOrder() {
          ??super();
          ??// TODO Auto-generated constructor stub
          ?}
          ?public void print() {
          ??// TODO Auto-generated method stub
          ??super.print();
          ?}
          ?
          }


          package Decorator.printorder;
          public abstract class OrderDecorator extends Order{
          ?protected Order order;
          ?
          ?public OrderDecorator(Order order){
          ??this.order = order;
          ??this.setSalesDate(this.order.getSalesDate());
          ??this.setCustomerName(this.order.getCustomerName());
          ?}
          }


          package Decorator.printorder;
          public class HeaderDecorator extends OrderDecorator {
          ?public HeaderDecorator(Order order) {
          ??super(order);
          ??// TODO Auto-generated constructor stub
          ?}
          ?public void print(){
          ??this.printHeader();
          ??super.order.print();
          ?}
          ?
          ?private void printHeader(){
          ??System.out.println("\t***\tI N V O I C E\t***\nXYZ Incorporated\nDate of Sale: "
          ????+ order.getSalesDate());
          ??????? System.out.println("========================================================");
          ??????? System.out.println("Item\t\tUnits\tUnit Price\tSubtotal");
          ?}
          }


          package Decorator.printorder;
          public class FooterDecorator extends OrderDecorator {
          ?public FooterDecorator(Order order) {
          ??super(order);
          ??// TODO Auto-generated constructor stub
          ?}
          ?public void print(){
          ??super.order.print();
          ??printFooter();
          ?}
          ?
          ?private void printFooter(){
          ??????? System.out.println("========================================================");
          ??????? System.out.println("Total\t\t\t\t\t" +
          ??????????? formatCurrency(super.order.getGrandTotal()));
          ?}
          }


          package Decorator.printorder;
          import java.util.Date;

          public class Client {
          ?private static Order order;
          ?
          ?public static void main(String[] args){
          ??order = new SalesOrder();
          ??order.setSalesDate(new Date());
          ??order.setCustomerName("XYZ Repair Shop");
          ??
          ??????? OrderLine line1 = new OrderLine();
          ??????? line1.setItemName("FireWheel Tire");
          ??????? line1.setUnitPrice(154.23);
          ??????? line1.setUnits(4);
          ??????? order.addItem(line1);
          ??????? OrderLine line2 = new OrderLine();
          ??????? line2.setItemName("Front Fender");
          ??????? line2.setUnitPrice(300.45);
          ??????? line2.setUnits(1);
          ??????? order.addItem(line2);
          ???????
          ??????? order = new HeaderDecorator(new FooterDecorator(order));
          ???????
          ??????? order.print();
          ???????
          ?}
          }


          posted @ 2007-03-23 15:56 chenweicai 閱讀(967) | 評論 (0)編輯 收藏

          小結:適配器模式用插座的適配器最為形象,插頭是2口的,插座是3口的,中間的適配器就是同時支持2口和三口的。從對象的角度就是一般繼承一個實現一個,總之,前方百計把兩者都關聯起來 。


          通常,客戶類(clients?of?class)通過類的接口訪問它提供的服務。有時,現有的類(existing?class)可以提供客戶類的功能需要,但是它所提供的接口不一定是客戶類所期望的。這是由于現有的接口太詳細或者缺乏詳細或接口的名稱與客戶類所查找的不同等諸多不同原因導致的。

            在這種情況下,現有的接口需要轉化(convert)為客戶類期望的接口,這樣保證了對現有類的重用。如果不進行這樣的轉化,客戶類就不能利用現有類所提供的功能。適配器模式(Adapter?Pattern)可以完成這樣的轉化。適配器模式建議定義一個包裝類,包裝有不兼容接口的對象。這個包裝類指的就是適配器(Adapter),它包裝的對象就是適配者(Adaptee)。適配器提供客戶類需要的接口,適配器接口的實現是把客戶類的請求轉化為對適配者的相應接口的調用。換句話說:當客戶類調用適配器的方法時,在適配器類的內部調用適配者類的方法,這個過程對客戶類是透明的,客戶類并不直接訪問適配者類。因此,適配器可以使由于借口不兼容而不能交互的類可以一起工作(work?together)。

            在上面討論的接口:

            (1)????不是指在JAVA編程語言中接口的概念,雖然類的接口可以通過JAVA借擴來定義。

            (2)????不是指由窗體和GUI控件所組成的GUI應用程序的用戶接口。

            (3)????而是指類所報漏的,被其他類調用的編程接口,

            類適配器(Class?Adapter)VS對象適配器(Object?Adapter)

            適配器總體上可以分為兩類??類適配器(Class?Adapter)VS對象適配器(Object?Adapter)
          ????

           類適配器:


            類適配器是通過繼承類適配者類(Adaptee?Class)實現的,另外類適配器實現客戶類所需要的接口。當客戶對象調用適配器類方法的時候,適配器內部調用它所繼承的適配者的方法。
          ????

           對象適配器:

            對象適配器包含一個適配器者的引用(reference),與類適配器相同,對象適配器也實現了客戶類需要的接口。當客戶對象調用對象適配器的方法的時候,對象適配器調它所包含的適配器者實例的適當方法。

            下表是類適配器(Class?Adapter)和對象適配器(Object?Adapter)的詳細不同

            
          ????
            例子:

            讓我們建立一個驗證給定客戶地址的應用。這個應用是作為大的客戶數據管理應用的一部分。

            讓我們定義一個Customer類:

          Customer?



          Figure?20.1:?Customer?Class?
          Listing?20.1:?Customer?Class?
          1. class?Customer?{?
          2. ??public?static?final?String?US?=?"US";?
          3. ??public?static?final?String?CANADA?=?"Canada";?
          4. ??private?String?address;?
          5. ??private?String?name;?
          6. ??private?String?zip,?state,?type;?
          7. ??public?boolean?isValidAddress()?{?
          8. ??????????…?
          9. ??????????…?
          10. ??}?
          11. ??public?Customer(String?inp_name,?String?inp_address,?
          12. ??????????????????String?inp_zip,?String?inp_state,?
          13. ??????????????????String?inp_type)?{?
          14. ????name?=?inp_name;?
          15. ????address?=?inp_address;?
          16. ????zip?=?inp_zip;?
          17. ????state?=?inp_state;?
          18. ????type?=?inp_type;?
          19. ??}?
          20. }//end?of?class?
            不同的客戶對象創建Customer對象并調用(invoke)isValidAddress方法驗證客戶地址的有效性。為了驗證客戶地址的有效性, Customer類期望利用一個地址驗證類(address?validator?class),這個驗證類提供了在接口 AddressValidator中聲明的接口。

            Listing?20.2:?AddressValidator?as?an?Interface?
          1. public?interface?AddressValidator?{?
          2. ??public?boolean?isValidAddress(String?inp_address,?
          3. ?????String?inp_zip,?String?inp_state);?
          4. }//end?of?class?

            讓我們定義一個USAddress的驗證類,來驗證給定的U.S地址。

            Listing?20.3:?USAddress?Class?
          1. class?USAddress?implements?AddressValidator?{?
          2. ??public?boolean?isValidAddress(String?inp_address,?
          3. ?????String?inp_zip,?String?inp_state)?{?
          4. ???if?(inp_address.trim().length()?<?10)?
          5. ?????return?false;?
          6. ???if?(inp_zip.trim().length()?<?5)?
          7. ?????return?false;?
          8. ???if?(inp_zip.trim().length()?>?10)?
          9. ?????return?false;?
          10. ???if?(inp_state.trim().length()?!=?2)?
          11. ?????return?false;?
          12. ???return?true;?
          13. ??}?
          14. }//end?of?class?

            USAddress類實現AddressValidator接口,因此Customer對象使用USAddress實例作為驗證客戶地址過程的一部分是沒有任何問題的。

            Listing?20.4:?Customer?Class?Using?the?USAddress?Class?
          1. class?Customer?{?
          2. ??????????…?
          3. ??????????…?
          4. ?public?boolean?isValidAddress()?{?
          5. ???//get?an?appropriate?address?validator?
          6. ???AddressValidator?validator?=?getValidator(type);?
          7. ???//Polymorphic?call?to?validate?the?address?
          8. ???return?validator.isValidAddress(address,?zip,?state);?
          9. ?}?
          10. ?private?AddressValidator?getValidator(String?custType)?{?
          11. ???AddressValidator?validator?=?null;?
          12. ???if?(custType.equals(Customer.US))?{?
          13. ?????validator?=?new?USAddress();?
          14. ???}?
          15. ???return?validator;?
          16. ?}?
          17. }//end?of?class?
          ?


          Figure?20.2:?Customer/USAddress?Validator?Class?Association?

            但是當驗證來自加拿大的客戶時,就要對應用進行改進。這需要一個驗證加拿大客戶地址的驗證類。讓我們假設已經存在一個用來驗證加拿大客戶地址的使用工具類CAAddress。

          從下面的CAAdress類的實現,可以發現CAAdress提供了客戶類Customer類所需要的驗證服務。但是它所提供的接口不用于客戶類Customer所期望的。

            Listing?20.5:?CAAdress?Class?with?Incompatible?Interface?
          1. class?CAAddress?{?
          2. ??public?boolean?isValidCanadianAddr(String?inp_address,?
          3. ?????String?inp_pcode,?String?inp_prvnc)?{?
          4. ???if?(inp_address.trim().length()?<?15)?
          5. ?????return?false;?
          6. ???if?(inp_pcode.trim().length()?!=?6)?
          7. ?????return?false;?
          8. ???if?(inp_prvnc.trim().length()?<?6)?
          9. ?????return?false;?
          10. ???return?true;?
          11. ??}?
          12. }//end?of?class?

            CAAdress類提供了一個isValidCanadianAddr方法,但是Customer期望一個聲明在AddressValidator接口中的isValidAddress方法。

            接口的不兼容使得Customer對象利用現有的CAAdress類是困難的。一種意見是改變CAAdress類的接口,但是可能會有其他的應用正在使用CAAdress類的這種形式。改變CAAdress類接口會影響現在使用CAAdress類的客戶。

            應用適配器模式,類適配器CAAdressAdapter可以繼承CAAdress類實現AddressValidator接口。

          ?


            Figure?20.3:?Class?Adapter?for?the?CAAddress?Class?
          Listing?20.6:?CAAddressAdapter?as?a?Class?Adapter?
          1. public?class?CAAddressAdapter?extends?CAAddress?
          2. ??implements?AddressValidator?{?
          3. ??public?boolean?isValidAddress(String?inp_address,?
          4. ?????String?inp_zip,?String?inp_state)?{?
          5. ????return?isValidCanadianAddr(inp_address,?inp_zip,?
          6. ???????????inp_state);?
          7. ??}?
          8. }//end?of?class?

            因為適配器CAAdressAdapter實現了AddressValidator接口,客戶端對象訪問適配器CAAdressAdapter對象是沒有任何問題的。當客戶對象調用適配器實例的isValidAddress方法的時候,適配器在內部把調用傳遞給它繼承的 isValidCanadianAddr方法。

            在Customer類內部,getValidator私有方法需要擴展,以至于它可以在驗證加拿大客戶的時候返回一個CAAdressAdapter實例。返回的對象是多態的,USAddress和CAAddressAdapter都實現了AddressValidator接口,所以不用改變。

          Listing?20.7:?Customer?Class?Using?the?CAAddressAdapter?Class?
          1. class?Customer?{?
          2. ??????????…?
          3. ??????????…?
          4. ??public?boolean?isValidAddress()?{?
          5. ????//get?an?appropriate?address?validator?
          6. ????AddressValidator?validator?=?getValidator(type);?
          7. ????//Polymorphic?call?to?validate?the?address?
          8. ????return?validator.isValidAddress(address,?zip,?state);?
          9. ??}?
          10. ??private?AddressValidator?getValidator(String?custType)?{?
          11. ????AddressValidator?validator?=?null;?
          12. ????if?(custType.equals(Customer.US))?{?
          13. ??????validator?=?new?USAddress();?
          14. ????}?
          15. ????if?(type.equals(Customer.CANADA))?{?
          16. ??????validator?=?new?CAAddressAdapter();?
          17. ????}?
          18. ????return?validator;?
          19. ??}?
          20. }//end?of?class?
            CAAddressAdapter設計和對AddressValidator(聲明期望的接口)對象的多態調用使Customer可以利用接口不兼容CAAddress類提供的服務。

          ?


            Figure?20.4:?Address?Validation?Application?Using?Class?Adapter?

          ?


            Figure?20.5:?Address?Validation?Message?Flow?Using?Class?Adapter?

            作為對象適配器的地址適配器

            當討論以類適配器來實現地址適配器時,我們說客戶類期望的AddressValidator接口是Java接口形式。現在,讓我們假設客戶類期望 AddressValidator接口是抽象類而不是java接口。因為適配器CAAdapter必須提供抽象類AddressValidatro中聲明的接口,適配器必須是AddressValidator抽象類的子類、實現抽象方法。
          1. Listing?20.8:?AddressValidator?as?an?Abstract?Class?
          2. public?abstract?class?AddressValidator?{?
          3. ??public?abstract?boolean?isValidAddress(String?inp_address,?
          4. ?????String?inp_zip,?String?inp_state);?
          5. }//end?of?class?
          6. Listing?20.9:?CAAddressAdapter?Class?
          7. class?CAAddressAdapter?extends?AddressValidator?{?
          8. ??????????…?
          9. ??????????…?
          10. ??public?CAAddressAdapter(CAAddress?address)?{?
          11. ????objCAAddress?=?address;?
          12. ??}?
          13. ??public?boolean?isValidAddress(String?inp_address,?
          14. ?????String?inp_zip,?String?inp_state)?{?
          15. ??????????…?
          16. ??????????…?
          17. ??}?
          18. }//end?of?class?

            因為多繼承在JAVA中不支持,現在適配器CAAddressAdapter不能繼承現有的CAAddress類,它已經使用了唯一一次繼承其他類的機會。

            應用對象適配器模式,CAAddressAdapter可以包含一個適配者CAAddress的一個實例。當適配器第一次創建的時候,這個適配者的實例通過客戶端傳遞給適配器。通常,適配者實例可以通過下面兩種方式提供給包裝它的適配器。

            (1)????對象適配器的客戶端可以傳遞一個適配者的實例給適配器。這種方式在選擇類的形式上有很大的靈活性,但是客戶端感知了適配者或者適配過程。這種方法在適配器不但需要適配者對象行為而且需要特定狀態時很適合。

            (2)????適配器可以自己創建適配者實例。這種方法相對來說缺乏靈活性。適用于適配器只需要適配者對象的行為而不需要適配者對象的特定狀態的情況。

          ?


            Figure?20.6:?Object?Adapter?for?the?CAAddress?Class?

            Listing?20.10:?CAAddressAdapter?as?an?Object?Adapter?
          1. class?CAAddressAdapter?extends?AddressValidator?{?
          2. ??private?CAAddress?objCAAddress;?
          3. ??public?CAAddressAdapter(CAAddress?address)?{?
          4. ????objCAAddress?=?address;?
          5. ??}?
          6. ??public?boolean?isValidAddress(String?inp_address,?
          7. ?????String?inp_zip,?String?inp_state)?{?
          8. ????return?objCAAddress.isValidCanadianAddr(inp_address,?
          9. ???????????inp_zip,?inp_state);?
          10. ??}?
          11. }//end?of?class?

            當客戶對象調用CAAddressAdapter(adapter)上的isValidAddress方法時,?適配器在內部調用CAAddress(adaptee)上的isValidCanadianAddr方法。


          ?


            Figure?20.7:?Address?Validation?Application?Using?Object?Adapter?

            從這個例子可以看出,適配器可以使Customer(client)類訪問借口不兼容的CAAddress(adaptee)所提供的服務!

          ?



            Figure?20.8:?Address?Validation?Message?Flow?Using?Object?Adapter

          posted @ 2007-03-23 13:12 chenweicai 閱讀(337) | 評論 (0)編輯 收藏

          Spring的模塊化是很強的,各個功能模塊都是獨立的,我們可以選擇的使用。這一章先從Spring的IoC開始。所謂IoC就是一個用XML來定義生成對象的模式,我們看看如果來使用的。
          1、數據模型。
          1、如下圖所示有三個類,Human(人類)是接口,Chinese(中國人)是一個子類,American(美國人)是另外一個子類。


          源代碼如下:
          package cn.com.chengang.spring;
          public interface Human {
          ?????? void eat();
          ?????? void walk();
          }
          ?
          package cn.com.chengang.spring;
          public class Chinese implements Human {
          ??? /* (非 Javadoc)
          ???? * @see cn.com.chengang.spring.Human#eat()
          ???? */
          ??? public void eat() {
          ??????? System.out.println("中國人對吃很有一套");
          ??? }
          ?
          ??? /* (非 Javadoc)
          ???? * @see cn.com.chengang.spring.Human#walk()
          ???? */
          ??? public void walk() {
          ??????? System.out.println("中國人行如飛");
          ??? }
          }
          ?
          package cn.com.chengang.spring;
          public class American implements Human {
          ??? /* (非 Javadoc)
          ???? * @see cn.com.chengang.spring.Human#eat()
          ???? */
          ??? public void eat() {
          ??????? System.out.println("美國人主要以面包為主");
          ??? }
          ?
          ??? /* (非 Javadoc)
          ???? * @see cn.com.chengang.spring.Human#walk()
          ???? */
          ??? public void walk() {
          ??????? System.out.println("美國人以車代步,有四肢退化的趨勢");
          ??? }
          }
          ?
          2、對以上對象采用工廠模式的用法如下
          創建一個工廠類Factory,如下。這個工廠類里定義了兩個字符串常量,所標識不同的人種。getHuman方法根據傳入參數的字串,來判斷要生成什么樣的人種。
          package cn.com.chengang.spring;
          public class Factory {
          ??? public final static String CHINESE = "Chinese";
          ?? ?public final static String AMERICAN = "American";
          ?
          ??? public Human getHuman(String ethnic) {
          ??????? if (ethnic.equals(CHINESE))
          ??????????? return new Chinese();
          ??????? else if (ethnic.equals(AMERICAN))
          ??????????? return new American();
          ??????? else
          ??????????? throw new IllegalArgumentException("參數(人種)錯誤");
          ??? }
          }
          ?
          下面是一個測試的程序,使用工廠方法來得到了不同的“人種對象”,并執行相應的方法。
          package cn.com.chengang.spring;
          public class ClientTest {
          ??? public static void main(String[] args) {
          ??????? Human human = null;
          ??????? human = new Factory().getHuman(Factory.CHINESE);
          ??????? human.eat();
          ??????? human.walk();
          ??????? human = new Factory().getHuman(Factory.AMERICAN);
          ??????? human.eat();
          ??????? human.walk();
          ??? }
          }
          ?
          控制臺的打印結果如下:
          ?
          3、采用Spring的IoC的用法如下:
          1、在項目根目錄下創建一個bean.xml文件
          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
          <beans>
          ?????? <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>
          ?????? <bean id="American" class="cn.com.chengang.spring.American"/>
          </beans>
          bean.xml的位置如下圖,注意不要看花眼把它看成是lib目錄下的了,它是在myspring目錄下的。
          ?
          2、修改ClientTest程序如下:
          package cn.com.chengang.spring;
          import org.springframework.context.ApplicationContext;
          import org.springframework.context.support.FileSystemXmlApplicationContext;
          public class ClientTest {
          ??? public final static String CHINESE = "Chinese";
          ??? public final static String AMERICAN = "American";
          ?
          ??? public static void main(String[] args) {
          ??????? //??????? Human human = null;
          ??????? //??????? human = new Factory().getHuman(Factory.CHINESE);
          ??????? //??????? human.eat();
          ??????? //??????? human.walk();
          ??????? //??????? human = new Factory().getHuman(Factory.AMERICAN);
          ??????? //??????? human.eat();
          ??????? //??????? human.walk();
          ?
          ??????? ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
          ??????? Human human = null;
          ??????? human = (Human) ctx.getBean(CHINESE);
          ??????? human.eat();
          ??????? human.walk();
          ??????? human = (Human) ctx.getBean(AMERICAN);
          ??????? human.eat();
          ??????? human.walk();
          ??? }
          }
          從這個程序可以看到,ctx就相當于原來的Factory工廠,原來的Factory就可以刪除掉了。然后又把Factory里的兩個常量移到了ClientTest類里,整個程序結構基本一樣。
          再回頭看原來的bean.xml文件的這一句
          <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>
          id就是ctx.getBean的參數值,一個字符串。class就是一個類(包名+類名)。然后在ClientTest類里獲得Chinese對象就是這么一句
          human = (Human) ctx.getBean(CHINESE);
          因為getBean方法返回的是Object類型,所以前面要加一個類型轉換。
          ?
          4、總結
          (1)也許有人說,IoC和工廠模式不是一樣的作用嗎,用IoC好象還麻煩一點。
          ?????? 舉個例子,如果用戶需求發生變化,要把Chinese類修改一下。那么前一種工廠模式,就要更改Factory類的方法,并且重新編譯布署。而IoC只需 要將class屬性改變一下,并且由于IoC利用了Java反射機制,這些對象是動態生成的,這時我們就可以熱插撥Chinese對象(不必把原程序停止 下來重新編譯布署)
          ?
          ?????? (2)也許有人說,即然IoC這么好,那么我把系統所有對象都用IoC方式來生成。
          ?????? 注意,IoC的靈活性是有代價的:設置步驟麻煩、生成對象的方式不直觀、反射比正常生成對象在效率上慢一點。因此使用IoC要看有沒有必要,我認為比較通用的判斷方式是:用到工廠模式的地方都可以考慮用IoC模式。
          ?
          ?????? (3)在上面的IoC的方式里,還有一些可以變化的地方。比如,bean.xml不一定要放在項目錄下,也可以放在其他地方,比如cn.com.chengang.spring包里。不過在使用時也要變化一下,如下所示:
          new FileSystemXmlApplicationContext("src/cn/com/chengang/spring/bean.xml");
          另外,bean.xml也可以改成其他名字。這樣我們在系統中就可以分門別類的設置不同的bean.xml。
          ?
          (4)關于IoC的低侵入性。
          什 么是低侵入性?如果你用過Struts或EJB就會發現,要繼承一些接口或類,才能利用它們的框架開發。這樣,系統就被綁定在Struts、EJB上了, 對系統的可移植性產生不利的影響。如果代碼中很少涉及某一個框架的代碼,那么這個框架就可以稱做是一個低侵入性的框架。
          Spring的侵入性很低,Humen.java、Chinese.java等幾個類都不必繼承什么接口或類。但在ClientTest里還是有一些Spring的影子:FileSystemXmlApplicationContext類和ctx.getBean方式等。
          現在,低侵入性似乎也成了判定一個框架的實現技術好壞的標準之一。
          ?
          (5)關于bean.xml的用法
          bean.xml 的用法還有很多,其中內容是相當豐富的。假設Chinese類里有一個humenName屬性(姓名),那么原的bean.xml修改如下。此后生成 Chinese對象時,“陳剛”這個值將自動設置到Chinese類的humenName屬性中。而且由于singleton為true這時生成 Chinese對象將采用單例模式,系統僅存在一個Chinese對象實例。
          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
          <beans>
          ?????? <bean id="Chinese" class="cn.com.chengang.spring.Chinese" ?singleton="true">
          ????????????? <property name="humenName">
          ???????????????????? <value>陳剛</value>
          ????????????? </property>
          ?????? </bean>
          ?????? <bean id="American" class="cn.com.chengang.spring.American"/>
          </beans>
          ?
          關于bean.xml的其它用法,不再詳細介紹了,大家自己拿Spring的文檔一看就明白了。

          posted @ 2007-03-21 21:15 chenweicai 閱讀(157) | 評論 (0)編輯 收藏

          ? Action類是用戶請求和業務邏輯之間的橋梁。每個Action充當用戶的一項業務代理。在 ? RequestProcessor類預處理請求時,在創建了Action的實例后,就調用自身的processActionPerform()方法,該方法再調用Action類的execute()方法。Action的execute()方法調用模型的業務方法,完成用戶請求的業務邏輯,然后根據執行結果把請求轉發給其他合適的Web組件。在實際的應用中,主要有以下幾種比較常見的使用方法: ?
          ? ?
          ? 1.普通的Action應用 ?
          ? ?
          ? <action ? path="/normalAction" ?
          ? type="package.OneActionClass"> ?
          ? name="oneForm" ?
          ? input="page.jsp" ?
          ? <forward ? name="success" ? path="success.jsp"/> ?
          ? <forward ? name="failure" ? path="failure.jsp"/> ?
          ? </action> ?
          ? ?
          ? ? ? ? ? Struts的ActionServlet接收到一個請求,然后根據struts-config.xml的配置定位到相應的mapping(映射);接下來如果form的范圍是request或者在定義的范圍中找不到這個form,創建一個新的form實例,如果找到則重用;取得form實例以后,調用其 ? reset()方法,然后將表單中的參數放入form,如果validate屬性不為false,調用validate()方法;如果validate ? ()返回非空的ActionErrors,將會被轉到input屬性指定的URI,如果返回空的ActionErrors,那么執行Action的 ? execute()方法,根據返回的ActionForward確定目標URI。即execute()僅當validate()成功以后才執行; ? input屬性指定的是一個URI。 ?
          ? ?
          ? ?
          ? 2.有Form的Action應用 ?
          ? ?
          ? <action ? path="/formAction" ?
          ? type="org.apache.struts.actions.ForwardAction" ?
          ? name="oneForm" ?
          ? input="page.jsp" ?
          ? parameter="another.jsp" ?
          ? /> ?
          ? ?
          ? ? ? ? ? Struts會在定義的scope搜尋oneForm,如果找到則重用,如果找不到則新建一個實例;取得form實例以后,調用其reset()方法,然后將表單中的參數放入form,如果validate屬性不為false,調用validate()方法;如果validate()返回非空的 ? ActionErrors,將會被轉到input屬性指定的URI,如果返回空的ActionErrors,那么轉到parameter屬性指定的目標 ? URI。 ?
          ? ?
          ? ? ? ? ? 這種方法使得沒有action類可以存放我們的業務邏輯,所以所有需要寫入的邏輯都只能寫到form的reset()或者validate()方法中。 ? validate()的作用是驗證和訪問業務層。因為這里的action映射不包括forward,所以不能重定向,只能用默認的那個forward。這種僅有form的action可以用來處理數據獲取并forward到另一個JSP來顯示。 ?
          ? ?
          ? ?
          ? 3.僅有Action的Action應用 ?
          ? ?
          ? <action ? path="/actionAction" ?
          ? type="package.OneActionClass"> ?
          ? input="page.jsp" ?
          ? <forward ? name="success" ? path="success.jsp"/> ?
          ? <forward ? name="failure" ? path="failure.jsp"/> ?
          ? </action> ?
          ? ?
          ? ? ? ? ? ActionServlet接收到請求后,取得action類實例,調用execute()方法;然后根據返回的ActionForward在配置中找 ? forward,forward到指定的URI或action。這樣就沒有form實例被傳入execute()方法,于是execute()必須自己從請求中獲取參數。Action可以被forward或者重定向。這種action不能處理通過HTML ? FORM提交的請求,只能處理鏈接式的請求。 ?
          ? ?
          ? ?
          ? 4.僅有JSP的Action應用 ?
          ? ?
          ? <action ? path="/jspAction" ?
          ? type="org.apache.struts.actions.ForwardAction" ?
          ? parameter="another.jsp" ?
          ? /> ?
          ? ?
          ? ? ? ? ? ActionServlet接到請求后調用ForwardAction的execute()方法,execute()根據配置的parameter屬性值來forward到那個URI。這種情況下,沒有任何form被實例化,比較現實的情形可能是form在request更高級別的范圍中定義;或者這個 ? action被用作在應用程序編譯好后充當系統參數,只需要更改這個配置文件而不需要重新編譯系統。 ?
          ? ?
          ? ?
          ? 5.兩個Action對應一個Form(和第四種方式部分的Action作用相近) ?
          ? ?
          ? <action ? path="/oneAction" ?
          ? type="package.OneActionClass"> ?
          ? name="oneForm" ?
          ? input="one.jsp" ?
          ? <forward ? name="success" ? path="/anotherAction.do"/> ?
          ? </action> ?
          ? <action ? path="/anotherAction" ?
          ? type="package.AnotherActionClass"> ?
          ? name="oneForm" ?
          ? input="another.jsp" ?
          ? <forward ? name="success" ? path="success.jsp"/> ?
          ? </action> ?
          ? ?
          ? ? ? ? ? 這個組合模式可以被用來傳遞form對象,就每個單獨的action來講,處理上并沒有和完整的action有什么實質的區別。需要注意的是在后一個 ? action中同樣會調用form的reset()和validate()方法,因此我們必須確保form中的信息不被重寫。這種情況分兩種方式處理: ? a) ? 在request中放入一個指示器表明前一個action有意向后一個action傳遞form,從而在后一個action可以保留那個form中的值,這一方式只能在使用forward時使用。b) ? 當使用redirect而不是forward時,可以把指示器放在session或更高的級別,在命令鏈的最后一環將這個指示器清除。 ?
          ? ?
          ? ?
          ? 6.兩個Action對應兩個form ?
          ? ?
          ? <action ? path="/oneAction" ?
          ? type="package.oneActionClass"> ?
          ? name="oneForm" ?
          ? input="one.jsp" ?
          ? <forward ? name="successful" ? path="/anotherAction.do" ? redirect="true"/> ?
          ? </action> ?
          ? <action ? path="/anotherAction" ?
          ? type="package.AnotherActionClass">" ?
          ? name="anotherForm" ?
          ? input="another.jsp" ?
          ? <forward ? name="success" ? path="success.jsp"/> ?
          ? </action> ?
          ? ?
          ? ? ? ? ? 這個組合方式跟前一種在流程上沒有太大區別,只是我們現在對于兩個action分別提供了form,于是代碼看上去更加清晰。于是我們可以分別處理WEB ? 應用程序的輸入和輸出。值得注意的是,后一個action同樣會嘗試往form中寫入那些參數,不過我們可以這樣處理:a) ? 在后一個form中使用另一套屬性名;b) ? 只提供getter而不提供setter。 ?
          ? ?
          ? 基本處理過程: ?
          ? ? ? ? ? 前一個action接收輸入、驗證、然后將數據寫入業務層或持久層,重定向到后一個action,后一個action手動的從業務層/持久層取出數據,寫入form(通過其他方式),交給前臺JSP顯示。這樣做的好處是不必保留輸入form中的值,因此可以使用redirect而不是forward。這樣就降低了兩個action之間的耦合度,同時也避免了不必要的重復提交。 ?
          ? ?
          ? 注明:文中所提及的“僅有Form”指的是沒有繼承Struts提供的Action類,而是直接使用了Struts自身提供的Action類;“僅有Action”指的是僅繼承了Struts提供的Action類而沒有使用Form。

          posted @ 2007-03-20 15:30 chenweicai 閱讀(201) | 評論 (0)編輯 收藏

          使用Struts有一段時間了,但也僅僅涉及到一部分內容,比如Action做邏輯控制、FormBean對象化用戶提交的Form數據、國際化資源文件,Struts很重要的一部分Struts Tag卻一直沒有使用到,一是因為要熟練的使用Struts Tag需要一定的時間來,二也是因為自己有一套比較好的Tag可用。JSP頁面上Tag使用的較多雖然能讓頁面看起來比較整潔,但可讀性會相對降低,開發時的靈活性也會降低。相對來說還是比較看中Action的邏輯控制部分。

          重新翻看Struts書本,特別注意了Tag部分,也還是覺得有些眩暈,或許使用熟練是才能體驗出其中的玄妙。摘錄幾個Tag放在這里。

          1、<bean:message key="hello.jsp.page.heading"/>
          用于輸出資源文件中的內容

          2、<html:errors/>或者寫成<html:error property="xxx"/>
          用于輸出錯誤信息,當指定peoperty時,則只顯示對應的錯誤信息,如:
          ActionErrors errors = new ActionErrors();
          errors.add("xxx",new ActionError("username.null"));

          3、<html:form action="/Helloworld">
          Form表單

          4、<html:text property="userName" size="16"/>
          表單中的輸入域

          5、<html:submit property="submit" value="Submit"/>
          提交按鈕

          6、<html:reset/>
          重置按鈕

          7、<bean:write name="bitiliu" property="userName"/>
          從request中或者session中獲得bitiliu對象,并輸出userName屬性的值,可指定scope

          8、非空邏輯判斷
          <logic:notEmpty name="bitiliu" property="userName" scope="request">
          ?do something...??
          </logic:notEmpty>

          9、<html:link>
          用于生成鏈接,可以增加參數
          <%
          ?pageContext.setAttribute("name","I am a boy!");

          ?HashMap myMap = new HashMap();?
          ?myMap.put("name","bitiliu");
          ?myMap.put("password",new String[]{"1","2","3"});
          ?pageContext.setAttribute("myMap",myMap);
          %>
          <html:link page="/Test.jsp" paramId="haha" paramName="name" name="myMap">Test</html:link>
          生成的超鏈接為:
          <a href="/Struts2/Test.jsp?password=1&amp;password=2&amp;password=3&amp;haha=I+am+a+boy%21&amp;name=bitiliu">Test</a>

          10、<html:img>
          生成圖片標記,如<html:img page="/logo.gif"/>,生成的HTML為<img src="/web/logo.gif">
          <html:img>也可以包含請求參數,可參考<html:link>

          11、<html:cancel>
          增加取消按鈕,點擊取消按鈕也會請求form的action事件,可以在execute方法中通過方法isCancelled(request)來判斷是否點擊了取消按鈕。

          12、<html:hidden>
          生成隱藏域,如<html:hidden property="name"/>,生成的HTML為<input type="hidden" name="name"/>

          13、<html:checkbox property="check">
          對應的Form中應該有一對應屬性check類型為boolean

          14、<html:multibox property="strArray" value="value1">
          對應的Form中對應屬性strArray類型為String

          15、<html:radio property="strArray" value="value1">
          同<html:multibox>,但為單選框

          16、<html:select>
          下拉選擇框,可指定property、size和multiple(true\false),標簽內可包含<html:option>、<html:options>和<html:optionCollections>

          17、<html:option>
          可寫成:<html:option value="color.orange" key="color.orange"/>或<html:option value="color.orange">Orange</html:option>

          18、<html:options>
          一次生成多個option,使用示例:
          Vector vec = new Vector();
          vec.add(new org.apache.struts.util.LabelValueBean("label1","value1"));
          vec.add(new org.apache.struts.util.LabelValueBean("label2","value2"));
          pageContext.setAttribute("vec",vec);

          <html:options collection="vec" property="value" labelProperty="label"/>

          19、<html:file>
          用于實現文件上傳,其中<html:form>的enctype="multipart/form-date",Form的對應屬性類型應為File類型,

          邏輯判斷標簽:
          1、<logic:equal>
          相等,示例如下:
          <logic:equal name="strValue" value="112" scope="request">equal</logic:equal>
          標簽從request中獲得strValue對象,然后和112比較,如果相等,則輸入字符串equal,否則不輸出。下面標簽相同。

          2、<logic:greateEqual>
          大于等于

          3、<logic:greaterThan>
          大于

          4、<logic:lessEqual>
          小于等于

          5、<logic:lessThan>
          小于

          6、<logic:notEqual>
          不等于

          7、<logic:match>
          指定的值是變量的子串

          8、<logic:notMatch>
          指定的值不是變量的子串

          9、<logic:iterate>
          疊代標簽

          其它:
          1、在國際化文件中添加兩個errors.header和errors.footer用于指定錯誤信息顯示時前后追加的內容。

          posted @ 2007-03-17 14:32 chenweicai 閱讀(642) | 評論 (1)編輯 收藏

          struts標簽bean

          <!--?################################################################?-->
          <!--
          訪問cookie的信息內容
          此標簽主要是用于在jsp中將一個cookie附給一個指定的變量
          以便于在jsp中應用
          -->
          <bean:cookie?id="唯一標識符"?name="存在的cookie屬性名"/>
          <!--?################################################################?-->
          <!--
          訪問cookie的信息內容
          此標簽主要是用于在jsp中將scop中一個的屬性附給一個指定的變量
          以便于在jsp中應用
          -->
          <bean:define
          ??id="唯一標識符"
          ??name="在page|request|response|session中存在的標識符"
          ??property="對象中的成員變量"
          ??scope="page|request|response|session"
          ??toScope="存放此對象的范圍page|request|response|session"
          ??type="此對象的數據類型(權限定類名)"
          ??value="默認初始化值"/>
          <!--?################################################################?-->
          <!--
          訪問頭部元素的信息內容
          <bean:header>標簽是用于將頭部信息中的一個元素屬性附給一個指定的變量
          以便于在jsp中應用
          -->
          <bean:header?id="唯一標識符"?name="頭部元素中存在的屬性名"/>
          <!--?################################################################?-->
          <!--
          訪問頭部元素的信息內容
          <bean:include>標簽是用于web應用程序中的一個資源引進當前jsp中,
          并且將指向它的一個地址附給指定的變量
          以便于在jsp中應用
          -->
          <bean:include?id="唯一標識符"?page="包含的web組件的uri路徑,以?/?開頭"?/>
          <!--?################################################################?-->
          <!--
          <bean:?page>標簽主要用于訪問jsp中的隱含對象,
          ??page|request|response|session|application
          ??將此屬性附給一個指定變量
          -->
          <bean:?page?id="唯一標識符"?name="jsp中的隱含對象"/>
          <!--?################################################################?-->
          <!--
          <bean:?parameter>標簽
          ??用于訪問請求參數?,
          ??將此屬性附給一個指定的變量,便于在當前jsp中應用
          -->
          <bean:?parameter
          ??id="唯一標識符"
          ??name="參數名"
          ??value="默認值"
          ??multiple="??????"/>
          <!--?################################################################?-->
          <!--
          <bean:resource>
          訪問系統配置中的資源綁定信息?Resource?Bundle
          此標簽的作用是,將指向系統配置中的某個資源的指針,附給指定變量,以便于在當前頁中調用
          -->
          <bean:resource
          ??id="唯一標識符"
          ??name="包含的?web?uri?路徑,以?/?開頭"
          ??input="?????"?/>
          <!--?################################################################?-->
          <!--
          <bean:size>?用于取得某個指定數據容器的深度大小?,并且將此值附給一個指定變量名
          -->
          <bean:size
          ??id="唯一標識符"
          ??name="page|request|response|session中存在的屬性變量名"
          ??property="變量中的成員變量名"
          ??scope="作用范圍page|request|response|session"
          ??collection="java.util.Collection類變量"?/>
          <!--?################################################################?-->
          <!--
          <bean:write>?用于輸出指定變量的內容值
          -->
          <bean:write
          ??name="page|request|response|session中存在的屬性變量名"
          ??property="變量中的成員變量名"
          ??filter="true|false"
          ??format="書寫格式"
          ??formatKey="索引主鍵,此主鍵與系統配置文件中的主鍵區配"
          ??scope="作用范圍page|request|response|session"
          ??bundle="??????"?ignore="??????"?locale="??????"
          ??/>
          <!--?################################################################?-->
          <!--
          <bean:message>?用于輸出資源配置中的信息內容
          -->
          <bean:message
          ??bundle="系統配置文件中的綁定參數"
          ??key="與系統配置中的資源文件中的主鍵區配"?/>
          <bean:message
          ??bundle="系統配置文件中的綁定參數"
          ??name="scope中存在的屬性名"
          ??scope="page|request|response|session"
          ??property="屬性對象中的成員變量名,并且此屬性變量的值與key的值相同"?/>

          struts標簽html

          <!--######################################################################-->
          <!--?不帶參數的page連接方式?-->
          <html:link?[page="/XXX.do"|action="/XXX"]>連接內容</html:link>
          <!--帶參數的一種連接方式-->
          <html:link
          ??[page="/XXX.do?paramName1=value1&amp;paramName2=value2"|
          ??page="/XXX?paramName1=value1&amp;paramName2=value2"]>
          ????????連接內容
          ??????</html:link>
          <html:link?[page="/XXX.do"|action="/XXX"]
          ???????????paramId="參數名"
          ???????????paramName="在page|request|response|session中存在的屬性名">
          ????????連接內容
          ??????</html:link>
          <!--帶參數的一種連接方式-->
          <html:link?[page="/XXX.do"|action="/XXX"]
          ???????????paramId="參數名"
          ???????????paramName="配置文件中的BEAN的配置名稱"
          ???????????paramProperty="配置對象中的成員變量">
          ????????連接內容
          ??????</html:link>
          <html:link?action="/XXX"
          ???????????name="在page|request|response|session中存在的屬性名">
          ????????連接內容
          ??????</html:link>
          <html:link?href="完整的url路徑">
          ????????連接內容
          ??????</html:link>
          <html:link?page="相對于當前操作路徑的url">
          ????????連接內容
          ??????</html:link>
          <html:link?forward="struts配置文件中存在的<global-forwards>元素的子元素<forwar>的name值">
          ????????連接內容
          ??????</html:link>
          <!--######################################################################-->
          <html:img?page="相對于當前操作路徑的url"?/>
          <!--######################################################################-->
          <html:img?src="完整的uri路徑"
          ??paramId="參數名"
          ??paramName="page|request|response|session中存在的屬性名"/>
          <!--######################################################################-->
          <html:form
          ??action="xxx.do"
          ??focus="焦點"
          ??method="GET|POST|DELETE|PUT|HEAD|OPTIONS"?>
          <!--######################################################################-->
          ??<!--?單行輸入框?-->
          ??<html:text?property="prptName">初始值</html:text>
          <!--######################################################################-->
          ??<!--?隱藏字段?-->
          ??<html:hidden?property="prptName"?/>
          <!--######################################################################-->
          ??<!--?密碼輸入框?-->
          ??<html:?password?property="prptName"></html:?password>
          <!--######################################################################-->
          ??<!--?文件獲取輸入框?-->
          ??<html:file?property="prptName">初始值</html:file>
          <!--######################################################################-->
          ??<!--?按鈕?-->
          ??<html:button?property="prptName">初始值</html:button>
          <!--######################################################################-->
          ??<!--?提交按鈕?-->
          ??<html:submit?property="prptName">初始值</html:submit>
          <!--######################################################################-->
          ??<!--?取消按鈕?-->
          ??<html:cancel?property="prptName">初始值</html:cancel>
          <!--######################################################################-->
          ??<!--?重置按鈕?-->
          ??<html:reset?property="prptName">初始值</html:reset>
          <!--######################################################################-->
          ??<!--?圖片按鈕?-->
          ??<html:image?onclick=""?src="url"?>初始值</html:image>
          <!--######################################################################-->
          ??<!--?復選框?-->
          ??<html:checkbox?property="prptName">初始值</html:checkbox>
          <!--######################################################################-->
          ??<!--多選框的表達方式一-->
          ??<html:multibox?property="屬性名"?value="初始值"/>
          <!--######################################################################-->
          ??<!--多選框的表達方式二-->
          ??<html:multibox?property="屬性名">初始值</html:multibox>
          <!--######################################################################-->
          ??<!--?選擇列表?-->
          ??<html:select
          ????property="prptName"
          ????size="指定在網頁上顯示的可選的數目"
          ????multiple="true|false,此屬性用于指定列表是否允許多選">
          <!--######################################################################-->
          ????<html:option
          ??????value="綁定的屬性名"
          ??????bundle="系統文件中綁定的屬性名"
          ??????key="資源文件中綁定的屬性"?>lable</html:option>
          <!--######################################################################-->
          ????<html:options
          ??????collection="Vector對象,此對象中放置org.apache.struts.util.LabelValueBean對象"
          ??????property="網頁中的value值其值一般是value"
          ??????labelProperty="網頁中顯示的標簽,其值一般是lable"/>
          <!--######################################################################-->
          ????<html:optionsCollection
          ??????name="page|request|response|session中存在的屬性名稱"
          ??????property="屬性對象中的成員變量"
          ??????label="成員變量中的成員變量"
          ??????value="成員變量中的成員變量"?/>
          ??</html:select>
          </html:form>

          struts標簽logic

          <!--#################################################################-->
          <!--?<logic:equal>和<logic:notEqual>判斷變量的值與指定常量是否相等?-->
          <logic:equal
          ??name="request,page,response,session中存在的以此名稱命名的變量"
          ??property="此變量中的成員變量"
          ??parameter="將要取得的變量的標識符,此變量存在于request|page|response|session中"
          ??scope="取得變量的范圍?request|page|response|session"
          ??value="?參加比對的值?">
          ??若判斷成立,則輸出此處的內容!
          ??</logic:equal>
          <!--=====================================================================-->
          <logic:notEqual
          ??name="request,page,response,session中存在的以此名稱命名的變量"
          ??property="此變量中的成員變量"
          ??parameter="將要取得的變量的標識符,此變量存在于request|page|response|session中"
          ??scope="取得變量的范圍?request|page|response|session"
          ??value="?參加比對的值?">
          ??若判斷成立,則輸出此處的內容!
          ??</logic:notEqual>
          <!--#################################################################-->
          <logic:iterate
          ??id="唯一標識符"
          ??name="在request|response|session|page中的標識符"
          ??property="若是自定義類對象,此處用語表示此對象中的屬性名"
          ??type="取得的對象的權限定類名"
          ??indexId="indexid"
          ??offset="起始位置"
          ??length="循環的長度">
          ??<li><em><bean:write?name="與logic:iterate的屬性id的內容一致"?/></em>&nbsp;[<bean:write?name="index"/>]</li>
          </logic:iterate>

          <!--#################################################################-->
          <!--<logic:empty>與<logic:notEmpty>用于判斷指定參數的屬性值是否是null值或是空字符串""-->
          <!--判斷scope中存在的指定參數名的變量值是否是null值或是空字符串,若是null或空字符串則輸出標簽之間的內容-->
          <logic:empty
          ??name="對象的唯一標識符"
          ??scope="page|request|response|session"
          ??property="對象中存在的成員變量">
          empty
          </logic:empty>
          <!--判斷scope中存在的指定參數名的變量值是否不是null值或是空字符串,若不是null或空字符串則輸出標簽之間的內容-->
          <logic:notEmpty
          ??name="對象的唯一標識符"
          ??scope="page|request|response|session"
          ??property="對象中存在的成員變量">
          notEmpty
          </logic:notEmpty>
          <!--#################################################################-->
          <!--<logic:?present>與<logic:notPresent>用于判斷指定參數的屬性是否存在-->
          <!--判斷cookie中是否存在指定參數名的變量若存在則輸出標簽之間的內容-->
          <logic:?present
          ??cookie="cookie中存在的變量名">
          ??此處是輸出內容!
          ??</logic:?present>
          <!--判斷header中是否存在指定參數名的變量若存在則輸出標簽之間的內容-->
          <logic:?present
          ??header="取得頭部元素中存在的變量">
          ??此處是輸出內容!
          ??</logic:?present>
          <!--判斷scope中是否存在指定參數名的變量若存在則輸出標簽之間的內容-->
          <logic:?present
          ??name="屬性名"
          ??property="對象中存在的成員變量"
          ??scope="page|request|response|session">
          ??此處是輸出內容!
          ??</logic:?present>
          <!--=================================================================-->
          <!--判斷cookie中是否存在指定參數名的變量若存在則輸出標簽之間的內容-->
          <logic:notPresent
          ??cookie="cookie中可能存在的變量名">
          ??此處是輸出內容!
          ??</logic:notPresent>
          <!--判斷header中是否存在指定參數名的變量若存在則輸出標簽之間的內容-->
          <logic:notPresent
          ??header="頭部元素中存在的變量">
          ??此處是輸出內容!
          ??</logic:notPresent>
          <!--判斷scope中是否存在指定參數名的變量若存在則輸出標簽之間的內容-->
          <logic:notPresent
          ??name="屬性名"
          ??property="對象中可能存在的成員變量"
          ??scope="page|request|response|session">
          ??此處是輸出內容!
          ??</logic:notPresent>
          <!--#################################################################-->
          <!--<logic:match>與<logic:notMatch>用于判斷指定參數的字符串是否區配某個給定標準-->
          <!--判斷cookie中存在指定參數名的變量的值,是否與指定的字符格式區配若區配則輸出此內容-->
          <logic:match
          ??cookie="cookie中可能存在的變量名"
          ??location="contains|start|end"
          ??value="要區配的字符格式">
          </logic:match>
          <!--判斷header中存在指定參數名的變量的值,是否與指定的字符格式區配若區配則輸出此內容-->
          <logic:match
          ??header="header中可能存在的變量名"
          ??location="contains|start|end"
          ??value="要區配的字符格式">
          </logic:match>
          <!--判斷scope中存在指定參數名的變量的值,是否與指定的字符格式區配若區配則輸出此內容-->
          <logic:match
          ??name="存在的屬性名"
          ??property="屬性對象中的成員變量"
          ??scope="page|request|response|session"
          ??location="contains|start|end"
          ??value="要區配的字符格式">
          </logic:match>
          <!--===================================================================-->
          <!--判斷cookie中存在指定參數名的變量的值,是否與指定的字符格式區配若區配則輸出此內容-->
          <logic:notMatch
          ??cookie="cookie中可能存在的變量名"
          ??location="contains|start|end"
          ??value="要區配的字符格式">
          </logic:notMatch>
          <!--判斷header中存在指定參數名的變量的值,是否與指定的字符格式區配若區配則輸出此內容-->
          <logic:notMatch
          ??header="header中可能存在的變量名"
          ??location="contains|start|end"
          ??value="要區配的字符格式">
          </logic:notMatch>
          <!--判斷scope中存在指定參數名的變量的值,是否與指定的字符格式區配若區配則輸出此內容-->
          <logic:notMatch
          ??name="存在的屬性名"
          ??property="屬性對象中的成員變量"
          ??scope="page|request|response|session"
          ??location="contains|start|end"
          ??value="要區配的字符格式">
          </logic:notMatch>
          <!--#################################################################-->
          <!--<logic:forward>用于地址轉向到指定位置-->
          <logic:forward?name="與系統配置文件中的<global-forward>元素中的子元素<forward>區配"/>
          <!--#################################################################-->
          <!--?<logic:redirect>用于地址重定向到指定位置?-->
          <logic:redirect
          ??anchor=""
          ??forward=""
          ??href=""
          ??name=""
          ??page=""
          ??paramId=""
          ??paramName=""
          ??paramProperty=""
          ??paramScope=""
          ??property=""
          ??scope=""
          ??transaction="">連接內容</logic:redirect>

          posted @ 2007-03-17 14:29 chenweicai 閱讀(1332) | 評論 (0)編輯 收藏

          當序列化遇到繼承…

          當一個父類實現Serializable接口后,他的子類都將自動的實現序列化。

          以下驗證了這一點:
          package InherSerialTest;

          import java.io.Serializable;

          public class SuperA implements Serializable {

          ?private int supervalue;

          ?public SuperA(int supervalue) {
          ??super();
          ??// TODO Auto-generated constructor stub
          ??this.supervalue = supervalue;
          ?}
          ?
          ?public int getSupervalue() {
          ??return supervalue;
          ?}

          ?public void setSupervalue(int supervalue) {
          ??this.supervalue = supervalue;
          ?}

          ?public String toString(){
          ??return "supervalue is :" + supervalue;
          ?}
          }

          package InherSerialTest;

          public class SubB extends SuperA {

          ?private int subvalue;

          ?public SubB(int supervalue, int subvalue) {
          ??super(supervalue);
          ??// TODO Auto-generated constructor stub
          ??this.subvalue = subvalue;
          ?}

          ?public int getSubvalue() {
          ??return subvalue;
          ?}

          ?public void setSubvalue(int subvalue) {
          ??this.subvalue = subvalue;
          ?}

          ?public String toString() {
          ??// TODO Auto-generated method stub
          ??return super.toString() + " ,subvalue " + subvalue;
          ?}
          ?
          }

          package InherSerialTest;

          import java.io.FileInputStream;
          import java.io.FileOutputStream;
          import java.io.ObjectInputStream;
          import java.io.ObjectOutputStream;

          public class InherSerialTest {

          ?/**
          ? * @param args
          ? */
          ?public static void main(String[] args) {
          ??// TODO Auto-generated method stub

          ??SubB sub = new SubB(100,200);
          ??
          ??try{
          ???ObjectOutputStream oos = new ObjectOutputStream(
          ?????new FileOutputStream("c:\\InherSerialTest.txt"));
          ???oos.writeObject(sub);
          ???oos.close();
          ???
          ???ObjectInputStream ois = new ObjectInputStream(
          ?????new FileInputStream("c:\\InherSerialTest.txt"));
          ???SubB sub2 = (SubB)ois.readObject();
          ???System.out.println(sub2);
          ???ois.close();
          ??}catch(Exception e){
          ???e.printStackTrace();
          ??}
          ?}

          }


          結果是:supervalue is :100 ,subvalue 200

          怎管讓子類實現序列化看起來是一件很簡單的事情,但有的時候,往往我們不能夠讓父類實現Serializable接口,原因是有時候父類是抽象的(這并沒有關系),并且父類不能夠強制每個子類都擁有序列化的能力。換句話說父類設計的目的僅僅是為了被繼承。

          要為一個沒有實現Serializable接口的父類,編寫一個能夠序列化的子類是一件很麻煩的事情。java docs中提到:

          “To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime. ”

          也就是說,要為一個沒有實現Serializable接口的父類,編寫一個能夠序列化的子類要做兩件事情:

          其一,父類要有一個無參的constructor;

          其二,子類要負責序列化(反序列化)父類的域。
          如果我們將上列中的SuperA沒有實現Serializable接口,而是在SubB類中實現Serializable接口的話,即:

          public class SuperA? {}
          public class SubB extends SuperA implements Serializable{}

          我們再次運行時,將會產生錯誤:

          java.io.InvalidClassException: InherSerialTest.SubB; no valid constructor
          ?at java.io.ObjectStreamClass.<init>(Unknown Source)
          ?at java.io.ObjectStreamClass.lookup(Unknown Source)
          ?at java.io.ObjectOutputStream.writeObject0(Unknown Source)
          ?at java.io.ObjectOutputStream.writeObject(Unknown Source)
          ?at InherSerialTest.InherSerialTest.main(InherSerialTest.java:21)

          果真如docs中所說的一樣,父類缺少無參構造函數是不行的。



          接下來,按照docs中的建議我們改寫這個例子:
          package InherSerialTest2;

          import java.io.Serializable;

          public abstract class SuperC {

          ?int supervalue;

          ?public SuperC(int supervalue) {
          ??super();
          ??// TODO Auto-generated constructor stub
          ??this.supervalue = supervalue;
          ?}
          ?
          //?父類沒有實現Serializable接口,子類要可序列化的話,父類必須要有無參構造函數
          ?public SuperC(){
          ??
          ?}

          ?public int getSupervalue() {
          ??return supervalue;
          ?}

          ?public void setSupervalue(int supervalue) {
          ??this.supervalue = supervalue;
          ?}

          ?public String toString() {
          ??// TODO Auto-generated method stub
          ??return "supervalue : " + supervalue;
          ?}

          }

          package InherSerialTest2;

          import java.io.IOException;
          import java.io.ObjectInputStream;
          import java.io.Serializable;

          import InherSerialTest.SuperA;

          public class SubC extends SuperC implements Serializable{
          ?
          ?private int subvalue;

          ?public SubC(int supervalue, int subvalue) {
          ??super(supervalue);
          ??// TODO Auto-generated constructor stub
          ??this.subvalue = subvalue;
          ?}

          ?public int getSubvalue() {
          ??return subvalue;
          ?}

          ?public void setSubvalue(int subvalue) {
          ??this.subvalue = subvalue;
          ?}

          ?public String toString() {
          ??// TODO Auto-generated method stub
          ??return super.toString() + " ,subvalue :" + subvalue;
          ?}
          ?
          ?private void writeObject(java.io.ObjectOutputStream oos)throws IOException{
          ??
          ??//先序列化對象
          ??oos.defaultWriteObject();
          ??//在序列化父類的域
          ??oos.writeInt(supervalue);
          ?}
          ?
          ?private void readObject(ObjectInputStream ois)throws IOException, ClassNotFoundException{
          ??
          ??//先反序列化對象
          ??ois.defaultReadObject();
          ??//再反序列化父類的域
          ??supervalue = ois.readInt();
          ?}
          }

          測試成功!

          posted @ 2007-03-09 22:44 chenweicai 閱讀(466) | 評論 (0)編輯 收藏

          序列化概述

          簡單來說序列化就是一種用來處理對象流的機制,所謂對象流也就是將對象的內容進行流化,流的概念這里不用多說(就是I/O),我們可以對流化后的對象進行讀寫操作,也可將流化后的對象傳輸于網絡之間(注:要想將對象傳輸于網絡必須進行流化)!在對對象流進行讀寫操作時會引發一些問題,而序列化機制正是用來解決這些問題的!

          問題的引出:

          如上所述,讀寫對象會有什么問題呢?比如:我要將對象寫入一個磁盤文件而后再將其讀出來會有什么問題嗎?別急,其中一個最大的問題就是對象引用!舉個例子來說:假如我有兩個類,分別是A和B,B類中含有一個指向A類對象的引用,現在我們對兩個類進行實例化{ A a = new A(); B b = new B(); },這時在內存中實際上分配了兩個空間,一個存儲對象a,一個存儲對象b,接下來我們想將它們寫入到磁盤的一個文件中去,就在寫入文件時出現了問題!因為對象b包含對對象a的引用,所以系統會自動的將a的數據復制一份到b中,這樣的話當我們從文件中恢復對象時(也就是重新加載到內存中)時,內存分配了三個空間,而對象a同時在內存中存在兩份,想一想后果吧,如果我想修改對象a的數據的話,那不是還要搜索它的每一份拷貝來達到對象數據的一致性,這不是我們所希望的!

          以下序列化機制的解決方案:

          1.保存到磁盤的所有對象都獲得一個序列號(1, 2, 3等等)

          2.當要保存一個對象時,先檢查該對象是否被保存了。

          3.如果以前保存過,只需寫入"與已經保存的具有序列號x的對象相同"的標記,否則,保存該對象

          通過以上的步驟序列化機制解決了對象引用的問題!

          序列化的實現

          將需要被序列化的類實現Serializable接口,該接口沒有需要實現的方法,implements Serializable只是為了標注該對象是可被序列化的,然后使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(對象流)對象,接著,使用ObjectOutputStream對象的writeObject(Object obj)方法就可以將參數為obj的對象寫出(即保存其狀態),要恢復的話則用輸入流。
          package serializable;

          import java.io.Serializable;

          public class Employee implements Serializable {

          ?private String name;
          ?
          ?private double salary;

          ?public Employee(String name, double salary) {
          ??super();
          ??// TODO Auto-generated constructor stub
          ??this.name = name;
          ??this.salary = salary;
          ?}
          ?
          ?public void raiseSalary(double byPercent){
          ??double temp = salary * byPercent / 100;
          ??salary += temp;
          ?}

          ?public String toString() {
          ??// TODO Auto-generated method stub
          ??return getClass().getName() +
          ???"[ Name = " + name + ", salary = " + salary +"]";
          ?}

          package serializable;

          public class Manager extends Employee {
          ?
          ?private Employee secretary;

          ?public Manager(String name, double salary) {
          ??super(name, salary);
          ??// TODO Auto-generated constructor stub
          ??secretary = null;
          ?}

          ?public Employee getSecretary() {
          ??return secretary;
          ?}

          ?public void setSecretary(Employee secretary) {
          ??this.secretary = secretary;
          ?}

          ?public String toString() {
          ??// TODO Auto-generated method stub
          ??return super.toString() + "[ secretary = " + secretary +"]";
          ?}
          ?
          }

          package serializable;

          import java.io.FileInputStream;
          import java.io.FileOutputStream;
          import java.io.ObjectInputStream;
          import java.io.ObjectOutputStream;

          public class Test {
          ?
          ?public static void main(String[] args){
          ??
          ??Employee employee = new Employee("LiLei", 1000);
          ??Manager manager1 = new Manager("Jim", 20000);
          ??manager1.setSecretary(employee);
          ??
          ??Employee[] staff = new Employee[2];
          ??staff[0] = employee;
          ??staff[1] = manager1;
          ??
          ??try{
          ???ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employee.dat"));
          ???oos.writeObject(staff);
          ???oos.close();
          ???
          ???ObjectInputStream ois = new ObjectInputStream(new FileInputStream("employee.dat"));
          ???Employee[] newStaff = (Employee[])ois.readObject();
          ???ois.close();
          ???
          ???newStaff[0].raiseSalary(1000);
          ???
          ???for(int i=0; i<newStaff.length; i++)
          ????System.out.println(newStaff[i]);
          ???
          ??}catch(Exception e)
          ??{
          ???e.printStackTrace();
          ??}
          ?}
          }

          修改默認的序列化機制?

          在序列化的過程中,有些數據字段我們不想將其序列化,對于此類字段我們只需要在定義時給它加上transient關鍵字即可,對于transient字段序列化機制會跳過不會將其寫入文件,當然也不可被恢復。但有時我們想將某一字段序列化,但它在SDK中的定義卻是不可序列化的類型,這樣的話我們也必須把他標注為transient,可是不能寫入又怎么恢復呢?好在序列化機制為包含這種特殊問題的類提供了如下的方法定義:

          private?void readObject(ObjectInputStream in) throws

          IOException, ClassNotFoundException;

          private void writeObject(ObjectOutputStream out) throws

          IOException;

          (注:這些方法定義時必須是私有的,因為不需要你顯示調用,序列化機制會自動調用的)

          使用以上方法我們可以手動對那些你又想序列化又不可以被序列化的數據字段進行寫出和讀入操作。

          下面是一個典型的例子,java.awt.geom包中的Point2D.Double類就是不可序列化的,因為該類沒有實現Serializable接口,在我的例子中將把它當作LabeledPoint類中的一個數據字段,并演示如何將其序列化
          package transientTest;

          import java.awt.geom.Point2D;
          import java.io.IOException;
          import java.io.ObjectInputStream;
          import java.io.ObjectOutputStream;
          import java.io.Serializable;


          public class LabeledPoint implements Serializable {

          ?private String label;
          ?transient private Point2D.Double point;
          ?
          ?public LabeledPoint(String label, double x, double y) {
          ??super();
          ??// TODO Auto-generated constructor stub
          ??this.label = label;
          ??this.point = new Point2D.Double(x,y);
          ?}
          ?
          ?private void writeObject(ObjectOutputStream oos)throws IOException{
          ??
          ??oos.defaultWriteObject();
          ??oos.writeDouble(point.getX());
          ??oos.writeDouble(point.getY());
          ?}
          ?
          ?private void readObject(ObjectInputStream ois)throws IOException, ClassNotFoundException{
          ??
          ??ois.defaultReadObject();
          ??double x = ois.readDouble() + 1.0;
          ??double y = ois.readDouble() + 1.0;
          ??point = new Point2D.Double(x,y);
          ?}

          ?public String toString() {
          ??// TODO Auto-generated method stub
          ??return getClass().getName() + "[ Label = " + label + ", point.getX() = "
          ???+ point.getX() + ", point.getY() = " + point.getY() + "]";
          ?}
          }

          package transientTest;

          import java.io.FileInputStream;
          import java.io.FileOutputStream;
          import java.io.ObjectInputStream;
          import java.io.ObjectOutputStream;

          public class transientTest {

          ?public static void main(String[] args){
          ??LabeledPoint label = new LabeledPoint("Book", 5.0, 5.0);
          ??
          ??try{
          ???System.out.println("before:\n" + label);
          ???
          ???ObjectOutputStream oos = new ObjectOutputStream(
          ?????new FileOutputStream("c:\\label.txt"));
          ???oos.writeObject(label);
          ???oos.close();
          ???
          ???System.out.println("after:\n" + label);
          ???ObjectInputStream ois = new ObjectInputStream(
          ?????new FileInputStream("c:\\label.txt"));
          ???LabeledPoint label1 = (LabeledPoint)ois.readObject();
          ???ois.close();
          ???
          ???System.out.println("after add 1.0:\n" + label);
          ??}catch(Exception e)
          ??{
          ???e.printStackTrace();
          ??}
          ?}
          }

          posted @ 2007-03-09 20:13 chenweicai 閱讀(387) | 評論 (0)編輯 收藏

          摘要:本文介紹了J2EE的分層結構,深入研究了如何使用Session Facade模式和ValueObject 模式設計EJB,并對其開發過程做了較詳細的說明。

          關鍵字:EJB ;值對象模式;會話外觀模式
          一、概述

            與傳統的二層體系結構相比,J2EE有兩個特點:

            1、定義了一套標準化組件,通過為這些組件提供完整的服務。

            2、使用多層分布式的應用程序模型。應用程序的邏輯根據其實現的不同功能被封裝到不同的組件中。如圖1所示。





            這種多層結構使企業級應用具有很強的伸縮性,允許各層專注于某種特定的角色:

            1、Client Tier用于顯示。

            2、Web Tier用于生成動態顯示。

            3、Business Tier用于實現業務邏輯。

            4、EIS Tier用于數據庫服務。

            其中,用于實現業務邏輯的EJB組件架構是J2EE的基礎和最重要的部分。

            正是認識到J2EE平臺作為一種可擴展的、全功能的平臺,可以將關鍵的企業應用擴展到任何Web瀏覽器上并可適合多種不同的Internet數據流、可連接到幾乎任何一種傳統數據庫和解決方案,J2EE已經成為開發電子商務應用的事實標準。

            為了使開發者開發出規范的、可重用的應用程序,J2EE為我們提供了大量的模式。模式盡管有時不易理解,但使用卻非常簡單,它提供了強大的可重用機制,避免了開發者和設計者的重復投資。

            可是,面對如此多的模式,初學者往往不知如何下手,為此,作者結合以往的開發經驗,詳細介紹如何使用模式完成EJB的設計。
          二、設計與實現

            1.值對象模式

            J2EE應用程序把服務器端業務組件實現為會話Bean和實體Bean。對于實體Bean的創建,開發人員通常采用CMP(容器管理持久性)模式,其好處在于容器提供公共的服務,例如目錄服務、事務管理、安全性、持久性、資源緩沖池以及容錯性等,使開發人員不必維護將會集成到業務邏輯中的系統級代碼,只需專注于商業邏輯。

            一般來說,有了實體bean,就可以通過調用業務組件的一些方法向客戶端返回數據。初學者往往會認為既然客戶端可以與服務器通信,那么任務就算完成了。可是,問題恰恰出在這里。業務組件的get方法只能返回一個屬性值,這就導致需要獲得所有屬性值的客戶端需要多次調用業務對象的get方法,如圖2-1所示。每次調用都是一次網絡調用,都會造成系統性能的退化,當調用次數增多時,系統性能就會嚴重下降。

            這就要求有一種方法使客戶端可以一次調用得到所需的大量數據,這種方法就是Value Object(值對象)模式。值對象是任意的可串行化的Java對象,也被稱為值的對象,它在一次網絡傳輸中包含和封裝了大量的數據并被保存在內存中。這樣,當客戶端需要再次使用數據的時候,不用再次到數據庫中查詢,而是直接在內存中讀取值對象,節省了大量的時間和系統開銷,如圖2-2。

            值對象模式有兩種策略――可更新的值對象策略和多值對象策略。

            可更新的值對象策略中,業務對象負責創建值對象,并且在客戶端請求時把該值對象返回給客戶端;同時,業務對象也可以從客戶端接收數據,形成值對象,并使用該對象來完成更新。

            例如,在銀行系統的例子中,Account 中提供一個以AccountValue為參數的setAccountValueObject方法,這樣客戶端可以通過這個方法來設置值對象的值,而不采用實體bean--Account中設置每個屬性的方法(setBalance()),因為后一種方法會導致大量的網絡負載。由于值對象的易變性,所以值對象類必須給每個可以被客戶端更新的屬性提供設置方法。例如,AccountValue中的setBalance()方法。這樣,一旦某客戶端擁有來自業務對象的值對象,客戶端就可以在本地調用必要的設置方法來更改屬性值,然后調用業務對象的setAccountValueObject()方法更新業務對象。

            多值對象策略

            一些應用程序業務對象往往比較復雜,在這種情況下,根據客戶端請求不同,有可能單個業務對象會產生多個不同的值對象。在這種情況下,可以考慮采用多值對象策略。這種策略的實現比較簡單,就是在entity bean中增加不同的Get×××ValueObject()方法和set×××ValueObject()方法。

            2.Session Facade 模式

            有了實體Bean,客戶端就可以直接調用它以獲得數據。也就是說實體Bean封裝了業務數據,并把他們的接口暴露給客戶,因而也就把分布式服務的復雜性暴露給客戶。在對J2EE 應用程序環境下,一般會產生如下問題:

            1、緊密耦合,這回導致客戶端和業務對象的直接依賴關系

            2、客戶端和服務器之間的網絡方法調用太多,容易導致網絡性能問題

            3、缺乏統一的客戶訪問策略,容易誤用業務對象

            4、如果實體bean的API改動,那么用戶端的一些代碼也要修改,擴展性很差



            解決這些問題的方法就是把客戶端和實體bean分割開。本文采用Session Facade模式,如圖3-2所示。該模式通過一個Session Bean,為一系列的實體bean提供統一的接口來實現流程。事實上,客戶端只是使用這個接口來觸發流程。這樣,所有關于實體bean實現流程所需要的改變,都和客戶端無關。當實體bean改變時,我們不用改變客戶端的代碼,只要對Session Bean做出相應的改變即可,大大提高了系統的可維護性。

            通過實體bean來表示業務對象是session facade的最常見用法。但多個實體bean參與某用例時,不必向客戶暴露所有實體bean。相反的,用session bean 包裝這些實體bean ,并且提供粗粒度方法來執行所需的業務功能,從而隱藏了實體bean交互的復雜性。

            但是千萬不要以為Facade模式就是簡單的用Session Bean把Entity Bean的所有方法統統封裝起來,而不提供任何額外的抽象。其實這是對Facade模式的濫用。這樣做并不是降低整個系統的復雜性,而是把復雜性轉移到另一個對象上。

            正確應用Facade模式應遵循三條基本原則:

            1、他們自己不作實際工作,而是委派其他對象作實際工作。

            2、他們提供簡單的接口。

            3、他們是底層系統的客戶端接口。他們應該把特定于子系統的信息封裝起來,并且不應該在不必要的情況下公開它。
          三、具體代碼

            下面用一個簡單的銀行系統的例子來解釋Facade模式和Value Object模式的具體應用。

            創建Entity Bean。其中對每個屬性的get和set方法是自動生成的,我們不去管它。

          public interface Account extends javax.ejb.EJBObject {
          private AccountValue creaeAccountValueObject();
          void setAccountVauleObject(AccountValue v);
          AccountValue getAccountValueObject();
          ……}

            其中

          private AccountValue createAccountValueObject(){
          AccountValue vo=new AccountValue();
          vo. accountNumber=accountNumber;
          Vo.balance=balance;
          ……}
          public AccountValue getAccountValueObject(){
          return createAccountValueObject();
          }
          public void setAccountValueObject(AccountValue v){
          accountNumber=v. accountNumber;
          balance=v.balance;
          ……}

            用值對象封裝Entity Bean數據。

          public class AccountValue implements java.io.Serializable {
          private java.lang.String accountNumber;
          private double balance;
          void setBalance(double newValue)
          ……}

            用Factory或者是Action類邏輯方法,涉及到數據的地方使用值對象。

          public class AccountFactory {
           private static AccountHome accountHome = null;
            ……
           public java.util.Vector getAccounts(String userid) throws FactoryException {
            try { Vector vect = new Vector();
             AccountHome home = getAccountHome();
             Enumeration accountRefs = home.findByUserid(userid);
             while (accountRefs.hasMoreElements()) {
              Account acc = (Account)accountRefs.nextElement();
              AccountValue valueObject =acc.getAccountValueObjcet();
              vect.addElement(valueObject);}
              return vect; }
            ……}

            在Session Bean的get方法中調用Factory或者是Action對象。

          public java.util.Vector getAccounts(String userid) throws FactoryException {
           AccountFactory fact = new AccountFactory();
           Vector result = fact.getAccounts(userid);
           return result;
          }

            正如代碼所示,使用session facade模式,可以

            1、提供統一的接口:會話外觀抽象了業務組件交互的復雜性,并且向客戶端提供一個更簡單的接口。

            2、減少耦合提高可管理性:會話外觀分離了業務對象和客戶端,這樣可以減少緊密耦合,以及客戶端對業務對象的依賴性。

            3、提供粗粒度訪問:會話外觀減少客戶端和服務器之間的網絡負載。客戶端與業務數據的說有交互都是通過會話外觀以粗粒度的發拿過是進行的。

            通過使用

            從上述代碼可以看出,使用模式之后,大大改善了系統性能,也提高了代碼的可重用性。此外,開發者也可以采用其他的小模式來提高系統性能,比如服務器定位模式,在此不作進一步介紹。

            四、總結

            綜上所述,本文詳細地介紹了使用值對象模式和會話模式設計商業邏輯層的方法,很好的實現了數據封裝和合理分層,大大提高了系統的可維護性和可伸縮性,也顯著的簡化了具有可伸縮性和高度負責的企業級應用的開發。

            注:航天自然基金贊助支持

          posted @ 2007-03-03 15:33 chenweicai 閱讀(147) | 評論 (0)編輯 收藏

          僅列出標題
          共6頁: 上一頁 1 2 3 4 5 6 下一頁 
          主站蜘蛛池模板: 皮山县| 扎赉特旗| 益阳市| 靖江市| 绥棱县| 宣汉县| 大余县| 深水埗区| 峨眉山市| 瑞安市| 习水县| 台北市| 彩票| 通河县| 富宁县| 科技| 财经| 天祝| 尖扎县| 大安市| 阿荣旗| 克山县| 隆尧县| 临潭县| 兴安县| 青神县| 平顶山市| 漳浦县| 乐亭县| 竹北市| 若尔盖县| 太和县| 达日县| 正蓝旗| 孝昌县| 高雄市| 肇东市| 随州市| 望城县| 洪雅县| 玛沁县|