这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战
访问者模式
定义
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。
概念挺抽象的,不会理解;说直白点,比如:书店有多种类型的书,也有多个层级的会员,每一个会员购买不同类型的书折扣不同,访问者模式就是将书最终的价格与不同类型的书分离开来
还不理解往下看演示
结构
抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
演示
1、抽象访问者
设置会员为访问者
public interface Visitor {
public double visit(NovelBook element);
public double visit(TutorialsBook element);
}
2、具体访问者
具体有普通会员和超级会员
// 普通会员
public class Member implements Visitor {
private double discount = 0.9; // 折扣
@Override
public double visit(NovelBook element) {
return element.getPrice() * discount;
}
@Override
public double visit(TutorialsBook element) {
return element.getPrice() * discount;
}
}
// 超级会员
public class SuperMember implements Visitor {
private double discount = 0.8; // 折扣
@Override
public double visit(NovelBook element) {
return element.getPrice() * discount;
}
@Override
public double visit(TutorialsBook element) {
return element.getPrice() * discount;
}
}
3、抽象元素
每个元素都是一个类型的书
public abstract class BookElement {
protected double price;
public BookElement(double price) {
this.price = price;
}
public double getPrice() {
return this.price;
}
public abstract double accept(Visitor visitor);
}
4、具体元素
// 小说
public class NovelBook extends BookElement {
public NovelBook(double price) {
super(price);
}
@Override
public double accept(Visitor visitor) {
return visitor.visit(this);
}
}
// 教科书
public class TutorialsBook extends BookElement {
public TutorialsBook(double price) {
super(price);
}
@Override
public double accept(Visitor visitor) {
return visitor.visit(this);
}
}
5、对象结构
// 书店
public class Bookstore {
private List<BookElement> list = new ArrayList<>();
public double accept(Visitor visitor) {
double sum = 0;
for (BookElement book : list) {
sum += book.accept(visitor);
}
return sum;
}
public void add(BookElement book) {
list.add(book);
}
public void remove(BookElement book) {
list.remove(book);
}
}
6、客户端
public class Client {
public static void main(String[] args) {
Visitor member = new Member();
Visitor superMember = new SuperMember();
Bookstore bookstore = new Bookstore();
bookstore.add(new NovelBook(100));
bookstore.add(new TutorialsBook(60));
System.out.println("普通会员价格:" + bookstore.accept(member));
System.out.println("超级会员价格:" + bookstore.accept(superMember));
// 普通会员:144.0
// 超级会员:128.0
}
}
总结
根据上面的代码可以看出
1、扩展性,再新增一个会员类型时,扩展是比较好的,直接新增一个类就可以了;但是新增一个类型的书时,每个会员类都需要进行修改,违背了开闭原则
2、复用性。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
3、灵活性。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
4、单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
5、封装性,访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性
6、依赖倒置原则,访问者模式依赖了具体类,而没有依赖抽象类,违反了依赖倒置原则。
针对第1点和第6点,可以对访问具体类做一些改变;这样新增一个类型的书时就不需要再修改每个会员类,也符合了依赖倒置原则。
public interface Visitor {
// 依赖抽象类,而不依赖具体类;
public double visit(BookElement element);
}
// 普通会员
public class Member implements Visitor {
private double discount = 0.9;
@Override
public double visit(BookElement element) {
return element.getPrice() * discount;
}
}
// 超级会员
public class SuperMember implements Visitor {
private double discount = 0.8;
@Override
public double visit(BookElement element) {
return element.getPrice() * discount;
}
}
最后,设计模式都已经写完,自己对设计模式的认识也加深了。说说自己的感受吧,每次重新学习设计模式的时候都会有新的感悟,有些设计模式给自己的感觉好像有很多相似的地方,只是处理的方式不一样。学习设计模式我收获更多的是思维上的变化,在很多业务场景都可以借鉴这些设计模式的思想。
所以我始终相信,一门技术的学习不仅仅局限于实现方式,更多是思维上的收获。