• 详解设计模式:访问者模式


    访问者模式(Visitor Pattern),是在 GoF 23 种设计模式中定义了的行为型模式。据《大话设计模式》中说算是最复杂也是最难以理解的一种模式了。

    访问者模式 是一种将数据操作与数据结构分离的设计模式,它可以算是 23 中设计模式中最复杂的一个,但它的使用频率并不是很高,大多数情况下,你并不需要使用访问者模式,但是当你一旦需要使用它时,那你就是需要使用它了。

    本篇文章内容包括:关于访问者模式、访问者模式 Demo



    一、关于访问者模式

    1、关于访问者模式

    访问者模式(Visitor Pattern),是在 GoF 23 种设计模式中定义了的行为型模式。据《大话设计模式》中说算是最复杂也是最难以理解的一种模式了。

    访问者模式 是一种将数据操作与数据结构分离的设计模式,它可以算是 23 中设计模式中最复杂的一个,但它的使用频率并不是很高,大多数情况下,你并不需要使用访问者模式,但是当你一旦需要使用它时,那你就是需要使用它了。

    访问者模式 的基本想法是,软件系统中拥有一个由许多对象构成的、比较稳定的对象结构,这些对象的类都拥有一个 accept 方法用来接受访问者对象的访问。访问者是一个接口,它拥有一个 visit 方法,这个方法对访问到的对象结构中不同类型的元素做出不同的处理。在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施 accept 方法,在每一个元素的 accept 方法中会调用访问者的 visit 方法,从而使访问者得以处理对象结构的每一个元素,我们可以针对对象结构设计不同的访问者类来完成不同的操作,达到区别对待的效果。

    访问者模式 适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式

    2、关于访问者模式的构成

    访问者模式主要包含以下 5 种角色:

    • 抽象访问者(Visitor)角色:定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类个数(Element的实现类个数)是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变。
    • 具体访问者(ConcreteVisitor)角色: 具体访问者实现了每个由抽象访问者声明的操作,每一个操作用于访问对象结构中一种类型的元素。
    • 抽象元素(Element)角色:定义了一个接受访问者的方法(accept),其意义是指,每一个元素都要可以被访问者访问。
    • 具体元素(ConcreteElement)角色: 提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
    • 对象结构(ObjectStructure)角色: 对象结构是一个元素的集合,它用于存放元素对象,并且提供了遍历其内部元素的方法。它可以结合组合模式来实现,也可以是一个简单的集合对象,如一个List对象或一个Set对象。
    3、关于访问者模式的UML

    image-20221204222000578

    4、关于访问者模式的适用场景
    • 对象结构相对稳定,但其操作算法经常变化的程序。
    • 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
    5、关于访问者模式的优缺点

    # 访问者模式的优点

    • 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
    • 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
    • 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
    • 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。

    # 访问者模式的缺点

    • 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
    • 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
    • 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。

    二、访问者模式 Demo

    1、Demo 设计

    艺术公司利用“铜”可以设计出铜像,利用“纸”可以画出图画;造币公司利用“铜”可以印出铜币,利用“纸”可以印出纸币。对“铜”和“纸”这两种元素,两个公司的处理方法不同,所以该实例用访问者模式来实现比较适合。

    image-20221204214648770

    • 首先,定义一个公司(Company)接口,它是抽象访问者,提供了两个根据纸(Paper)或铜(Cuprum)这两种元素创建作品的方法;再定义艺术公司(ArtCompany)类和造币公司(Mint)类,它们是具体访问者,实现了父接口的方法。
    • 然后,定义一个材料(Material)接口,它是抽象元素,提供了 accept(Company visitor)方法来接受访问者(Company)对象访问;再定义纸(Paper)类和铜(Cuprum)类,它们是具体元素类,实现了父接口中的方法。
    • 最后,定义一个材料集(SetMaterial)类,它是对象结构角色,拥有保存所有元素的容器 List,并提供让访问者对象遍历容器中的所有元素的 accept(Company visitor)方法。
    2、Demo 实现

    # Company 抽象访问者(Visitor)角色

    public interface Company {
        
        /**
         * 操作Page元素
         *
         * @param element
         * @return
         */
        String create(Paper element);
    
        /**
         * 操作Cuprum元素
         *
         * @param element
         * @return
         */
        String create(Cuprum element);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    # ArtCompany/MintCompany 具体访问者(ConcreteVisitor)角色

    public class ArtCompany implements Company {
    
        @Override
        public String create(Paper element) {
            // 艺术公司利用Paper元素操作
            return "打印广告";
        }
    
        @Override
        public String create(Cuprum element) {
            // 艺术公司利用Cuprum元素制造铜像
            return "孔子铜像";
        }
    }
    
    public class MintCompany implements Company {
        
        @Override
        public String create(Paper element) {
            // 造币公司利用Paper元素造纸币
            return "纸币";
        }
    
        @Override
        public String create(Cuprum element) {
            // 造币公司利用Cuprum元素造铜币
            return "铜币";
        }
    }
    
    • 1
    • 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

    # Material 抽象元素(Element)角色

    public interface Material {
        
        /**
         * 给指定访问者提供访问当前元素(就是this)的方法
         *
         * @param visitor 指定的访问者
         * @return 访问当前元素返回的结果
         */
        String accept(Company visitor);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    # Paper/Cuprum 具体元素(ConcreteElement)角色

    public class Paper implements Material {
    
        @Override
        public String accept(Company visitor) {
            // 让指定访问者visitor访问当前Paper元素
            return visitor.create(this);
        }
    }
    public class Cuprum implements Material {
    
        @Override
        public String accept(Company visitor) {
            // 让指定访问者visitor访问当前Cuprum元素
            return visitor.create(this);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    # MaterialSet 对象结构(ObjectStructure)角色

    public class MaterialSet {
        
        /**
         * 存储材料元素的集合
         */
        private List<Material> list = new ArrayList<>();
    
        /**
         * 让指定访问者访问list集合中的所有元素
         *
         * @param visitor 指定的访问者
         * @return 批量访问的结果
         */
        public String accept(Company visitor) {
            // 获取集合的迭代器
            Iterator<Material> iterator = list.iterator();
            // 遍历集合,让集合中的所有材料元素都被当前访问者所访问
            String result = "";
            while (iterator.hasNext()) {
                result += iterator.next().accept(visitor) + " ";
            }
            // 返回某公司的作品集
            return result;
        }
    
        /**
         * 添加元素到材料集合中
         *
         * @param element 待添加的元素
         */
        public void add(Material element) {
            list.add(element);
        }
    
        /**
         * 删除集合中的指定元素
         *
         * @param element 待删除的元素
         */
        public void remove(Material element) {
            list.remove(element);
        }
    }
    
    • 1
    • 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
    3、Demo 测试
    public class Client {
    
        public static void main(String[] args) {
            // 创建材料元素集合
            MaterialSet ms = new MaterialSet();
            // 向集合中添加元素
            ms.add(new Paper());
            // 向集合中添加元素
            ms.add(new Cuprum());
    
            // 创建具体的访问者,让该访问者来访问对象结构中的所有元素
            Company artCompany = new ArtCompany();
            System.out.println(ms.accept(artCompany));
    
            System.out.println("==========================");
    
            // 创建具体的访问者,让该访问者来访问对象结构中的所有元素
            Company mintCompany = new MintCompany();
            System.out.println(ms.accept(mintCompany));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    ROS 机器人操作系统:概述
    安卓APP(1)——安卓工程构建和第一APP运行
    面试官直言:有没有 Framework 的项目经验?
    c语言数据结构 排序(一)
    【机器人算法】机器人运动学参数辨识/DH参数校准/DH参数辨识
    企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图
    Bean的生命周期及演示
    centos7系统安装Boost、Cpp-httplib、jsoncpp等库
    CPU cache:组织及一致性
    【HMS core】【Analytics Kit】华为分析服务常见问题FAQ 2
  • 原文地址:https://blog.csdn.net/weixin_45187434/article/details/128197861