• 设计模式-行为型-访问者模式(双分派)


    描述

    • 使数据结构和元素进行分离,增加元素,不影响数据结构
    • 即,一个对象要操作多个元素,多个元素与操作者(访问者角色)分离,访问者定义具体元素角色聚合访问者。

    角色

    • 抽象访问者角色:定义访问元素的行为,参数就是访问者元素。要求元素类的个数不能改变(与具体元素角色个数一致)。
    • 具体访问者角色:给出访问元素类的具体行为。
    • 抽象元素角色:定义了一个接收访问者的方法,每个元素可被访问者访问。
    • 具体元素角色:实现抽象元素角色,提供访问元素的具体实现。
    • 对象结构角色:定义数据对象结构,可以理解为具有容器性质或者符合复合对象特效的角色,包含了一组元素,供访问者使用。

    代码

    public class Test {
        public static void main(String[] args) {
            // 把配套元素提取到一块,与操作对象去耦合
            // 吃什么
            Home home = new Home();
            home.add(new Fruit());
    
            // 让访问者去吃
            home.action(new SomeonePerson());
    
    		// 配套元素2
            Home home2 = new Home();
            home2.add(new Fruit());
            home2.add(new Vegetable());
            // 访问者去吃
            home2.action(new OtherPerson());
        }
    }
    
    // 抽象访问类角色
    interface Person {
        // 吃水果
        void eat(Fruit fruit);
        // 吃蔬菜
        void eat(Vegetable vegetable);
    }
    // 抽象元素角色,参数就是访问者对象
    interface Food {
        // 接收访问者访问
        void receive(Person person);
    }
    // 具体元素角色,访问者具体对这个元素干啥
    class Fruit implements Food {
        @Override
        public void receive(Person person) {
            System.out.println("水果维生素");
            // 实现双分派
            person.eat(this);
        }
    }
    // 具体元素角色,访问者具体对这个元素干啥
    class Vegetable implements Food {
        @Override
        public void receive(Person person) {
            System.out.println("蔬菜维生素");
            person.eat(this);
        }
    }
    // 具体抽象类对象
    class SomeonePerson implements Person {
    
        @Override
        public void eat(Fruit fruit) {
            System.out.println("某人在吃水果");
        }
    
        @Override
        public void eat(Vegetable vegetable) {
            System.out.println("某人在吃蔬菜");
        }
    }
    
    // 具体抽象类对象
    class OtherPerson implements Person {
    
        @Override
        public void eat(Fruit fruit) {
            System.out.println("其它人在吃水果");
        }
    
        @Override
        public void eat(Vegetable vegetable) {
            System.out.println("其它人在吃蔬菜");
        }
    }
    // 对象结构类
    class Home {
        // 集合对象
        private List<Food> foodList = new ArrayList<>();
        // 添加元素
        public void add(Food food) {
            foodList.add(food);
        }
        // 执行
        public void action(Person person) {
            foodList.forEach(food -> food.receive(person));
        }
    }
    
    • 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
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    优点

    • 扩展性好,可在不改变对象原有元素结构情况下,添加新元素,比如:添加一个主食元素类。
    • 可复用好,通用的配套元素可给与多个访问者,对象结构类统一action(Person person)执行元素。
    • 每个访问者的类,功能比较单一。

    缺点

    • 对象结构比较固定,每增加一个新的元素,都要在每一个抽象和具体访问者对象中添加操作方法。
    • 违背依赖倒置原则,访问者类依赖了具体类,而不是抽象类。

    使用场景

    • 对象结构基本稳定,结构中的对象操作算法不相关或经常变动的程序。

    扩展

    分派

    • 分派:

      • Map map = new HashMap();
      • map 是静态类型Map,实际类型HashMap。根据对象的类型而对方法进行的选择,就是分派。
    • 分为两种:

      • 静态分派:发送在编译时期,静态类型信息进行分派,比如方法重写(多态)。
    class Test1{
        public static void main(String[] args) {
            A a = new B();
            a.haha();
        }
    }
    class A {
        void haha() {
            System.out.println("a");
        }
    }
    class B extends A {
        void haha() {
            System.out.println("b");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 动态分派:发送在运行时期,动态地置换掉某个方法,比如方法重载。
    class Test1{
    
        public static void main(String[] args) {
            Test1 test1 = new Test1();
            A a = new A();
            A b = new B();
            A c = new C();
    
            test1.exc(a);
            test1.exc(b);
            test1.exc(c);
        }
    
        void exc(A a) {
            System.out.println("a");
        }
        void exc(B b) {
            System.out.println("b");
        }
        void exc(C c) {
            System.out.println("c");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 双分派:
      • 在单分派的基础上(消息接收者的运行时区分),还要根据参数的运行时区分。
      • 静态分派(重写)+ 动态分派(重载)
    class Test1{
        public static void main(String[] args) {
            Test1 test1 = new Test1();
            A a = new A();
            A b = new B();
            A c = new C();
    
            a.re(test1);
            b.re(test1);
            c.re(test1);
        }
        void exc(A a) {
            System.out.println("a");
        }
        void exc(B b) {
            System.out.println("b");
        }
        void exc(C c) {
            System.out.println("c");
        }
    }
    class A {
        void re(Test1 test1){
    	    // 实现双分派(重载+重写)
            test1.exc(this);
        }
    }
    class B extends A {
        void re(Test1 test1){
    		// 实现双分派(重载+重写)
            test1.exc(this);
        }
    }
    class C extends A {
        void re(Test1 test1){
            // 实现双分派(重载+重写)
            test1.exc(this);
        }
    }
    
    • 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
  • 相关阅读:
    Docker Compose部署Mysql8
    Qt操作Sqlite类封装,及命令行导入csv文件到Sqlite数据库
    JavaScript系列之JS执行机制
    NewStarCTF 2023 公开赛道 WEEK2|MISC1-序章
    漏洞复现-易思无人值守智能物流文件上传
    ROS读书记录1:机器人SLAM导航核心技术与实战1
    股票量化交易接口的功能逻辑
    消息队列-概述-JMS和AMQP
    数据挖掘——了解数据
    组合系列--有排列就有组合
  • 原文地址:https://blog.csdn.net/qq_43630812/article/details/126090840