设计模式(24)- 访问者模式
程序员文章站
2022-07-13 16:53:03
...
访问者模式
1.定义
表示一个作用于某对象结构中的个元素的操作,它可以使你在不改变个元素的类的前提下定义作用于这些元素的新操作。
2.示例代码
/** * 访问组合对象结构的访问者接口 */ public interface Visitor { /** * 访问组合对象,相当于给组合对象添加访问者的功能 * @param composite 组合对象 */ public void visitComposite(Composite composite); /** * 访问叶子对象,相当于给叶子对象添加访问者的功能 * @param leaf 叶子对象 */ public void visitLeaf(Leaf leaf); }
/** * 抽象的组件对象,相当于访问者模式中的元素对象 */ public abstract class Component { /** * 接受访问者的访问 * @param visitor 访问者对象 */ public abstract void accept(Visitor visitor); /** * 向组合对象中加入组件对象 * @param child 被加入组合对象中的组件对象 */ public void addChild(Component child) { // 缺省实现,抛出例外,叶子对象没这个功能,或子组件没有实现这个功能 throw new UnsupportedOperationException("对象不支持这个功能"); } /** * 从组合对象中移出某个组件对象 * @param child 被移出的组件对象 */ public void removeChild(Component child) { // 缺省实现,抛出例外,叶子对象没这个功能,或子组件没有实现这个功能 throw new UnsupportedOperationException("对象不支持这个功能"); } /** * 返回某个索引对应的组件对象 * @param index 需要获取的组件对象的索引,索引从0开始 * @return 索引对应的组件对象 */ public Component getChildren(int index) { throw new UnsupportedOperationException("对象不支持这个功能"); } }
/** * 组合对象,可以包含其它组合对象或者叶子对象, * 相当于访问者模式的具体Element实现对象 * public class Composite extends Component{ public void accept(Visitor visitor) { //回调访问者对象的相应方法 visitor.visitComposite(this); //循环子元素,让子元素也接受访问 for(Component c : childComponents){ //调用子对象接受访问,变相实现递归 c.accept(visitor); } } /** * 用来存储组合对象中包含的子组件对象 */ private List<Component> childComponents = new ArrayList<Component>(); /** * 组合对象的名字 */ private String name = ""; /** * 构造方法,传入组合对象的名字 * @param name 组合对象的名字 */ public Composite(String name){ this.name = name; } public void addChild(Component child) { childComponents.add(child); } public String getName() { return name; } }
/** * 叶子对象,相当于访问者模式的具体Element实现对象 */ public class Leaf extends Component{ public void accept(Visitor visitor) { //回调访问者对象的相应方法 visitor.visitLeaf(this); } /** * 叶子对象的名字 */ private String name = ""; /** * 构造方法,传入叶子对象的名字 * @param name 叶子对象的名字 */ public Leaf(String name){ this.name = name; } public String getName() { return name; } }
/** * 具体的访问者,实现:输出对象的名称,在组合对象的名称前面添加"节点:", * 在叶子对象的名称前面添加"叶子:" */ public class PrintNameVisitor implements Visitor { public void visitComposite(Composite composite) { //访问到组合对象的数据 System.out.println("节点:"+composite.getName()); } public void visitLeaf(Leaf leaf) { //访问到叶子对象的数据 System.out.println("叶子:"+leaf.getName()); } }
/** * 对象结构,通常在这里对元素对象进行遍历,让访问者能访问到所有的元素 */ public class ObjectStructure { /** * 表示对象结构,可以是一个组合结构 */ private Component root = null; /** * 提供给客户端操作的高层接口 * @param visitor 客户端需要使用的访问者 */ public void handleRequest(Visitor visitor){ //让组合对象结构中的根元素,接受访问 //在组合对象结构中已经实现了元素的遍历 if(root!=null){ root.accept(visitor); } } /** * 传入组合对象结构 * @param ele 组合对象结构 */ public void setRoot(Component ele){ this.root = ele; } }
public class Client { public static void main(String[] args) { //定义所有的组合对象 Component root = new Composite("服装"); Component c1 = new Composite("男装"); Component c2 = new Composite("女装"); //定义所有的叶子对象 Component leaf1 = new Leaf("衬衣"); Component leaf2 = new Leaf("夹克"); Component leaf3 = new Leaf("裙子"); Component leaf4 = new Leaf("套装"); //按照树的结构来组合组合对象和叶子对象 root.addChild(c1); root.addChild(c2); c1.addChild(leaf1); c1.addChild(leaf2); c2.addChild(leaf3); c2.addChild(leaf4); //创建ObjectStructure ObjectStructure os = new ObjectStructure(); os.setRoot(root); //调用ObjectStructure来处理请求功能 Visitor psVisitor = new PrintNameVisitor(); os.handleRequest(psVisitor); } }
/* *要实现这个功能,在组合对象结构中去遍历子对象的方式就比较难于实现,因为要输出这个树形结构,需要控制每个对象在输出的时候,向后的退格数量,这个需要在对象结构的循环中来控制,这种功能可以选择在访问者当中去遍历对象结构。 *来改造上面的示例,看看通过访问者来遍历元素如何实现这样的功能。 *首先在Composite的accept实现中去除掉递归调用子对象的代码,同时添加一个让访问者访问到其所包含的子对象的方法,示例代码如下: */ public class Composite extends Component{ //其它相同部分就省略了,只看变化的方法 public void accept(Visitor visitor) { //回调访问者对象的相应方法 visitor.visitComposite(this); } public List<Component> getChildComponents() { return childComponents; } } /** * 具体的访问者,实现:输出组合对象自身的结构 */ public class PrintStructVisitor implements Visitor { /** * 用来累计记录对象需要向后退的格 */ private String preStr = ""; public void visitComposite(Composite composite) { //先把自己输出去 System.out.println(preStr+"+"+composite.getName()); //如果还包含有子组件,那么就输出这些子组件对象 if(composite.getChildComponents()!=null){ //然后添加一个空格,表示向后缩进一个空格 preStr+=" "; //输出当前对象的子对象了 for(Component c : composite.getChildComponents()){ //递归输出每个子对象 c.accept(this); } //把循环子对象所多加入的一个退格给去掉 preStr = preStr.substring(0,preStr.length()-1); } } public void visitLeaf(Leaf leaf) { //访问到叶子对象的数据 System.out.println(preStr+"-"+leaf.getName()); } } /*客户端测试*/ public class Client { public static void main(String[] args) { //定义所有的组合对象过程跟上一个client是一样的,这里省略了 //调用根元素的方法来接受请求功能 Visitor psVisitor = new PrintStructVisitor(); root.accept(psVisitor); } }
3.实际应用
访问者模式能给一系列对象,透明的添加新功能。从而避免在维护期间,对这一系列对象进行修改,而且还能变相实现复用访问者所具有的功能。由于是针对一系列对象的操作,这也导致,如果只想给一系列对象中的部分对象添加功能,就会有些麻烦;而且要始终能保证把这一系列对象都要调用到,不管是循环也好,还是递归也好,总之要让每个对象都要被访问到。
- 如果想对一个对象结构,实施一些依赖于对象结构中的具体类的操作,可以使用访问者模式
- 如果想对一个对象结构中的各个元素,进行很多不同的而且不相关的操作,为了避免这些操作使得类变得杂乱,可以使用访问者模式,把这些操作分散到不同的访问者对象中去,每个访问者对象实现同一类功能
- 如果对象结构很少变动,但是需要经常给对象结构中的元素对象定义新的操作,可以使用访问者模式
访问者模式的本质:预留通路,回调实现
上一篇: Mybatis源码分析