• 设计模式-访问者模式


    完成测评系统:观众(男人和女人)对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价包括成功、失败和待定)


    1.传统方式

    创建继承Person类的Man类和Woman类,并在类中创建三种评价方法

    • 当系统增加越来越多新的功能时(比如再加一种评价),需要修改Man类和Woman类的代码,违反了OCP原则, 不利于维护
    • 扩展性不好,比如增加了新的人员类型或评价方法

    2.访问者模式

    2.1 基本介绍

    • 封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作
    • 主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题
    • 在被访问的类里面加一个对外提供接待访问者的接口
    • 对一个对象结构中的对象进行很多不同操作(操作间没有关联),需要避免让这些操作"污染"这些对象的类,可以选用访问者模式

    在这里插入图片描述

    • Visitor:抽象访问者(即Action类),为该对象结构中的ConcreteElement的每一个类声明一个visit操作
    • ConcreteVisitor :具体的访问值(即Success类和Fail类),实现每个有Visitor声明的操作,是每个操作实现的部分
    • ObjectStructure:能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
    • Element:定义一个accept方法,接收一个访问者对象(即Person类)
    • ConcreteElement:具体元素,实现了accept 方法(即Man类和Woman类)

    2.2 代码实现

    public abstract class Action {
        // 得到男性的测评
        public abstract void getManResult(Man man);
    
        // 得到女性的测评
        public abstract void getWomanResult(Woman woman);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    public class Success extends Action {
        @Override
        public void getManResult(Man man) {
            System.out.println(" 男人给的评价该歌手很成功 !");
        }
    
        @Override
        public void getWomanResult(Woman woman) {
            System.out.println(" 女人给的评价该歌手很成功 !");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    public class Fail extends Action {
        @Override
        public void getManResult(Man man) {
            System.out.println(" 男人给的评价该歌手失败 !");
        }
    
        @Override
        public void getWomanResult(Woman woman) {
            System.out.println(" 女人给的评价该歌手失败 !");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    public abstract class Person {
        // 提供一个方法,让访问者可以访问
        public abstract void accept(Action action);
    }
    
    • 1
    • 2
    • 3
    • 4
    public class Man extends Person {
        @Override
        public void accept(Action action) {
            action.getManResult(this);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    // 1. 使用到了双分派, 即首先在客户端程序中,将具体状态作为参数传递Woman中(第一次分派)
    // 2. 然后Woman类调用作为参数的"具体方法"中方法getWomanResult, 同时将自己(this)作为参数传入,完成第二次的分派
    public class Woman extends Person{
        @Override
        public void accept(Action action) {
            action.getWomanResult(this);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    // 数据结构,管理很多人(Man , Woman)
    public class ObjectStructure {
        // 维护了一个集合
        private List<Person> persons = new LinkedList<>();
    
        // 增加到list
        public void attach(Person p) {
            persons.add(p);
        }
        // 移除
        public void detach(Person p) {
            persons.remove(p);
        }
    
        // 显示测评情况
        public void display(Action action) {
            for(Person p: persons) {
                p.accept(action);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    // 新增加的评价,不需要修改Person类及其子类的代码
    public class Wait extends Action {
        @Override
        public void getManResult(Man man) {
            System.out.println(" 男人给的评价是该歌手待定 ..");
        }
    
        @Override
        public void getWomanResult(Woman woman) {
            System.out.println(" 女人给的评价是该歌手待定 ..");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    public class Client {
    
        public static void main(String[] args) {
            // 创建ObjectStructure
            ObjectStructure objectStructure = new ObjectStructure();
    
            objectStructure.attach(new Man());
            objectStructure.attach(new Woman());
    
            // 成功
            Success success = new Success();
            objectStructure.display(success);
    
            System.out.println("===============");
            Fail fail = new Fail();
            objectStructure.display(fail);
    
            System.out.println("=======给的是待定的测评========");
            Wait wait = new Wait();
            objectStructure.display(wait);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.注意事项和细节

    • 符合单一职责原则、让程序具有优秀的扩展性
    • 违背了依赖倒转原则(因为访问者依赖的是具体元素,而不是抽象元素)

    参考

    https://www.bilibili.com/video/BV1G4411c7N4?p=106-110


  • 相关阅读:
    【x265 源码分析系列】:概述
    2023年【化工自动化控制仪表】找解析及化工自动化控制仪表试题及解析
    openlayer+ol-ext 裁剪 天地图 中国或者其他省份 范围进行展示
    C语言 IO函数练习
    如何在忘记手机密码或图案时重置 Android 手机?
    激光驱动电路中的充电边沿导致激光误点亮问题总结
    万姓女孩清秀文雅的名字
    Boot 和 Cloud 的版本选型
    sentinel加密狗使用及规则配置
    什么是会话固定以及如何在 Node.js 中防止它
  • 原文地址:https://blog.csdn.net/qq_41398418/article/details/125999012