• 设计模式——中介者模式、迭代器模式、访问者模式(双分派)(行为型模式)


    1.中介者模式

    中介者模式包含以下主要角色:

    • 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。

    • 具体中介者(ConcreteMediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。

    • 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。

    • 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

    抽象中介者:

    1. //抽象中介者类
    2. public abstract class Mediator {
    3. //沟通的方法
    4. public abstract void constact(String message, Person person);
    5. }

    具体中介者:

    1. //具体的中介者角色类
    2. public class MediatorStructure extends Mediator {
    3. //聚合房主和租房者对象
    4. private HouseOwner houseOwner;
    5. private Tenant tenant;
    6. public HouseOwner getHouseOwner() {
    7. return houseOwner;
    8. }
    9. public void setHouseOwner(HouseOwner houseOwner) {
    10. this.houseOwner = houseOwner;
    11. }
    12. public Tenant getTenant() {
    13. return tenant;
    14. }
    15. public void setTenant(Tenant tenant) {
    16. this.tenant = tenant;
    17. }
    18. @Override
    19. public void constact(String message, Person person) {
    20. if(person == houseOwner) {
    21. tenant.getMessage(message);
    22. } else {
    23. houseOwner.getMessage(message);
    24. }
    25. }
    26. }

    抽象同事类:

    1. //抽象同事类
    2. public abstract class Person {
    3. protected String name;
    4. protected Mediator mediator;
    5. public Person(String name, Mediator mediator) {
    6. this.name = name;
    7. this.mediator = mediator;
    8. }
    9. //和中介联系(沟通)
    10. public void constact(String message) {
    11. mediator.constact(message, this);
    12. }
    13. }

    具体同事类:

    1. //租房者:具体的同事角色类
    2. public class Tenant extends Person {
    3. public Tenant(String name, Mediator mediator) {
    4. super(name, mediator);
    5. }
    6. //获取信息
    7. public void getMessage(String message) {
    8. System.out.println("租房者" + name + "获取到的信息是:" + message);
    9. }
    10. }
    11. //房主类: 具体的同事角色类
    12. public class HouseOwner extends Person {
    13. public HouseOwner(String name, Mediator mediator) {
    14. super(name, mediator);
    15. }
    16. //获取信息
    17. public void getMessage(String message) {
    18. System.out.println("房主" + name + "获取到的信息是:" + message);
    19. }
    20. }

    测试类:

    1. public class Client {
    2. public static void main(String[] args) {
    3. //创建中介者对象
    4. MediatorStructure mediator = new MediatorStructure();
    5. //创建租房者对象
    6. Tenant tenant = new Tenant("李四", mediator);
    7. //创建房主对象
    8. HouseOwner houseOwner = new HouseOwner("张三", mediator);
    9. //中介者要知道具体的房主和租房者
    10. mediator.setTenant(tenant);
    11. mediator.setHouseOwner(houseOwner);
    12. tenant.constact("我要租三室的房子!!!");
    13. houseOwner.constact("我这里有三室的房子,你要租吗?");
    14. //房主张三获取到的信息是:我要租三室的房子!!!
    15. //租房者李四获取到的信息是:我这里有三室的房子,你要租吗?
    16. }
    17. }

    2.迭代器模式

    迭代器模式主要包含以下角色:

    • 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。

    • 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。

    • 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法。

    • 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

    存储的实体类:

    1. public class Student {
    2. private String name;
    3. private String number;
    4. public Student() {
    5. }
    6. public Student(String name, String number) {
    7. this.name = name;
    8. this.number = number;
    9. }
    10. public String getName() {
    11. return name;
    12. }
    13. public void setName(String name) {
    14. this.name = name;
    15. }
    16. public String getNumber() {
    17. return number;
    18. }
    19. public void setNumber(String number) {
    20. this.number = number;
    21. }
    22. @Override
    23. public String toString() {
    24. return "Student{" +
    25. "name='" + name + '\'' +
    26. ", number='" + number + '\'' +
    27. '}';
    28. }
    29. }

    抽象聚合:

    1. //抽象聚合角色接口
    2. public interface StudentAggregate {
    3. //添加学生功能
    4. void addStudent(Student stu);
    5. //删除学生功能
    6. void removeStudent(Student stu);
    7. //获取迭代器对象功能
    8. StudentIterator getStudentIterator();
    9. }

    具体聚合:

    1. import java.util.ArrayList;
    2. import java.util.List;
    3. //具体聚合角色类
    4. public class StudentAggregateImpl implements StudentAggregate {
    5. private List list = new ArrayList<>();
    6. @Override
    7. public void addStudent(Student stu) {
    8. list.add(stu);
    9. }
    10. @Override
    11. public void removeStudent(Student stu) {
    12. list.remove(stu);
    13. }
    14. @Override
    15. //获取迭代器对象
    16. public StudentIterator getStudentIterator() {
    17. return new StudentIteratorImpl(list);
    18. }
    19. }

    抽象迭代器:

    1. //抽象迭代器角色接口
    2. public interface StudentIterator {
    3. //判断是否还有元素
    4. boolean hasNext();
    5. //获取下一个元素
    6. Student next();
    7. }

    具体迭代器:

    1. import java.util.List;
    2. //具体迭代器角色类
    3. public class StudentIteratorImpl implements StudentIterator {
    4. private List list;
    5. private int position = 0;//用来记录遍历时的位置
    6. public StudentIteratorImpl(List list) {
    7. this.list = list;
    8. }
    9. @Override
    10. public boolean hasNext() {
    11. return position < list.size();
    12. }
    13. @Override
    14. public Student next() {
    15. //从集合中获取指定位置的元素
    16. Student currentStudent = list.get(position);
    17. position++;
    18. return currentStudent;
    19. }
    20. }

    Java源码中的集合迭代器

    1. public class IteratorDemo {
    2. public static void main(String[] args) {
    3. ArrayList arr = new ArrayList<>();
    4. for (int i = 0; i < 20; i++) {
    5. arr.add(i);
    6. }
    7. Iterator iterator = arr.iterator();
    8. while (iterator.hasNext()) {
    9. Integer next = iterator.next();
    10. System.out.println(next);
    11. }
    12. }
    13. }
    1. public class ArrayList{
    2. public Iterator iterator() {
    3. return new Itr();
    4. }
    5. private class Itr implements Iterator {
    6. int cursor; // index of next element to return
    7. int lastRet = -1; // index of last element returned; -1 if no such
    8. int expectedModCount = modCount;
    9. public boolean hasNext() {
    10. return cursor != size;
    11. }
    12. @SuppressWarnings("unchecked")
    13. public E next() {
    14. checkForComodification();
    15. int i = cursor;
    16. if (i >= size)
    17. throw new NoSuchElementException();
    18. Object[] elementData = ArrayList.this.elementData;
    19. if (i >= elementData.length)
    20. throw new ConcurrentModificationException();
    21. cursor = i + 1;
    22. return (E) elementData[lastRet = i];
    23. }
    24. }
    25. public interface Iterator {
    26. boolean hasNext();
    27. E next();
    28. default void remove() {
    29. throw new UnsupportedOperationException("remove");
    30. }
    31. default void forEachRemaining(Consumersuper E> action) {
    32. Objects.requireNonNull(action);
    33. while (hasNext())
    34. action.accept(next());
    35. }
    36. }

    3.访问者模式

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

    • 抽象访问者(Visitor)角色:定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类个数(Element的实现类个数)是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变。

    • 具体访问者(ConcreteVisitor)角色:给出对每一个元素类访问时所产生的具体行为。

    • 抽象元素(Element)角色:定义了一个接受访问者的方法(accept),其意义是指,每一个元素都要可以被访问者访问。

    • 具体元素(ConcreteElement)角色: 提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。

    • 对象结构(Object Structure)角色:定义当中所提到的对象结构,对象结构是一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素(Element),并且可以迭代这些元素,供访问者访问。

    抽象访问者:

    1. //喂食的人:抽象访问者角色类
    2. public interface Person {
    3. //喂食宠物狗
    4. void feed(Cat cat);
    5. //喂食宠物猫
    6. void feed(Dog dog);
    7. }

    具体访问者:

    1. //主人:体访问者角色类
    2. public class Owner implements Person {
    3. @Override
    4. public void feed(Cat cat) {
    5. System.out.println("主人喂食猫");
    6. }
    7. @Override
    8. public void feed(Dog dog) {
    9. System.out.println("主人喂食狗");
    10. }
    11. }
    12. //其他人:具体访问者角色类
    13. public class Someone implements Person {
    14. @Override
    15. public void feed(Cat cat) {
    16. System.out.println("其他人喂食猫");
    17. }
    18. @Override
    19. public void feed(Dog dog) {
    20. System.out.println("其他人喂食狗");
    21. }
    22. }

    抽象元素:

    1. //宠物:抽象元素角色类
    2. public interface Animal {
    3. //接受访问者访问的功能
    4. void accept(Person person);
    5. }

    具体元素:

    1. //宠物猫:具体元素角色类
    2. public class Cat implements Animal {
    3. @Override
    4. public void accept(Person person) {
    5. person.feed(this); //访问者给宠物猫喂食
    6. System.out.println("好好吃,喵喵喵。。。");
    7. }
    8. }
    9. //宠物狗:具体元素角色类
    10. public class Dog implements Animal {
    11. @Override
    12. public void accept(Person person) {
    13. person.feed(this); //访问者给宠物猫喂食
    14. System.out.println("好好吃,汪汪汪。。。");
    15. }
    16. }

    对象结构:

    1. //对象结构类
    2. public class Home {
    3. //声明一个集合对象,用来存储元素对象
    4. private List nodeList = new ArrayList<>();
    5. //添加元素功能
    6. public void add(Animal animal) {
    7. nodeList.add(animal);
    8. }
    9. public void action(Person person) {
    10. //遍历集合,获取每一个元素,让访问者访问每一个元素
    11. for (Animal animal : nodeList) {
    12. animal.accept(person);
    13. }
    14. }
    15. }

    测试类 :

    1. public class Client {
    2. public static void main(String[] args) {
    3. //创建Home对象
    4. Home home = new Home();
    5. //添加元素到Home对象中
    6. home.add(new Dog());
    7. home.add(new Cat());
    8. //创建主人对象
    9. Owner owner = new Owner();
    10. //让主人喂食所有的宠物
    11. home.action(owner);
    12. }
    13. }

    优缺点

    1,优点:

    • 扩展性好

      在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。

    • 复用性好

      通过访问者来定义整个对象结构通用的功能,从而提高复用程度。

    • 分离无关行为

      通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。

    2,缺点:

    • 对象结构变化很困难

      在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。

    • 违反了依赖倒置原则

      访问者模式依赖了具体类,而没有依赖抽象类。

    使用场景

    • 对象结构相对稳定,但其操作算法经常变化的程序。

    • 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。

    双分派技术

    访问者模式用到了一种双分派的技术。

    分派

    变量被声明时的类型叫做变量的静态类型,有些人又把静态类型叫做明显类型;而变量所引用的对象的真实类型又叫做变量的实际类型。比如 Map map = new HashMap() ,map变量的静态类型是 Map ,实际类型是 HashMap 。根据对象的类型而对方法进行的选择,就是分派(Dispatch),分派(Dispatch)又分为两种,即静态分派和动态分派。

    静态分派(Static Dispatch)

    发生在编译时期,分派根据静态类型信息发生。静态分派对于我们来说并不陌生,方法重载就是静态分派。

    !!!重载方法的分派是根据静态类型进行的,这个分派过程在编译时期就完成了!!!

    1. /**
    2. * 静态分派
    3. * 发生在编译时期,分派根据静态类型信息发生。
    4. * 方法重载就是静态分派。
    5. *
    6. * !!!重载方法的分派是根据静态类型进行的,这个分派过程在编译时期就完成了
    7. */
    8. public class demo2 {
    9. public static void main(String[] args) {
    10. Animal a = new Animal();
    11. Animal a1 = new Dog();
    12. Animal a2 = new Cat();
    13. Execute exe = new Execute();
    14. exe.execute(a);//Animal
    15. exe.execute(a1);//Animal
    16. exe.execute(a2);//Animal
    17. }
    18. }
    19. class Animal {
    20. }
    21. class Dog extends Animal {
    22. }
    23. class Cat extends Animal {
    24. }
    25. class Execute {
    26. public void execute(Animal a) {
    27. System.out.println("Animal");
    28. }
    29. public void execute(Dog d) {
    30. System.out.println("dog");
    31. }
    32. public void execute(Cat c) {
    33. System.out.println("cat");
    34. }
    35. }

    动态分派(Dynamic Dispatch)

    发生在运行时期,动态分派动态地置换掉某个方法。Java通过方法的重写支持动态分派。

    1. /**
    2. * 动态分派
    3. * 发生在运行时期,动态分派动态地置换掉某个方法
    4. * Java通过方法的重写支持动态分派。
    5. */
    6. public class demo1 {
    7. public static void main(String[] args) {
    8. Animal a = new Dog();
    9. a.execute();//dog
    10. Animal a1 = new Cat();
    11. a1.execute();//cat
    12. }
    13. }
    14. class Animal {
    15. public void execute() {
    16. System.out.println("Animal");
    17. }
    18. }
    19. class Dog extends Animal {
    20. @Override
    21. public void execute() {
    22. System.out.println("dog");
    23. }
    24. }
    25. class Cat extends Animal {
    26. @Override
    27. public void execute() {
    28. System.out.println("cat");
    29. }
    30. }

    双分派

     * 第一次分派:将Execute对象做为参数传递给Animal类型的变量调用的方法。这里是方法重写,所以是动态分派
     * 第二次分派:将自己this(也就是实际类型)作为参数传递进去,这里的Execute类中有多个重载的方法,所以是静态分派

    1. /**
    2. * 双分派
    3. * 第一次分派:将Execute对象做为参数传递给Animal类型的变量调用的方法。这里是方法重写,所以是动态分派
    4. * 第二次分派:将自己this(也就是实际类型)作为参数传递进去,这里的Execute类中有多个重载的方法,所以是静态分派
    5. */
    6. class demo3 {
    7. public static void main(String[] args) {
    8. Animal a = new Animal();
    9. Animal d = new Dog();
    10. Animal c = new Cat();
    11. Execute exe = new Execute();
    12. a.accept(exe);//animal
    13. d.accept(exe);//dog
    14. c.accept(exe);//cat
    15. }
    16. }
    17. class Animal {
    18. public void accept(Execute exe) {
    19. exe.execute(this);
    20. }
    21. }
    22. class Dog extends Animal {
    23. public void accept(Execute exe) {
    24. exe.execute(this);
    25. }
    26. }
    27. class Cat extends Animal {
    28. public void accept(Execute exe) {
    29. exe.execute(this);
    30. }
    31. }
    32. class Execute {
    33. public void execute(Animal a) {
    34. System.out.println("animal");
    35. }
    36. public void execute(Dog d) {
    37. System.out.println("dog");
    38. }
    39. public void execute(Cat c) {
    40. System.out.println("cat");
    41. }
    42. }

  • 相关阅读:
    无法信任 ASP.NET Core 中的本地 HTTPS 证书
    基于单片机设计的气压与海拔高度检测计(采用MPL3115A2芯片实现)
    springboot快速入门一篇文章全
    bugku-web-文件上传
    依赖配置与依赖传递
    标记二肽Dansyl-Ala-Arg、87687-46-5
    WebSocket 详细教程
    FreeRTOS自我救赎3之USB虚拟串口
    git:多分支管理
    【云原生之k8s】K8s 管理工具 kubectl 详解(二)
  • 原文地址:https://blog.csdn.net/m0_63544124/article/details/126047511