Visitor Pattern Introduction (轉)
:namespace prefix = o ns = "urn:schemas--com::office" />
(wang hailong)
Visitor Pattern可能是設計中最複雜的模式了。Visitor Pattern從Double Dispatch Pattern派生而來,由Double一詞可見其複雜度。
Visitor Pattern,顧名思義,有訪問者和被訪問者,既然,以訪問者命名,那麼,主要的工作都是訪問者來做。
本文不從設計入手,而從一個日常生產生活的例子入手,來解釋Visitor Pattern。
一個生產高能電池的廠家,下設一個客戶服務部門,其主要任務之一就是收集的反饋意見,以便改進產品功能和服務質量。
以前,客戶服務部門只是採用發放調查問卷的方式。調查問卷的問題千篇一律,不能針對特殊使用者的興趣點,尤其一些集體使用者,不會認真對待這些問卷,懶得把問卷髮到具體的使用者手裡,而且,很多使用者不願意花時間把調查問卷寄回。調查結果不全面,不真實,幾乎沒有任何效果。
之後,個性化服務、CRM客戶關係管理等概念興起,客戶服務部門引入了一套CRM客戶(使用者)關係管理,對客戶資訊進行管理。針對不同使用者的特點,採用不同的調查方式。採用電話、E-、傳真、問卷、登門拜訪等多種方式,對使用者進行調查訪問。針對一些集體使用者,比如,企事業單位使用者,客戶服務人員首先訪問聯絡該單位集體,安排好時間之後,客戶服務人員具體訪問每一個具體使用者,這些使用者接受客戶服務人員的訪問,給出親身使用電池產品的第一手資料。
還有的情況,集體單位下面還有子單位,比如,公司下面有子公司。那麼,遵循同樣的流程,分別訪問下面的子公司,這些子公司接受客戶服務人員的訪問,安排好時間之後,客戶服務人員具體訪問每一個具體使用者,這些使用者接受客戶服務人員的訪問,給出親身使用電池產品的第一手資料。
我們可以看到,這是一個典型的Visitor Pattern。客戶服務部門就是Visitor,不同型別的使用者就是被訪問者。客戶服務部門(Visitor)幾乎做了所有的工作,儘量不給使用者增加負擔。
下面給出這個例子的示意程式碼。
// 客戶服務部門類,
// 總結CRM客戶關係管理系統的客戶型別資訊,定義以下的方法
class ServiceDepartment{
// 訪問方式
private Email sendEmail(…){
…
}
// 電話訪問方式
private Answer callPhone(…){
…
}
// 訪問使用者。
// 這是ServiceDepartment類的入口點方法。
// 注意,參照下面的User的定義程式碼,User是一個介面型別
public void visitUser(User aUser){
user.accept(this);
}
// 訪問習慣Email訪問的使用者
public void visitEmailUser (EmailUser anEmailUser){
// send email to email user
Email reply = anEmailUser.replyEmail( this.sendEmail() );
// put anwer to database
}
// 訪問習慣電話訪問的使用者
public void visitPhoneUser (PhoneUser aPhoneUser){
// call phone user
Answer answer = A.answerPhone( this. callPhone () );
// put answer to database
}
// 訪問公司使用者
public void visitCompanyUser( CompanyUser companyUser){
// visit company user
List userList = companyUser.getUserList();
for each user in userList{
// 訪問每個使用者,每個使用者都接受訪問。
Visitor.visitUser(user);
// 注意,這裡User的型別有可能是CompanyUser型別。
// 這時形成對子公司使用者的遞迴。
}
}
}
// 以下列出每個使用者類的程式碼,
// 因為使用者是被訪問者,負擔很少。所以,每個使用者的方法都很簡單。
// 公共介面類,每個使用者類都應該實現這個介面。
public interface User{
// 接受客戶服務部門的訪問
public void accept(ServiceDepartment visitor);
};
// Email user 類
public class EmailUser implements User {
// 用Email反饋
public Email replayEmail(Email question){
…
}
// 接受客戶服務部門的訪問
public void accept(ServiceDepartment visitor){
visitor.visitEmailUser(this);
}
};
// Phone user 類
public class PhoneUser implements User{
// 接聽電話,進行反饋
public Answer answerPhone(Phone question){
…
}
// 接受客戶服務部門的訪問
public void accept(ServiceDepartment visitor){
visitor.visitPhoneUser(this);
}
};
// company user 類
public class CompanyUser implements User {
// 接受客戶服務部門的訪問
public void accept(ServiceDepartment visitor){
visitor.visitCompanyUser(this);
}
};
下面給出一個使用上述模式的例子。
public class TestMain{
public void main(String[] args){
// create a visitor
ServiceDepartmant visitor = new ServiceDepartmant();
// 從中取得所有的使用者資訊
// 這些使用者的型別,有可能是PhoneUser, EmailUser,還有可能是CompanyUser。
for each user created from database {
// 訪問每一個使用者,使用者接受訪問,給出反饋,存放到資料庫中
visitor.visitUser(user);
}
}
}
好了,所有的示意程式碼都在這裡了。現在,我們考慮問題的變化和擴充套件。畢竟,設計模式的目的就是為了讓變化的部分越小越好,越簡單越好。
客戶服務部門引入CRM客戶關係管理系統,就是為了更好地對應客戶(使用者)資訊的變化。
過了一段時間,CRM客戶關係管理系統加入了一個新使用者的資訊,這個使用者習慣使用傳真回答調查問卷。這時,我們多了一個使用者類,FaxUser。我們需要對上述的ServiceDepartment類(visitor類)進行擴充套件。
第一種方法是,直接修改ServiceDepartment類,增加一個visitFaxUser(FaxUser)方法。這種方法比較直觀,但是需要修改以前的程式碼。
public class ServiceDepartment{
… // 以前的程式碼
// 增加一個新的方法,傳送傳真
private Fax sendFax(…){
…
}
// 增加一個新的方法,訪問習慣傳真的使用者
void visitFaxUser(FaxUser aFaxUser){
Fax fax = aFaxUser.replyFax(this.sendFax());
// 把fax的資訊存放到資料庫
}
};
這時,新增的FaxUser的程式碼如下:
public class FaxUser implements User{
// 用傳真反饋
public Fax replyFax(Fax fax){
…
}
// 接受客戶服務部門的訪問
public void accept(ServiceDepartment visitor){
visitor.visitFaxUser(this);
}
}
第二種方法是,繼承ServiceDepartment類,定義一個新類ExtendedServiceDepartment,增加一個visitFaxUser(FaxUser)方法。這種方法的好處是不用修改以前的程式碼,但是,新增加的User類,需要知道新類ExtendedServiceDepartment的定義。
下面給出示意程式碼,ExtendedServiceDepartment類。
public class ExtendedServiceDepartment extends ServiceDepartment{
// 增加一個新的方法,傳送傳真
private Fax sendFax(…){
…
}
// 增加一個新的方法,訪問習慣傳真的使用者
void visitFaxUser(FaxUser aFaxUser){
Fax fax = aFaxUser.replyFax(this.sendFax());
// 把fax的資訊存放到資料庫
}
};
這時,新增的FaxUser的程式碼如下:
public class FaxUser implements User{
// 用傳真反饋
public Fax replyFax(Fax fax){
…
}
// 接受客戶服務部門的訪問
public void accept(ExtendedServiceDepartment visitor){
visitor.visitFaxUser(this);
}
}
後記 為什麼寫這篇文章?
以前看到一本書,講述。作者解釋Socket的時候舉了一個接聽電話的例子,講述的很明白。
1.你要接聽電話,首先你要有一個電話,所以,第一步,create a Socket. 這裡,Socket就是你的電話。
2.你還要有一個電話號碼,對於你的Socket來說,你的和埠號就是電話號碼,所以,第二步,bind to a port.
3.你還要等在電話旁邊,等電話鈴響,listen to the port.
4.別人要給你打電話,他(她)也要有一個電話,他需要create a Socket。
5.他需要撥打你的電話號碼,connect to your address and port.
6.他的電話來了,你要接聽他的電話,accept his connection。這時,還有來電顯示,你知道他的電話號碼(IP地址)。
7.你同時能接聽幾個打來的電話,你和他們開始通話。read and write.
8.通話結束,你說,“你先掛電話吧,我還要和別人講話。”他把電話掛了,close his Socket。
看了這段之後,我很受啟發。後來寫了一篇文章《Design Pattern Introduction》,提到Observer Pattern,舉了訂閱,簡訊訂閱的例子。
Visitor Pattern,是一個比較複雜的設計模式。有很多關於Visitor Pattern的爭論,有些人建議使用,有些人建議不使用。這裡,我多費些筆墨,把Visitor Pattern作為一個日常生產生活的場景描述出來。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992365/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- JAVA設計模式之 訪問者模式【Visitor Pattern】Java設計模式
- 《Pattern Recognition and Machine Learning》第一章1. INTRODUCTIONMac
- [轉]Golang Functional Options PatternGolangFunction
- Java Tip: 用Reflection實現Visitor模式 (轉)Java模式
- 設計模式、用Delphi描述-->Visitor模式 (轉)設計模式
- 足球戰術->全攻全守之visitor篇 (轉)
- 訂閱屬於自己得資料(Visitor) (轉)
- [轉]Introduction to JAIN SIP API part2AIAPI
- (轉)關於 awk 的 pattern(模式)模式
- Observer Design Pattern in C#! (轉)ServerC#
- 訪問者(Visitor)
- Visitor模式加深理解模式
- Groovy探索 Visitor模式模式
- Observer Pattern - An Enterprise JavaBean Implementation (轉)ServerJavaBean
- 看visitor模式的感受模式
- <url-pattern>/</url-pattern>和<url-pattern>/*</url-pattern>區別
- Introduction to AlgorithmGo
- Introduction to GitGit
- RL Introduction
- 設計模式、用Delphi描述-->Lock pattern (轉)設計模式
- 設計模式、用Delphi描述-->Reference Count pattern (轉)設計模式
- TLS 1.3 IntroductionTLS
- nodejs introductionNodeJS
- Chapter 1:IntroductionAPT
- Introduction to Partitioning
- No Sql Db IntroductionSQL
- Hadoop IntroductionHadoop
- Introduction to Databases and MySQLDatabaseMySql
- Oracle RAC introductionOracle
- HTML 01 - IntroductionHTML
- Introduction to Vetors
- Visitor模式和Observer觀察者模式模式Server
- 我的一個visitor實作
- SVG <pattern>SVG
- Introduction of DataGuard protection mode
- A re-introduction to JavaScriptJavaScript
- [zz]android introductionAndroid
- Introduction to Index Segments (24)Index