youyichannel

志于道,据于德,依于仁,游于艺!

0%

访问者模式

访问者模式

作用:操作某对象结构中各个(各层级)元素,在不改变元素整体结构的前提下,定义作用于这些新元素的新操作。

简介

针对于商品类目这类树形结构的数据来说,访问者模式可以对树型结构的任意节点进行操作,比如添加、删除等。

访问者模式UML类图:

从上图可以看出,访问者模式的类图和组合模式的类图相比,含有两个相同的角色。

=> 组合模式和访问者模式之所以能够打出精彩的配合,很大程度上因为组合模式的UML类图是访问者模式UML类图的一部分。

1)Visitor抽象访问者:接口 / 抽象类,定义访问者能够访问的数据类型,上图中的虚线箭头就说明Visitor能够访问Component数据类型。

2)ConcreteVisitor具体访问者

3)ObjectStructure数据提供者,从上图来看,Client端先通过ObjectStructure获取数据,在通过Visitor来对数据进行操作

4)Component被访问抽象对象

5)Composite被访问具体对象

实现

需求:对于商品类目进行增加和删除的操作(这部分需求是在组合模式上进行延伸的操作,所以先看下之前的文章「组合模式」吧,就在本站搜索就可以了)

1)Visitor抽象访问者:ItemVisitor

public interface ItemVisitor<T> {

T visitor(AbstractProductItem productItem);

}

2)ConcreteVisitor具体访问者:AddItemVisitorDelItemVisitor

根据需求,我们需要定义两个具体访问者:

  • 一个专门负责商品类目的新增
  • 一个专门负责商品类目的删除

AddItemVisitor

@Component
public class AddItemVisitor implements ItemVisitor<AbstractProductItem> {

/**
* Object Structure 数据提供者
*/
@Autowired
private RedisCommonProcessor redisCommonProcessor;

@Override
public AbstractProductItem visitor(AbstractProductItem productItem) {
ProductComposite currentItem = (ProductComposite) redisCommonProcessor.get(PRODUCT_CACHE_KEY);
// to be added item
ProductComposite newItem = (ProductComposite) productItem;
// 新增节点的父结点就是当前节点,直接添加
if (newItem.getPid().equals(currentItem.getId())) {
currentItem.addProductItem(newItem);
return currentItem;
}
addChild(newItem, currentItem);
return currentItem;
}

private void addChild(ProductComposite newItem, ProductComposite currentItem) {
for (AbstractProductItem itemChild : currentItem.getChildren()) {
ProductComposite child = (ProductComposite) itemChild;
if (child.getId().equals(newItem.getPid())) {
child.addProductItem(newItem);
break;
} else {
addChild(newItem, child);
}
}
}
}

DelItemVisitor

@Component
public class DelItemVisitor implements ItemVisitor<AbstractProductItem> {

/**
* Object Structure 数据提供者
*/
@Autowired
private RedisCommonProcessor redisCommonProcessor;

@Override
public AbstractProductItem visitor(AbstractProductItem productItem) {
ProductComposite currentItem = (ProductComposite) redisCommonProcessor.get(PRODUCT_CACHE_KEY);
// to be deleted item
ProductComposite delItem = (ProductComposite) productItem;
// 不能删除根节点
if(delItem.getId().equals(currentItem.getId())) {
throw new IllegalArgumentException("Cannot delete root node");
}
// 待删除节点的父结点就是当前节点,直接添加
if (delItem.getPid().equals(currentItem.getId())) {
currentItem.delProductItem(delItem);
return currentItem;
}
delChild(delItem, currentItem);
return currentItem;
}

private void delChild(ProductComposite delItem, ProductComposite currentItem) {
for (AbstractProductItem itemChild : currentItem.getChildren()) {
ProductComposite child = (ProductComposite) itemChild;
if (child.getId().equals(delItem.getPid())) {
child.delProductItem(delItem);
break;
} else {
delChild(delItem, child);
}
}
}
}

3)ObjectStructure数据提供者

在此处,我们的数据是商品类目数据,从代码中可以看出来,数据是从Redis缓存中拿的,因此RedisCommonProcessor就是数据提供者。

对于访问者模式的数据提供者,没有特殊的规则,只要能够提供数据即可,数据来源不关心,数据库也好,缓存也罢,甚至直接读写文件皆可。

最终的类图:

总结

通过组合模式,构建了商品类目树;通过访问者模式,访问商品类目树的各级节点,并提供了商品类目的新增和删除的功能。

同时要清楚的是,组合模式和访问者模式的搭配使用并不是唯一的选择,只是说在处理树形结构的数据上,二者能够打出近乎完美的配合。但是如果不需要对数据进行操作,我们完全可以单独使用组合模式;对于访问者模式,即使没有树型结构,只有单一的平面结构,依然可以使用访问者模式对平面结构的数据进行操作 => 设计模式需要因地制宜的使用