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

描述

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

角色

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

代码

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));
    }
}

优点

  • 扩展性好,可在不改变对象原有元素结构情况下,添加新元素,比如:添加一个主食元素类。
  • 可复用好,通用的配套元素可给与多个访问者,对象结构类统一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");
    }
}
  • 动态分派:发送在运行时期,动态地置换掉某个方法,比如方法重载。
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");
    }
}
  • 双分派:
    • 在单分派的基础上(消息接收者的运行时区分),还要根据参数的运行时区分。
    • 静态分派(重写)+ 动态分派(重载)
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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值