??訪問者模式在23種設(shè)計模式算是復(fù)雜的,因此在解釋定義前,先以實際的例子代入一下磨德,覺得理解起來會更好一些。
場景:很多人都有養(yǎng)寵物的習(xí)慣吆视,這里就以此為例
訪問者角色:給寵物喂食的人
具體訪問者角色:主人典挑、其他人
抽象元素角色:動物抽象類
具體元素角色:寵物狗、寵物貓
結(jié)構(gòu)對象角色:主人家
- 創(chuàng)建抽象訪問者接口
interface Person {
void feed(Cat cat);
void feed(Dog dog);
}
- 創(chuàng)建不同的具體訪問者角色 -- 主人/其他人啦吧,同時實現(xiàn)
Person
接口
class Owner implements Person {
@Override
public void feed(Cat cat) {
System.out.println("主人喂食貓");
}
@Override
public void feed(Dog dog) {
System.out.println("主人喂食狗");
}
}
class Someone implements Person {
@Override
public void feed(Cat cat) {
System.out.println("其他人喂食貓");
}
@Override
public void feed(Dog dog) {
System.out.println("其他人喂食狗");
}
}
- 創(chuàng)建 抽象節(jié)點 -- 寵物
interface Animal {
void accept(Person person);
}
- 創(chuàng)建實現(xiàn)
Animal
接口的 具體節(jié)點(元素)
/**
* 具體節(jié)點(元素)角色 -- 寵物狗
*/
class Dog implements Animal {
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("好好吃您觉,汪汪汪!J谧摇琳水!");
}
}
/**
* 具體節(jié)點(元素)角色 -- 寵物貓
*/
class Cat implements Animal {
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("好好吃,喵喵喵0愣选T谛ⅰ!");
}
}
- 創(chuàng)建結(jié)構(gòu)對象角色
class Home {
private List<Animal> nodeList = new ArrayList<>();
void action(Person person) {
for (Animal node : nodeList) {
node.accept(person);
}
}
/**
* 添加操作
*
* @param animal 動物
*/
void add(Animal animal) {
nodeList.add(animal);
}
}
- 創(chuàng)建客戶端淮摔,用于測試
public class Client {
public static void main(String[] args) {
Home home = new Home();
home.add(new Dog());
home.add(new Cat());
Owner owner = new Owner();
home.action(owner);
Someone someone = new Someone();
home.action(someone);
}
}
- 運行結(jié)果
主人喂食狗
好好吃私沮,汪汪汪!:统取仔燕!
主人喂食貓
好好吃造垛,喵喵喵!N蟆五辽!
其他人喂食狗
好好吃,汪汪汪M馑 1计辍!
其他人喂食貓
好好吃吁讨,喵喵喵!B屠省建丧!
??訪問者模式(Visitor)表示一個作用于某對象結(jié)構(gòu)中的各元素的操作波势。它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作翎朱。訪問者模式是一種對象行為模式。
- Visitor 抽象訪問者
??為該對象結(jié)構(gòu)中的ConcreteElement
的每一個類聲明的一個操作尺铣。
- ConcreteVisitor 具體訪問者
??實現(xiàn)Visitor
申明的每一個操作拴曲,每一個操作實現(xiàn)算法的一部分。
- Element 抽象元素
??它定義了一個接受訪問者(accept
)的方法凛忿,其意義是指澈灼,每一個元素都要可以被訪問者訪問。
- ConcreteElement 具體元素
??它提供接受訪問方法的具體實現(xiàn)店溢,而這個具體的實現(xiàn)叁熔,通常情況下是使用訪問者提供的訪問該元素類的方法。
- ObjectStructure 對象結(jié)構(gòu)角色
??對象結(jié)構(gòu)是一個抽象表述床牧,具體點可以理解為一個具有容器性質(zhì)或者復(fù)合對象特性的類荣回,它會含有一組元素(Element
),并且可以迭代這些元素戈咳,供訪問者訪問心软。
優(yōu)點:
?新增訪問操作變得更加簡單。
?能夠使得用戶在不修改現(xiàn)有類的層次結(jié)構(gòu)下著蛙,定義該類層次結(jié)構(gòu)的操作删铃。
?將有關(guān)元素對象的訪問行為集中到一個訪問者對象中,而不是分散搞一個個的元素類中册踩。
缺點:
?增加新的元素類很困難泳姐。在訪問者模式中,每增加一個新的元素類都意味著要在抽象訪問者角色中增加一個新的抽象操作暂吉,并在每一個具體訪問者類中增加相應(yīng)的具體操作胖秒,違背了“開閉原則”的要求缎患。
?破壞封裝。當(dāng)采用訪問者模式的時候阎肝,就會打破組合類的封裝挤渔。
- 抽象訪問者
public abstract class Visitor {
public abstract void visitConcreteElementA(ConcreteElementA concreteElementA);
public abstract void visitConcreteElementB(ConcreteElementB concreteElementB);
}
- 具體訪問者
public class ConcreteVisitor1 extends Visitor {
@Override
public void visitConcreteElementA(ConcreteElementA concreteElementA) {
System.out.println("ConcreteVisitor1:"+concreteElementA.operationA());
}
@Override
public void visitConcreteElementB(ConcreteElementB concreteElementB) {
System.out.println("ConcreteVisitor1:"+concreteElementB.operationB());
}
}
public class ConcreteVisitor2 extends Visitor {
@Override
public void visitConcreteElementA(ConcreteElementA concreteElementA) {
System.out.println("ConcreteVisitor2:"+concreteElementA.operationA());
}
@Override
public void visitConcreteElementB(ConcreteElementB concreteElementB) {
System.out.println("ConcreteVisitor2:"+concreteElementB.operationB());
}
}
- 抽象元素
public abstract class Element {
public abstract void accept(Visitor visitor);
}
- 具體元素
public class ConcreteElementA extends Element {
@Override
public void accept(Visitor visitor) {
visitor.visitConcreteElementA(this);
}
public String operationA() {
return "ConcreteElementA";
}
}
public class ConcreteElementB extends Element {
@Override
public void accept(Visitor visitor) {
visitor.visitConcreteElementB(this);
}
public String operationB() {
return "ConcreteElementB";
}
}
- 對象結(jié)構(gòu)角色
public class ObjectStructure {
private List<Element> elments = new LinkedList<Element>();
// 新增
public void attach(Element element) {
elments.add(element);
}
// 刪除
public void detach(Element element) {
elments.remove(element);
}
public void accept(Visitor visitor) {
for (Element elment:elments ) {
elment.accept(visitor);
}
}
}
- 測試類
public class Test {
public static void main(String[]args) {
ObjectStructure o = new ObjectStructure();
o.attach(new ConcreteElementA());
o.attach(new ConcreteElementB());
ConcreteVisitor1 concreteVisitor1 = new ConcreteVisitor1();
ConcreteVisitor2 concreteVisitor2 = new ConcreteVisitor2();
o.accept(concreteVisitor1);
o.accept(concreteVisitor2);
}
}
- 運行結(jié)果
ConcreteVisitor1:ConcreteElementA
ConcreteVisitor1:ConcreteElementB
ConcreteVisitor2:ConcreteElementA
ConcreteVisitor2:ConcreteElementB
??GoF四人中的一個作者說過:大多時候你并不需要訪問者模式,但一旦你需要訪問者模式時风题,那就是真的需要它了判导。事實上我們很難找到數(shù)據(jù)結(jié)構(gòu)不變化的情況,所以用訪問者模式的機會也就不太多了沛硅。