设计模式之访问者模式

访问者模式

  • 定义:

    ​ 提供一个作用于某种对象结构上的各元素的操作方式,可以使我们在不改变元素结构的前提下,定义作用于元素的新操作。如果系统的数据结构是比较稳定的,但其操作(算法)是易于变化的,那么使用访问者模式是个不错的选择;如果数据结构是易于变化的,则不适合使用访问者模式。

  • 使用场景:

    1. 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖与其具体类的操作,也就是用迭代器模式已经不能胜任的情景。
    2. 需要对一个对结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象。
  • UML:

    1. Visitor:接口或者抽象类,定义了对每个 Element 访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。
    2. ConcreteVisitor:具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为。
    3. Element:元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
    4. ElementA、ElementB:具体的元素类,它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
    5. ObjectStructure:定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素提供访问者访问。

在这里插入图片描述

  • 优点:

    1. 各角色职责分离,符合单一职责原则。
    2. 具有优秀的扩展性。
    3. 使得数据结构和作用于结构上的操作解耦合,使得操作集合可以独立的变化。
    4. 提高了系统的灵活性。
  • 缺点:

    1. 具体元素对访问者公开细节,违反了迪米特法则。
    2. 具体元素变更时修改成本大。
    3. 违反了依赖倒置原则,为了达到区别对待而依赖了具体类,没有依赖抽象。
  • 样例:

    // 员工基类
    public abstract class Staff {
    
        public String name;
        public int kpi;// 员工KPI
    
        public Staff(String name) {
            this.name = name;
            kpi = new Random().nextInt(10);
        }
        // 核心方法,接受Visitor的访问
        public abstract void accept(Visitor visitor);
    }
    
    public class Engineer extends Staff {
    
        public Engineer(String name) {
            super(name);
        }
    
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
        // 工程师一年的代码数量
        public int getCodeLines() {
            return new Random().nextInt(10 * 10000);
        }
    }
    
    // 经理
    public class Manager extends Staff {
    
        public Manager(String name) {
            super(name);
        }
    
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
        // 一年做的产品数量
        public int getProducts() {
            return new Random().nextInt(10);
        }
    }
    
    // 员工业务报表类
    public class BusinessReport {
    
        private List<Staff> mStaffs = new LinkedList<>();
    
        public BusinessReport() {
            mStaffs.add(new Manager("经理-A"));
            mStaffs.add(new Engineer("工程师-A"));
            mStaffs.add(new Engineer("工程师-B"));
            mStaffs.add(new Engineer("工程师-C"));
            mStaffs.add(new Manager("经理-B"));
            mStaffs.add(new Engineer("工程师-D"));
        }
    
        /**
         * 为访问者展示报表
         * @param visitor 公司高层,如CEO、CTO
         */
        public void showReport(Visitor visitor) {
            for (Staff staff : mStaffs) {
                staff.accept(visitor);
            }
        }
    }
    
    public interface Visitor {
    
        // 访问工程师类型
        void visit(Engineer engineer);
    
        // 访问经理类型
        void visit(Manager manager);
    }
    
    // CEO访问者
    public class CEOVisitor implements Visitor {
        @Override
        public void visit(Engineer engineer) {
            System.out.println("工程师: " + engineer.name + ", KPI: " + engineer.kpi);
        }
    
        @Override
        public void visit(Manager manager) {
            System.out.println("经理: " + manager.name + ", KPI: " + manager.kpi +
                    ", 新产品数量: " + manager.getProducts());
        }
    }
    
    public class ReportUtil {
        public void visit(Staff staff) {
            if (staff instanceof Manager) {
                Manager manager = (Manager) staff;
                System.out.println("经理: " + manager.name + ", KPI: " + manager.kpi +
                        ", 新产品数量: " + manager.getProducts());
            } else if (staff instanceof Engineer) {
                Engineer engineer = (Engineer) staff;
                System.out.println("工程师: " + engineer.name + ", KPI: " + engineer.kpi);
            }
        }
    }
    
    public class CTOVisitor implements Visitor {
        @Override
        public void visit(Engineer engineer) {
            System.out.println("工程师: " + engineer.name + ", 代码行数: " + engineer.getCodeLines());
        }
    
        @Override
        public void visit(Manager manager) {
            System.out.println("经理: " + manager.name + ", 产品数量: " + manager.getProducts());
        }
    }
    
    public class Client {
    
        public static void main(String[] args) {
            // 构建报表
            BusinessReport report = new BusinessReport();
            System.out.println("=========== CEO看报表 ===========");
            report.showReport(new CEOVisitor());
            System.out.println("=========== CTO看报表 ===========");
            report.showReport(new CTOVisitor());
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值