访问者模式
# 访问者模式
# 定义
访问者模式(Visitor Pattern)是一种行为设计模式,它用于将算法操作从数据结构中分离出来。访问者模式可以在不修改数据结构的情况下,定义新的操作或算法,并将其应用于数据结构中的元素。
访问者模式将数据结构和对数据结构的操作分离开来。数据结构通常由一组不同类型的元素组成,而这些元素可能具有不同的类型和结构。访问者模式通过定义访问者对象,让访问者对象可以独立于数据结构进行操作。访问者对象可以访问并处理数据结构中的每个元素,而不需要在元素类中添加新的操作方法。
访问者模式与策略模式的区别
访问者模式通过访问者对象用于对元素进行不同的操作,而策略模式用于在运行时选择算法或策略。
访问者模式将操作封装在访问者类中,而策略模式将算法封装在策略类中。
# 结构
抽象访问者(Visitor):定义了可以访问和处理每个具体元素的操作方法。它通常定义了多个访问方法,每个方法对应一个具体元素类型。
具体访问者(ConcreteVisitor):实现了抽象访问者定义的操作方法。每个具体访问者对应一个具体的操作或算法。
抽象元素(Element):定义了接受访问者对象访问的接口。它通常包含一个接受访问者的方法,该方法将访问者作为参数传入。
具体元素(ConcreteElement):实现了抽象元素定义的接口。每个具体元素都需要实现接受访问者的方法,并在方法中调用访问者的相应操作方法。
对象结构(Object Structure):包含一组元素对象,并提供让访问者访问元素的方法。对象结构可以是一个集合、列表、树等形式的数据结构。
- img: https://bitouyun.com/images/design-pattern/visitor.png
link: https://bitouyun.com/images/design-pattern/visitor.png
name: 访问者模式
2
3
# 优点
访问者模式的优点是可以在不修改元素类的情况下,定义新的操作或算法。它将操作和数据结构解耦,使得添加新的操作变得简单,同时也符合开闭原则
# 缺点
增加了新的访问者类,可能会增加系统的复杂性。
为了让访问者对象能够访问元素的内部状态,可能需要在元素类中暴露一些内部细节,破坏了元素类的封装性。
# 应用场景
1.访问者模式通过将操作从数据结构中分离出来,实现了操作和数据结构的解耦。它适用于需要对复杂的数据结构进行访问和操作,并且希望在不修改数据结构的情况下添加新的操作或算法的场景。
2.当一个数据结构包含多种类型的元素,并且每个元素可能需要执行不同的操作时,可以使用访问者模式。这样可以将特定于元素类型的操作封装到访问者对象中,从而使得操作和元素的类型解耦。
3.当需要在不修改元素类的情况下,添加新的操作或算法时,可以使用访问者模式。通过添加新的访问者对象,可以在不影响元素类的情况下,定义新的操作。
4.当需要对数据结构进行多种不相关的操作时,可以使用访问者模式。通过定义不同的访问者对象,可以在不同的上下文中执行不同的操作,而无需修改数据结构。
# 示例代码
场景
使用访问者模式获取普通产品和促销产品信息
/**
* 产品访问者
* 抽象访问者,定义了访问具体元素的方法,每个方法对应一个具体元素类。通过这些方法,访问者可以对元素进行不同的操作。
*/
public interface ProductVisitor {
// 获取普通商品
void getNormalProduct(NormalProduct normalProduct);
// 获取促销产品
void getPromotionProduct(PromotionProduct promotionProduct);
}
2
3
4
5
6
7
8
9
10
11
12
/**
* 产品价格计算
* 具体元素,实现了抽象元素接口,提供了接受访问者的具体实现。
*/
@Slf4j
public class ProductPriceCalculator implements ProductVisitor {
@Override
public void getNormalProduct(NormalProduct normalProduct) {
log.info("商品:{},价格:{}", normalProduct.getName(), normalProduct.getPrice());
}
@Override
public void getPromotionProduct(PromotionProduct promotionProduct) {
log.info("商品:{},价格:{},折扣:{}", promotionProduct.getName(), promotionProduct.getPrice(), promotionProduct.getDiscountRate());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 产品抽象元素
* 抽象元素,定义一个接受访问者对象的接口,通常会有一个接受(accept)方法,接受访问者方法。
*/
public interface ProductElement {
// 接受访问者方法
void accept(ProductVisitor productVisitor);
}
2
3
4
5
6
7
8
/**
* 普通商品具体元素类
* 具体元素,实现了抽象元素接口,提供了接受访问者的具体实现。
*/
@AllArgsConstructor
public class NormalProduct implements ProductElement {
private String name;
private double price;
public String getName() {
return name;
}
public double getPrice() {
return price;
}
@Override
public void accept(ProductVisitor productVisitor) {
productVisitor.getNormalProduct(this);
}
}
/**
* 促销产品具体元素类
* 具体元素,实现了抽象元素接口,提供了接受访问者的具体实现。
*/
@AllArgsConstructor
public class PromotionProduct implements ProductElement {
private String name;
private double price;
private double discountRate;
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public double getDiscountRate() {
return discountRate;
}
@Override
public void accept(ProductVisitor productVisitor) {
productVisitor.getPromotionProduct(this);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* 产品结构类
* 对象结构,包含一组元素对象,并提供让访问者访问元素的方法。
*/
public class ProductStructure {
private List<ProductElement> elements = new ArrayList<>();
// 添加元素
public void add(ProductElement productElement) {
elements.add(productElement);
}
// 移除元素
public void remove(ProductElement productElement) {
elements.remove(productElement);
}
// 接受访问者
public void accept(ProductVisitor productVisitor) {
for (ProductElement productElement : elements) {
productElement.accept(productVisitor);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
// 1.对象结构测试
objectStructureTest();
// 2.无对象结构测试
test();
}
private static void objectStructureTest() {
// 对象结构
ProductStructure objectStructure = new ProductStructure();
objectStructure.add(new NormalProduct("普通产品", 199));
objectStructure.add(new PromotionProduct("促销产品", 99, 0.5));
// 访问者
ProductVisitor visitor = new ProductPriceCalculator();
objectStructure.accept(visitor);
}
private static void test() {
List<ProductElement> products = Arrays.asList(new NormalProduct("普通产品", 199),
new PromotionProduct("促销产品", 99, 0.5));
ProductVisitor visitor = new ProductPriceCalculator();
for (ProductElement product : products) {
product.accept(visitor);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Make sure to add code blocks to your code group