您的系統中有客戶、會員與VIP,假設經過設計考量,確定以下的設計是必要的:
class Customer { void doCustomer() { System.out.println("客戶服務"); } void pay() { System.out.println("結帳"); } }
class Member extends Customer { void doMember() { System.out.println("會員服務"); } } class VIP extends Customer { void doVIP() { System.out.println("VIP 服務"); } } 您要設計一個結帳功能,針對客戶所使用的服務計算客戶要付的費用,計算的演算大部份是針對Customer來進行操作,但其中幾個步驟,不免要針對特定客戶類型來設計,例如:
class Service { void doService(Customer customer) { customer.doCustomer(); if(customer instanceof Member) { ((Member) customer).doMember(); } if(customer instanceof VIP) { ((VIP) customer).doVIP(); } customer.pay(); } } 使用instanceof來判斷物件類型,一般是不被鼓勵的,如果您的客戶類型繁多,這樣的結構化設計會逐漸加深程式碼的繁複。一般多希望利用多型操作來解決問題,不要針對特定類型來進行設計。
如果經過仔細的考量設計,必須針對特定類型來進行操作確實是不可避免的,那麼您可以換個方式,例如:
interface Visitable { void accept(Visitor visitor); }
interface Visitor { void visit(Member member); void visit(VIP vip); }
class Customer implements Visitable { void doCustomer() { System.out.println("客戶服務"); } void pay() { System.out.println("結帳"); } public void accept(Visitor visitor) { // nothing to do } }
class Member extends Customer { void doMember() { System.out.println("會員服務"); } @Override public void accept(Visitor visitor) { visitor.visit(this); // 看似多型,其實是 overload } } class VIP extends Customer { void doVIP() { System.out.println("VIP 服務"); } @Override public void accept(Visitor visitor) { visitor.visit(this); // 看似多型,其實是 overload } }
class VisitorImpl implements Visitor { public void visit(Member member) { member.doMember(); } public void visit(VIP vip) { vip.doVIP(); } }
class Service { private Visitor visitor = new VisitorImpl(); void doService(Customer customer) { customer.doCustomer(); ((Visitable) customer).accept(visitor); customer.pay(); } }
public class Main { public static void main(String[] args) { Service service = new Service(); service.doService(new Member()); } } doService()方法接受的是Customer型態,原先為了針對特別型態作特定操作,不得已使用instanceof作判斷,而上面這個設計,則是由Visitor登門入戶,使用物件中的this參考名稱之型態資訊,由物件自行選擇要呼叫的overload方法。
這是Visitor模式的一個實現,Visitor 模式的目的,是將演算流程與所操作的物件之特定結構分離,這樣分離之後,針對特定物件的操作部份將集中在Visitor中管理,並可以隨時修改操作。
靜
態語言可以使用overload來實現Visitor模式,overload是編譯時期就決定要呼叫哪個方法,對於不支援overload的語言,或者是
動態語言,則可以在Visitor的方法名稱上作個區別(只是少了單一方法名稱呼叫的方便,其實若您將overload的型態資訊也看成是方法名稱的一部
份,也就是方法簽署,道理就相同了)。例如,以下是Python的實現方式:
class Customer: def doCustomer(self): print("客戶服務") def pay(self): print("結帳") def accept(self, visitor): pass
class Member(Customer): def doMember(self): print("會員服務") def accept(self, visitor): visitor.visitMember(self) class VIP(Customer): def doVIP(self): print("VIP 服務") def accept(self, visitor): visitor.visitVIP(self)
class VisitorImpl: def visitMember(self, member): member.doMember(); def visitVIP(self, vip): vip.doVIP() class Service: def __init__(self): self.visitor = VisitorImpl() def doService(self, customer): customer.doCustomer() customer.accept(self.visitor) customer.pay()
service = Service() service.doService(VIP())
Visitor模式的 UML 結構類圖如下:
 |