访问者模式
作用:操作某对象结构中各个(各层级)元素,在不改变元素整体结构的前提下,定义作用于这些新元素的新操作。
简介
针对于商品类目这类树形结构的数据来说,访问者模式可以对树型结构的任意节点进行操作,比如添加、删除等。
访问者模式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具体访问者:AddItemVisitor
和DelItemVisitor
根据需求,我们需要定义两个具体访问者:
- 一个专门负责商品类目的新增
- 一个专门负责商品类目的删除
AddItemVisitor
@Component public class AddItemVisitor implements ItemVisitor<AbstractProductItem> {
@Autowired private RedisCommonProcessor redisCommonProcessor;
@Override public AbstractProductItem visitor(AbstractProductItem productItem) { ProductComposite currentItem = (ProductComposite) redisCommonProcessor.get(PRODUCT_CACHE_KEY); 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> {
@Autowired private RedisCommonProcessor redisCommonProcessor;
@Override public AbstractProductItem visitor(AbstractProductItem productItem) { ProductComposite currentItem = (ProductComposite) redisCommonProcessor.get(PRODUCT_CACHE_KEY); 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
就是数据提供者。
对于访问者模式的数据提供者,没有特殊的规则,只要能够提供数据即可,数据来源不关心,数据库也好,缓存也罢,甚至直接读写文件皆可。
最终的类图:
总结
通过组合模式,构建了商品类目树;通过访问者模式,访问商品类目树的各级节点,并提供了商品类目的新增和删除的功能。
同时要清楚的是,组合模式和访问者模式的搭配使用并不是唯一的选择,只是说在处理树形结构的数据上,二者能够打出近乎完美的配合。但是如果不需要对数据进行操作,我们完全可以单独使用组合模式;对于访问者模式,即使没有树型结构,只有单一的平面结构,依然可以使用访问者模式对平面结构的数据进行操作
=> 设计模式需要因地制宜的使用