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