Visitor 模式


您的系統中有客戶、會員與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 結構類圖如下: