2010年12月6日 星期一

[OO 設計模式] Prototype : 原型模式

轉載自 這裡 
有些物件若以標準的方式建立實例,或者是設定至某個狀態需要複雜的運算及昂貴的資源,則您可以考慮直接以某個物件作為原型,在需要個別該物件時,複製原型並傳回. 
先來看看Prototype的類別圖 : 
 
請注意,在這邊Cloneable並非指Java中的 java.lang.Cloneable,而是指支援原型複製的物件,必須實作之公開協定. 
不 同的語言可能提供不同程度支援之物件複製技術,以Java而言,java.lang.Object本身即定義有clone()方法,因此所有的物件基本上 皆具自我複製之能力,不過真正要讓物件支援複製,則物件必須實作java.lang.Cloneable這個標示介面(Tag interface). 
下面這個範例示範了,如何使用Java實作Prototype模式(建議您參考:How to avoid traps and correctly override methods from java.lang.Object): 

- Car.java :
  1. package dp.prototype;  
  2.   
  3. public class Car implements Cloneable{  
  4.     // 也許還有一些複雜的設定  
  5.     private Wheel[] wheels;  
  6.       
  7.     public Car(){  
  8.         // Initialize takes time for one second...  
  9.         System.out.println("Car constructor: Initialization...");  
  10.         try {  
  11.             Thread.sleep(1000);  
  12.         } catch (InterruptedException e) {  
  13.             e.printStackTrace();  
  14.         }  
  15.         wheels = new Wheel[4];  
  16.         for(int i=0; i<4; i++) wheels[i] = new Wheel();  
  17.     }  
  18.       
  19.     protected Object clone() throws CloneNotSupportedException {  
  20.         Car copy = (Car) super.clone();  
  21.         copy.wheels = (Wheel[]) this.wheels.clone();  
  22.         for(int i = 0; i < this.wheels.length; i++) {  
  23.             copy.wheels[i] = (Wheel) this.wheels[i].clone();  
  24.         }  
  25.         return copy;   
  26.     }  
  27.     // 也許還有別的方法  
  28. }  

- Wheel.java :
  1. package dp.prototype;  
  2.   
  3. public class Wheel implements Cloneable{  
  4.     public Wheel(){  
  5.         // Initialization takes time.  
  6.         System.out.println("Wheel Constructor: Initialization...");  
  7.         try {  
  8.             Thread.sleep(500);  
  9.         } catch (InterruptedException e) {            
  10.             e.printStackTrace();  
  11.         }  
  12.     }  
  13.     protected Object clone() throws CloneNotSupportedException {   
  14.         return super.clone();   
  15.     }   
  16. }  

- Cars.java :
  1. package dp.prototype;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.Map;  
  5.   
  6. public class Cars {  
  7.     private Map prototypes = new HashMap();  
  8.     void addPrototype(String brand, Car prototype) {  
  9.         prototypes.put(brand, prototype);  
  10.     }  
  11.     Car getPrototype(String brand) throws CloneNotSupportedException {  
  12.         return (Car) prototypes.get(brand).clone();  
  13.     }  
  14. }  

- Main.java :
  1. package dp.prototype;  
  2.   
  3. public class Main {  
  4.     public static void main(String[] args) throws CloneNotSupportedException{  
  5.         long bmwST = System.currentTimeMillis();  
  6.         System.out.println("Create BMW...");  
  7.         Car bmw = new Car();  
  8.         System.out.println("taking "+(System.currentTimeMillis()-bmwST)/1000+" sec\n");  
  9.         // 作一些 BMW 複雜的初始、設定、有的沒的  
  10.           
  11.         long benzST = System.currentTimeMillis();  
  12.         System.out.print("Create Benz...");  
  13.         Car benz = new Car();  
  14.         System.out.println("taking "+(System.currentTimeMillis()-benzST)/1000+" sec\n");  
  15.         // 作一些 BENZ 複雜的初始、設定、有的沒的  
  16.         Cars cars = new Cars();  
  17.           
  18.           
  19.         cars.addPrototype("BMW", bmw);         
  20.         cars.addPrototype("BENS", benz);  
  21.           
  22.         // 取得 BMW 原型複製  
  23.         long protoST = System.currentTimeMillis();  
  24.         System.out.println("Take Prototype of BMW...");  
  25.         Car bmwPrototype = cars.getPrototype("BMW");  
  26.         System.out.println("taking "+(System.currentTimeMillis()-protoST)+" ms\n");  
  27.     }  
  28. }  

執行結果 : 
Create BMW...
Car constructor: Initialization...
Wheel Constructor: Initialization...
Wheel Constructor: Initialization...
Wheel Constructor: Initialization...
Wheel Constructor: Initialization...
taking 3 sec

Create Benz...Car constructor: Initialization...
Wheel Constructor: Initialization...
Wheel Constructor: Initialization...
Wheel Constructor: Initialization...
Wheel Constructor: Initialization...
taking 3 sec

Take Prototype of BMW...
taking 0 ms // 由輸出結果可以知道透過 prototype 可以省掉Constructor 的 Initialization 時間...

Prototype模式可應用於避免子類化物件創建者(object creator),在 Gof 的設計模式書中有個範例,設計一個通用的圖型編輯器框架。在這個框架中有個工具列,您可以在上面選擇符號以加入圖片中,並可以隨時調整符號的位置等。 
圖型編輯器框架是通用的,然而事先並不知道這些符號的型態,有人或許會想到繼承圖型編輯器框架來為每個符號設計一個框 架子類別,但由於符號的可能種類很多,這會產生相當多的子類別,為了避免這種情況,可以透過Prototype模式來減少子類別的數目,可以設計出以下的 結構. 
 
依照這個結構,圖型編輯器框架可以獨立於要套用的符號類別,雖然不知道被複製傳回的物件型態是什麼,但可以用原型複製的方式來建立新物件,且可以按照 Graphics所定義的公開介面來操作這些物件,例如使用範例中的draw()方法來繪製符號. 

補充說明 : 
* Wiki : Prototype Pattern 
The prototype pattern is a creational design pattern used in software development when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects. This pattern is used to:
avoid subclasses of an object creator in the client application, like the abstract factory pattern does.
avoid the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword) when it is prohibitively expensive for a given application.

To implement the pattern, declare an abstract base class that specifies a pure virtual clone() method. Any class that needs a "polymorphic constructor" capability derives itself from the abstract base class, and implements the clone() operation.

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...