#訪問者模式(Visitor Pattern)
##簡介
訪問者模式是一種將算法與對象結構分離的軟件設計模式。
這個模式的基本想法如下:首先我們擁有一個由許多對象構成的對象結構,這些對象的類都擁有一個accept方法用來接受訪問者對象;訪問者是一個接口,它擁有一個visit方法,這個方法對訪問到的對象結構中不同類型的元素作出不同的反應;在對象結構的一次訪問過程中,我們遍歷整個對象結構,對每一個元素都實施accept方法,在每一個元素的accept方法中回調訪問者的visit方法,從而使訪問者得以處理對象結構的每一個元素。我們可以針對對象結構設計不同的實在的訪問者類來完成不同的操作。
訪問者模式使得我們可以在傳統的單分派語言(如Smalltalk、Java和C++)中模擬雙分派技術。對于支持多分派的語言(如CLOS),訪問者模式已經內置于語言特性之中了,從而不再重要。
```Java
interface Visitor {
void visit(Wheel wheel);
void visit(Engine engine);
void visit(Body body);
void visit(Car car);
}
class Wheel {
private String name;
Wheel(String name) {
this.name = name;
}
String getName() {
return this.name;
}
void accept(Visitor visitor) {
visitor.visit(this);
}
}
class Engine {
void accept(Visitor visitor) {
visitor.visit(this);
}
}
class Body {
void accept(Visitor visitor) {
visitor.visit(this);
}
}
class Car {
private Engine engine = new Engine();
private Body body = new Body();
private Wheel[] wheels
= { new Wheel("front left"), new Wheel("front right"),
new Wheel("back left") , new Wheel("back right") };
void accept(Visitor visitor) {
visitor.visit(this);
engine.accept(visitor);
body.accept(visitor);
for (int i = 0; i < wheels.length; ++ i)
wheels[i].accept(visitor);
}
}
class PrintVisitor implements Visitor {
public void visit(Wheel wheel) {
System.out.println("Visiting " + wheel.getName()
+ " wheel");
}
public void visit(Engine engine) {
System.out.println("Visiting engine");
}
public void visit(Body body) {
System.out.println("Visiting body");
}
public void visit(Car car) {
System.out.println("Visiting car");
}
}
public class VisitorDemo {
static public void main(String[] args) {
Car car = new Car();
Visitor visitor = new PrintVisitor();
car.accept(visitor);
}
}
```
##實例
###男人和女人
'人'類,是'男人'和'女人'類的抽象類
```
abstract class person
{
protected string action;
public string Action
{
get {return action;}
set {action = value};
}
//得到結論或者反應
public abstract void GetConclusion();
}
```
"男人"類
```
class Man: Person
{
//得到結論或反應
public override void GetConclusion()
{
if (action == "成功")
{
Console.WriteLine("{0}{1}時,背后多半有一個偉大的女人",this.GetType().name,action);
}
else if (action == "失敗")
{
Console.WriteLine("{0}{1}時,"蒙頭喝酒,誰也不用勸",this.GetType().name,action);
}
else if (action == "戀愛")
{
Console.WriteLine("{0}{1}時,"凡事不懂也要裝懂",this.GetType().name,action);
}
}
}
```
"女人"類
```
class Woman: Person
{
//得到結論或反應
public override void GetConclusion()
{
if (action == "成功")
{
Console.WriteLine("{0}{1}時,背后多半有一個不成功的男人",this.GetType().name,action);
}
else if (action == "失敗")
{
Console.WriteLine("{0}{1}時,"淚眼汪汪,誰也勸不了",this.GetType().name,action);
}
else if (action == "戀愛")
{
Console.WriteLine("{0}{1}時,"凡事懂也要裝不懂",this.GetType().name,action);
}
}
}
```
客戶端代碼
```
static void Main(string[] args)
{
IList<Person> persons = new List<Person>();
Person man1 = new Man();
man1.Action = "成功";
persons.add(man1);
Person woman1 = new Woman();
woman1.Action = "成功";
persons.add(woman1);
Person man2 = new Man();
man2.Action = "失敗";
persons.add(man2);
Person woman2 = new Woman();
woman2.Action = "失敗";
persons.add(woman2);
Person man3 = new Man();
man3.Action = "戀愛";
persons.add(man3);
Person woman3 = new Woman();
woman3.Action = "戀愛";
persons.add(woman3);
foreach (Person item in persons)
{
item.GetConclusion();
}
Console.Read();
}
```
這個算是面向對象編程,但是'男人'和'女人'類當中的那些if....else....很是礙眼。而且如果我們增加一個'結婚狀態',怎么改?
###用模式實現
'狀態'的抽象和'人'的抽象
```
abstract class Action
{
//得到男人結論或反應
public abstract void GetManConclusion(Man concreteElementA);
public abstract void GetWomanConclusion(Woman concreteElementB);
}
abstract class Person
{
//接受
public abstract void Accept(Action visitor);
}
```
具體"狀態"類
```
//成功
class Success: Action
{
public override void GetManConclusion(Man concreteElementA)
{
Console.WriteLine("{0}{1}時,背后多半有一個偉大的女人",this.GetType().name,action);
}
public abstract void GetWomanConclusion(Woman concreteElementB)
{
Console.WriteLine("{0}{1}時,背后多半有一個不成功的男人",this.GetType().name,action);
}
}
//失敗
class Falling: Action
{
public override void GetManConclusion(Man concreteElementA)
{
Console.WriteLine("{0}{1}時,"蒙頭喝酒,誰也不用勸",this.GetType().name,action);
}
public abstract void GetWomanConclusion(Woman concreteElementB)
{
Console.WriteLine("{0}{1}時,"淚眼汪汪,誰也勸不了",this.GetType().name,action);
}
}
//戀愛
class Amativeness: Action
{
public override void GetManConclusion(Man concreteElementA)
{
Console.WriteLine("{0}{1}時,"凡事不懂也要裝懂",this.GetType().name,action);
}
public abstract void GetWomanConclusion(Woman concreteElementB)
{
Console.WriteLine("{0}{1}時,"凡事懂也要裝不懂",this.GetType().name,action);
}
}
```
"男人"類和"女人"類
```
//男人
class Man : Person
{
public override void Accept(Action visitor)
{
//***
visitor.GetManConclusion(this);
}
}
//女人
class Woman : Person
{
public override void Accept(Action visitor)
{
//***
visitor.GetWomanConclusion(this);
}
}
```
這里需要講下雙派技術,首先客戶端將具體狀態作為參數傳遞給"男人"完成了一次分派,然后男人類調用方法"男人反應",同時將自己作為參數傳遞進去。這便完成了第二次分派。雙分派意味著得到執行的操作決定于請求的種類和兩種接受者的類型。"接受"方法就是一個雙分派的操作,它得到執行的操作不僅決定于'狀態'類的具體狀態,還決定于它訪問的'人'的類別。
```
//對象結構
class ObjectStructure
{
private IList<Person>elements = new List<Person>();
//增加
public void Attach(Person element)
{
elements.Add(element);
}
//移除
public void Detach(Person element)
{
elements.Remove(element);
}
//查看顯示
public void Display(Action visitor)
{
foreach(Person e in elements)
{
e.Accept(visitor);
}
}
}
```
客戶端代碼
```
static void Main(string[] args)
{
ObjectStructure o = new ObjectStructure();
o.Attach(new Man());
o.Attach(new Man());
//成功時反應
Success v1 = new Success();
o.Display(v1);
//失敗時反應
Success v1 = new Success();
o.Display(v1);
//戀愛時反應
Success v1 = new Success();
o.Display(v1);
Console.Read();
}
```
***訪問者適合于數據結構相對穩定的系統。它把數據結構和作用于結構之上的操作之間的耦合解脫開來,使得操作集合可以相對自由地y演化。***
***訪問者模式的目的是要把處理從數據結構分離出來。如果這樣的系統有比較穩定的數據結構,又有易于變化的算法的話,使用訪問者模式就是比較適合的,因為訪問者模式使得算法操作的增加變得容易。***
***那其實訪問者模式的優點就是增加新的操作很容易,因為增加新的操作就意味著增加一個新的訪問者,訪問者模式將有關的行為集中到一個訪問者對象中。***
***那訪問者的缺點其實也就是使得增加新的數據結構變的困難了。***
正如《設計模式》的作者GoF對訪問者模式的描述:大多數情況下,你并需要使用訪問者模式,但是當你一旦需要使用它時,那你就是真的需要它了。當然這只是針對真正的大牛而言。在現實情況下(至少是我所處的環境當中),很多人往往沉迷于設計模式,他們使用一種設計模式時,從來不去認真考慮所使用的模式是否適合這種場景,而往往只是想展示一下自己對面向對象設計的駕馭能力。編程時有這種心理,往往會發生濫用設計模式的情況。所以,在學習設計模式時,一定要理解模式的適用性。必須做到使用一種模式是因為了解它的優點,不使用一種模式是因為了解它的弊端;而不是使用一種模式是因為不了解它的弊端,不使用一種模式是因為不了解它的優點。