1.定义:作用于某对象结构中的各个元素的操作,可以在不改变元素类的前提下定义作用于这些元素的新操作
2.UML
3.涉及角色
1.抽象访问者:定义了访问者需要访问的元素,该元素采用参数的形式声明,违背依赖倒置原则
2.具体访问者:实现抽象访问者声明的操作,实现算法的一部分 该算法片段对应于结构对象中类
3.抽象元素:该类中的方法有两种 1自有方法2接受访问者访问的方法,接受访问者访问的方法的参数为抽象访问者
4.具体元素:实现抽象元素中接受访问者访问的方法
5.结构对象:结构对象 能够枚举她的元素 提供一个高层的接口方便访问者访问她的元素。同时提供添加元素以及删除元素的操作,也就是说采用集合的形式操作元素哦
4.优点
1.符合单一职责原则。具体元素和访问者职责明确
2.易于扩展。
数据结构和作用于结构上的操作分离。封装在访问者中的操作可能发生变化,在不修改元素的前提下可以实现对变化部分的扩展
元素类可以通过接受不同的访问者实现对不同操作的扩展
3.增加新的操作很容易。增加新的操作意味着增加新的访问者,因为该模式将有关的行为集中到一个访问者对象中
5.缺点
1.违背依赖倒置原则,访问者依赖的是具体元素而不是抽象元素,导致扩展比较困难
2.具体元素变更比较困难,没增加一个新的元素需要在抽象访问者中增加一个新的抽象操作,并在具体访问者中增加对应的实现,违背开闭原则
3.破坏封装违背迪米特原则。访问者要访问一个类必然要求该类公布一些方法和数据,也就是说访问者关注了其他类的内部细节
6.使用场景
1.需要对一个对象结构中的对象进行不同且不相关的操作,同时避免操作污染这些对象类
2.数据结构相对稳定的系统,因为它是将数据结构和作用于结构上的操作实现解耦,使得操作可以自由的演化,也就是说系统中的具体元素固定或者发生变化的概率很小,此时作用具体元素的操作即访问者就可以自由演化
3.一个对象结构包含很多类对象,它们有不同的接口,对这些对象实施依赖于这些对象具体类的操作
7. code
模拟超市购物场景,作为客户需要买入东西,但是作为收银员需要按照不同的商品类型来计算价格,如果书本直接按照价格收费,如果是水果需要称重收费。该场景下客户和收银员都是不同的访问者。书本或水果是具体元素。我们的购物车就是对象结构
客户端
package com.patterns.behavior.visitor;
public class Client {
public static void main(String[] args) {
ShopCar shopCar = new ShopCar();
shopCar.add(new Books());
shopCar.add(new Fruits());
Customer customer = new Customer();
shopCar.display(customer);
Cashier cashier = new Cashier();
shopCar.display(cashier);
}
}
抽象访问者
// 抽象访问者
// 抽象类或者接口声明访问者需要访问哪些元素
// 具体的就是访问方法中定义的参数,该处违背了依赖导致原则,因为参数为具体的元素
public interface IVisitor {
public void visitor(Books books);
public void visitor(Fruits fruits);
}
//具体访问者(客户)
public class Customer implements IVisitor {
@Override
public void visitor(Books books) {
System.out.println("购买books");
}
@Override
public void visitor(Fruits fruits) {
System.out.println("购买fruits");
}
}
//具体访问者(收银员)
//具体访问者,访问者访问具体元素后需要执行的操作以及业务
public class Cashier implements IVisitor{
@Override
public void visitor(Books books) {
System.out.println("按照图书价格收取费用");
}
@Override
public void visitor(Fruits fruits) {
System.out.println("需要称重量然后收取费用");
}
}
//抽象元素
//抽象元素
// 接口或者抽象类,该类中一般会有两个方法
// 一个为自有的方法,一个为允许哪类访问者访问的方法即该方法声明了访问者的类型
public abstract class Goods {
//允许哪类访问者访问
public abstract void accpet(IVisitor visitor);
//自有方法
public void self() {}
}
//具体元素(书籍)
//具体元素,实现抽象元素方法,该处实现了双分派
//第一次分派将具体的访问者以参数的形式传递给具体元素即实现具体元素accpet的调用
//第二次分派具体元素类l类中调用具体访问者中的访问方法并将this传入即实现访问方法的调用
public class Books extends Goods {
@Override
public void accpet(IVisitor visitor) {
visitor.visitor(Books.this);
}
}
具体元素(水果)
public class Fruits extends Goods {
@Override
public void accpet(IVisitor visitor) {
visitor.visitor(Fruits.this);
}
}
对象结构(购物车)
//结构对象即一个元素的容器
public class ShopCar {
private List<Goods> goods = new ArrayList<Goods>();
public void add(Goods good) {
goods.add(good);
}
public void remove(Goods good) {
goods.remove(good);
}
public void display(IVisitor visitor) {
for(Goods good : goods) {
good.accpet(visitor);
}
}
}