-
访问者模式是一种行为设计模式
-
访问者模式被用在针对一组相同类型对象的操作
-
优点是,可以把针对此对象的操作逻辑转移到另外一个类上
-
适合场景:
-
对象结构比较稳定,但经常需要在此对象结构上定义新的操作
-
对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类
-
UML图:
添加不同类型商品的购物车,当点击结算的时候,它计算出所有不同商品需付的费用
通过访问者模式把计算逻辑转移到了另外一个类(访问者)上面
商品接收访问者 accept(Visitor visitor),通过 visitor.visit(this) ,把商品本身传给访问者,访问者接收传递过来的商品,然后可以对该商品进行费用计算的逻辑,这样计算的逻辑就与商品本身解耦了。当计算逻辑发生变化后,不影响商品
商品接口类 Goods
package visitor.goods;
import visitor.visitor.Visitor;
/**
* @author 土味儿
* Date 2021/8/3
* @version 1.0
* 商品接口
* 被访问者
*/
public interface Goods {
/**
* 接受访问者
* 返回值为该商品的费用
* 交给访问者去计算
* @param visitor
* @return
*/
public int accept(Visitor visitor);
public String getName();
public int getNum();
public int getPrice();
}
商品抽象类 AbstractGoods
package visitor.goods;
import visitor.visitor.Visitor;
/**
* @author 土味儿
* Date 2021/8/3
* @version 1.0
* 抽象商品类
*/
public abstract class AbstractGoods implements Goods{
// 商品名称
private String name;
// 商品数量
private int num;
// 商品价格
private int price;
/**
* 构造器
* @param name
* @param num
*/
public AbstractGoods(String name, int num,int price) {
this.name = name;
this.num = num;
this.price = price;
}
@Override
public abstract int accept(Visitor visitor);
public String getName() {
return name;
}
public int getNum() {
return num;
}
public int getPrice() {
return price;
}
}
具体商品
public class Book extends AbstractGoods{
/**
* 构造器
*
* @param name
* @param num
* @param price
*/
public Book(String name, int num,int price) {
super(name, num,price);
}
/**
* 接受访问者,并且把自已(商品本身)传给访问者,把一些处理逻辑(计算费用)的 【操作权限】 给访问者
* 当处理逻辑发生变化时(如:折扣率),在访问者中修改即可,不影响商品本身
* 因为这个处理逻辑是在商品之外(即访问者中)进行的
* @param visitor
* @return
*/
@Override
public int accept(Visitor visitor) {
return visitor.visit(this);
}
}
public class Fruit extends AbstractGoods{
/**
* 构造器
*
* @param name
* @param num
* @param price
*/
public Fruit(String name, int num, int price) {
super(name, num, price);
}
/**
* 接受访问者,并且把自已(商品本身)传给访问者,把一些处理逻辑(计算费用)的 【操作权限】 给访问者
* 当处理逻辑发生变化时(如:折扣率),在访问者中修改即可,不影响商品本身
* 因为这个处理逻辑是在商品之外(即访问者中)进行的
* @param visitor
* @return
*/
@Override
public int accept(Visitor visitor) {
return visitor.visit(this);
}
}
访问者接口 Visitor
每一个具体商品类目都对应一个visit方法。返回类型根据业务逻辑而定,此处是计算费用,用int
package visitor.visitor;
import visitor.goods.Book;
import visitor.goods.Fruit;
/**
* @author 土味儿
* Date 2021/8/3
* @version 1.0
* 访问者接口
*/
public interface Visitor {
// 每一个具体商品类目都对应一个visit方法。返回类型根据业务逻辑而定,此处是计算费用,用int
int visit(Book book);
int visit(Fruit fruit);
}
具体访问者
public class ShoppingCarVisitorImpl implements Visitor {
/**
* 计算书的费用,并返回费用
* 满100元减10元
* @param book
* @return
*/
@Override
public int visit(Book book) {
int cost = book.getNum() * book.getPrice();
// 满100元减10元
if (cost >= 100) {
cost = cost - 10;
}
return cost;
}
/**
* 计算水果的费用,并返回费用
* 满100元减15元
* @param fruit
* @return
*/
@Override
public int visit(Fruit fruit) {
int cost = fruit.getNum() * fruit.getPrice();
// 满100元减15元
if (cost >= 100) {
cost = cost - 15;
}
return cost;
}
}
购物车 ShoppingCar
相当于结构对象(ObjectStructure)角色
package visitor;
import visitor.goods.Goods;
import visitor.visitor.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author 土味儿
* Date 2021/8/3
* @version 1.0
* 购物车
*/
public class ShoppingCar {
// 商品集合
private static final List<Goods> goods = new ArrayList<>();
// 访问者
private static final Visitor visitor = new ShoppingCarVisitorImpl();
// 添加商品
public void add(Goods g) {
goods.add(g);
}
// 删除商品
public void remove(Goods g) {
goods.remove(g);
}
// 计算总费用
public int getCosts() {
int sum = 0;
int i = 1;
for (Goods g : goods) {
int c = g.accept(visitor);
System.out.println("" + i++ + "\t[" + g.getName() + "] " + g.getNum() + "*" + g.getPrice() + "," + c +"元");
sum = sum + c;
}
System.out.println("-----------------------\n合计:" + sum + " 元");
return sum;
}
}
测试客户端
package visitor;
import visitor.goods.Book;
import visitor.goods.Fruit;
/**
* @author 土味儿
* Date 2021/8/3
* @version 1.0
* 测试客户端
*/
public class Client {
public static void main(String[] args) {
// 创建购物车
ShoppingCar shoppingCar = new ShoppingCar();
// 添加商品
shoppingCar.add(new Book("西游记",5,25));
shoppingCar.add(new Book("三国演义",1,30));
shoppingCar.add(new Fruit("苹果",2,5));
shoppingCar.add(new Fruit("香蕉",5,8));
shoppingCar.add(new Fruit("西瓜",50,3));
// 计算总费用
shoppingCar.getCosts();
}
}
运行结果
1 [西游记] 5*25,115元
2 [三国演义] 1*30,30元
3 [苹果] 2*5,10元
4 [香蕉] 5*8,40元
5 [西瓜] 50*3,135元
-----------------------
合计:330 元