程式扎記: [OO 設計模式] Gossip@DesignPattern : Behavioral - Visitor 模式

標籤

2016年7月2日 星期六

[OO 設計模式] Gossip@DesignPattern : Behavioral - Visitor 模式

轉載自 這裡 
您的系統中有 客戶會員 與 VIP,假設經過設計考量,確定以下的設計是必要的 : 
  1. class Customer {  
  2.     void doCustomer() {  
  3.         System.out.println("客戶服務");  
  4.     }  
  5.     void pay() {  
  6.         System.out.println("結帳");  
  7.     }  
  8. }  
  9.   
  10. class Member extends Customer {  
  11.     void doMember() {  
  12.         System.out.println("會員服務");  
  13.     }  
  14. }  
  15.       
  16. class VIP extends Customer {  
  17.     void doVIP() {  
  18.         System.out.println("VIP 服務");  
  19.     }  
  20. }  
您要設計一個結帳功能,針對客戶所使用的服務計算客戶要付的費用,計算的演算大部份是針對 Customer 來進行操作,但其中幾個步驟,不免要針對特定客戶類型來設計,例如 : 
  1. class Service {  
  2.     void doService(Customer customer) {  
  3.         customer.doCustomer();  
  4.         if(customer instanceof Member) {  
  5.             ((Member) customer).doMember();  
  6.         }  
  7.         if(customer instanceof VIP) {  
  8.             ((VIP) customer).doVIP();  
  9.         }  
  10.         customer.pay();  
  11.     }  
  12. }  
使用 instanceof 來判斷物件類型,一般是不被鼓勵的,如果您的客戶類型繁多,這樣的結構化設計會逐漸加深程式碼的繁複。一般多希望利用多型操作來解決問題,不要針對特定類型來進行設計. 如果經過仔細的考量設計,必須針對特定類型來進行操作確實是不可避免的,那麼您可以換個方式,例如 : 
  1. interface Visitable {  
  2.     void accept(Visitor visitor);  
  3. }  
  4.   
  5. interface Visitor {  
  6.     void visit(Member member);  
  7.     void visit(VIP vip);  
  8. }  
  9.   
  10. class Customer implements Visitable {  
  11.     void doCustomer() {  
  12.         System.out.println("客戶服務");  
  13.     }  
  14.     void pay() {  
  15.         System.out.println("結帳");  
  16.     }  
  17.     public void accept(Visitor visitor) {  
  18.         // nothing to do  
  19.     }      
  20. }  
  21.   
  22. class Member extends Customer {  
  23.     void doMember() {  
  24.         System.out.println("會員服務");  
  25.     }  
  26.     @Override  
  27.     public void accept(Visitor visitor) {  
  28.         visitor.visit(this); // 看似多型,其實是 overload  
  29.     }      
  30. }  
  31.       
  32. class VIP extends Customer {  
  33.     void doVIP() {  
  34.         System.out.println("VIP 服務");  
  35.     }  
  36.     @Override  
  37.     public void accept(Visitor visitor) {  
  38.         visitor.visit(this); // 看似多型,其實是 overload  
  39.     }      
  40. }  
  41.   
  42. class VisitorImpl implements Visitor {  
  43.     public void visit(Member member) {  
  44.         member.doMember();  
  45.     }  
  46.     public void visit(VIP vip) {  
  47.         vip.doVIP();  
  48.     }  
  49. }  
  50.   
  51. class Service {  
  52.     private Visitor visitor = new VisitorImpl();  
  53.     void doService(Customer customer) {  
  54.         customer.doCustomer();  
  55.         ((Visitable) customer).accept(visitor);  
  56.         customer.pay();  
  57.     }  
  58. }  
  59.   
  60. public class Main {  
  61.     public static void main(String[] args) {  
  62.         Service service = new Service();  
  63.         service.doService(new Member());  
  64.     }  
  65. }  
doService() 方法接受的是 Customer 型態,原先為了針對特別型態作特定操作,不得已使用 instanceof 作判斷,而上面這個設計,則是 Visitor 登門入戶,使用物件中的 this 參考名稱之型態資訊,由物件自行選擇要呼叫的overload方法
這是Visitor模式的一個實現,Visitor 模式的目的,是將演算流程與所操作的物件之特定結構分離,這樣分離之後,針對特定物件的操作部份將集中在 Visitor 中管理,並可以隨時修改操作

Visitor 模式的 UML 結構類圖如下 : 

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!