• 设计模式之观察者模式、访问者模式与模板方法模式


    目录

    观察者模式

    简介

    优缺点

    结构

    实现

    运用场景

    访问者模式

    简介

    优缺点

    结构

    实现

    运用场景

    模板方法模式

    简介

    优缺点

    结构

    实现

    运用场景


    观察者模式

    简介

            又叫发布-订阅模式,定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知,并且自动更新

    优缺点

    优点:

            1.主题与观察者之间松耦合

            2.支持广播

    缺点:

            1.目标与观察者之间的依赖关系并没有完全解除,而且可能出现循环引用

            2.当观察者多时,通知花费很多时间,影响效率

            3.采用顺序通知,如果某一观察者卡住,其他将无法接收消息

    结构

    角色:

            抽象主题角色:被观察的对象,定义了一个观察者集合

            具体主题角色:具体目标类,实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象

            抽象观察者角色:一个抽象类或接口,它包含了一个更新自己的方法,当接到更改通知时被调用

            具体观察者角色:实现抽象观察者中定义的抽象方法,以便在得到目标更改通知时被调用

    实现

    1.被观察者

    1. public interface Subject{
    2. //被观察者创建3个方法:添加监听者、删除监听者、通知监听者
    3. public void register(Observer observer);
    4. public void remove(Observer observer);
    5. public void notifyAll();
    6. public abstract void doSomething();
    7. }

    2.具体观察者

    1. public class ConcreteSubject implements Subject {
    2. private List observers = new ArrayList<>();
    3. @Override
    4. public void register(Observer observer){
    5. observers.add(observer);
    6. }
    7. @Override
    8. public void remove(Observer observer){
    9. observers.remove(observer);
    10. }
    11. @Override
    12. public void notifyAllObserver(){
    13. for (Observer observer : observers){
    14. observer.update();
    15. }
    16. }
    17. @Override
    18. public void doSomething(){
    19. this.notifyAllObserver();
    20. }
    21. }

    3.观察者

    1. public interface Observer{
    2. public void update();
    3. }

    4.具体观察者

    1. public class Concrete2 implements Observer{
    2. @Override
    3. public void update(){
    4. System.out.println("收到消息!");
    5. }
    6. }

    运用场景

            1.对一个对象状态的更新需要同步给其他对象,而且其他对象的数量是动态可变的

            2.系统存在事件多级触发时

            3.对象仅需通知自己更新的通知,而不需要知道其他对象的细节

    访问者模式

    简介

            在不改变聚合对象内元素的前提下,为聚合对象内每个元素提供多种访问方式,即聚合对象内的每个元素都有多个访问者对象

    优缺点

    优点:

            符合单一职责原则,即数据的存储和操作分别由对象结构类和访问者类实现

            优秀的扩展性和灵活性

    缺点:

            具体元素对访问者公布了其细节

            具体元素的增加将导致访问者类的修改

            访问者类依赖了具体类而不是抽象

    结构

            抽象元素:定义接受访问者的行为。
            具体元素:实现抽象访问者,实现其定义的接受访问者的行为,并将访问行为委托给接受的访问者对象。
            抽象访问者:定义访问不同元素的行为。即当系统中有多个不同类型的元素时,抽象访问者则需要定义多个访问行为。
            具体访问者:实现抽象访问者,实现其定义的多个访问不同元素的行为。
            对象结构:即聚合对象,持有一个抽象元素的聚合引用,并提供添加元素、获取元素、移除元素、访问元素的方法

    实现

    1. 抽象元素:
    2. public interface Element {
    3. /**
    4. * 接受访问者
    5. * @param visitor
    6. */
    7. void accept(Visitor visitor);
    8. }
    9. //具体元素一
    10. public class OneElement implements Element {
    11. @Override
    12. public void accept(Visitor visitor) {
    13. visitor.visitor(this);
    14. }
    15. }
    16. //具体元素二
    17. public class TwoElement implements Element {
    18. @Override
    19. public void accept(Visitor visitor) {
    20. visitor.visitor(this);
    21. }
    22. }
    23. 抽象访问者:
    24. public interface Visitor {
    25. /**
    26. * 访问元素一
    27. * @param oneElement
    28. */
    29. void visitor(OneElement oneElement);
    30. /**
    31. * 访问元素二
    32. * @param twoElement
    33. */
    34. void visitor(TwoElement twoElement);
    35. }
    36. 具体访问者一:
    37. public class OneVisitor implements Visitor {
    38. @Override
    39. public void visitor(OneElement oneElement) {
    40. System.out.println("访问者一访问元素一 " + oneElement);
    41. }
    42. @Override
    43. public void visitor(TwoElement twoElement) {
    44. System.out.println("访问者一访问元素二 " + twoElement);
    45. }
    46. }
    47. 具体访问者二:
    48. public class TwoVisitor implements Visitor {
    49. @Override
    50. public void visitor(OneElement oneElement) {
    51. System.out.println("访问者二访问元素一 " + oneElement);
    52. }
    53. @Override
    54. public void visitor(TwoElement twoElement) {
    55. System.out.println("访问者二访问元素二 " + twoElement);
    56. }
    57. }
    58. 对象结构:
    59. public class ObjectStructure {
    60. private List elements;
    61. public ObjectStructure() {
    62. this.elements = new ArrayList<>();
    63. }
    64. public void add(Element element) {
    65. this.elements.add(element);
    66. }
    67. public Element get(Integer index) {
    68. return this.elements.get(index);
    69. }
    70. public void remove(Element element) {
    71. this.elements.remove(element);
    72. }
    73. /**
    74. * 访问对象内元素
    75. * @param visitor
    76. */
    77. public void accept(Visitor visitor) {
    78. for (Element element : this.elements) {
    79. element.accept(visitor);
    80. }
    81. }
    82. }
    83. 测试:
    84. public class VisitorTest {
    85. public static void main(String[] args) {
    86. ObjectStructure objectStructure = new ObjectStructure();
    87. objectStructure.add(new OneElement());
    88. objectStructure.add(new OneElement());
    89. objectStructure.add(new TwoElement());
    90. objectStructure.add(new TwoElement());
    91. objectStructure.accept(new OneVisitor());
    92. System.out.println();
    93. objectStructure.accept(new TwoVisitor());
    94. }
    95. }

    运用场景

            当对象结构相对稳定,但其操作算法经常变化时

            当需要为对象结构中的元素提供多个不同且不想管的操作,且要避免让这些操作的变化影响对象的结构时

    模板方法模式

    简介

            定义一个模版结构即抽象,将具体内容延迟到子类去实现

    优缺点

    优点:

            提高代码复用性

            提高了拓展性

            实现了反向控制

    缺点:

            引入了抽象类,每一个不同的实现都需要一个子类来实现,导致类的个数增加,从而增加了系统实现的复杂度

    结构

    角色:

            抽象类角色:定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤

            具体子类角色:实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤

    实现

    1.抽象类

    1. package com.charon.template;
    2. /**
    3. * @className: Account
    4. * @description:
    5. * @author: charon
    6. * @create: 2022-03-26 14:57
    7. */
    8. public abstract class Account {
    9. private String accountNum;
    10. /**
    11. * 无参构造,帮助子类初始化
    12. * 如果没有显示声明父类的无参的构造方法,系统会自动默认生成一个无参构造方法。
    13. * 但是,如果声明了一个有参的构造方法,而没有声明无参的构造方法,这时系统不会动默认生成一个无参构造方法。
    14. */
    15. public Account() {
    16. }
    17. public Account(String accountNum) {
    18. this.accountNum = accountNum;
    19. }
    20. /**
    21. * 模板方法,计算利息的数额
    22. * 使用final修饰,可以防止子类重写模板方法
    23. * @return
    24. */
    25. public final double calcInterest(){
    26. double interestRate = calcInterestRate();
    27. String accountType = calcAccountType();
    28. double amount = getAmount(accountType,accountNum);
    29. return interestRate * amount;
    30. }
    31. /**
    32. * 获取账户金额
    33. * @param accountType 账户类型
    34. * @param accountNum 账号
    35. * @return
    36. */
    37. protected double getAmount(String accountType, String accountNum){
    38. // 从数据库中获取该账户的金额,这里直接返回一个
    39. return 5000;
    40. }
    41. /**
    42. * 计算账户类型
    43. */
    44. protected abstract String calcAccountType();
    45. /**
    46. * 计算利息利率
    47. * @return
    48. */
    49. protected abstract double calcInterestRate();
    50. }

    2.子类

    1. package com.charon.template;
    2. /**
    3. * @className: MoneyMarketAccount
    4. * @description:
    5. * @author: charon
    6. * @create: 2022-03-26 15:12
    7. */
    8. public class MoneyMarketAccount extends Account{
    9. @Override
    10. protected String calcAccountType() {
    11. return "Money Market";
    12. }
    13. @Override
    14. protected double calcInterestRate() {
    15. return 0.045;
    16. }
    17. }
    18. package com.charon.template;
    19. /**
    20. * @className: CDAccount
    21. * @description:
    22. * @author: charon
    23. * @create: 2022-03-26 15:22
    24. */
    25. public class CDAccount extends Account {
    26. @Override
    27. protected String calcAccountType() {
    28. return "CD";
    29. }
    30. @Override
    31. protected double calcInterestRate() {
    32. return 0.065;
    33. }
    34. }

    3.调用

    1. package com.charon.template;
    2. /**
    3. * @className: Client
    4. * @description:
    5. * @author: charon
    6. * @create: 2022-03-25 23:44
    7. */
    8. public class Client {
    9. public static void main(String[] args) {
    10. Account mmAccount = new MoneyMarketAccount();
    11. System.out.println("货币市场账号的利息是:" + mmAccount.calcInterest());
    12. Account cdAccount = new CDAccount();
    13. System.out.println("定期账号的利息是:" + cdAccount.calcInterest());
    14. }
    15. }
    16. 打印:
    17. 货币市场账号的利息是:225.0
    18. 定期账号的利息是:325.0

    运用场景

            一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现;

            各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复;

            控制子类的扩展

  • 相关阅读:
    【21天学习挑战赛】Python学习第四篇:多线程 threading 模块
    大学网课查题公众号零基础注册使用(内含详细教程和接口)
    【微信小程序毕业设计源代码】最近开发的60个java+python微信小程序源码+毕业设计选题推荐
    通过Vue自带服务器实现Ajax请求跨域(vue-cli)
    pycharm复习
    广告制作如何高效完成视频视频审片?
    搭建Docker+SRS服务器实现推流拉流的效果
    基于高性能商用密码的电信领域防护实践
    面试java框架-Spring
    详细讲解RNN+LSTM+Tree_LSTM(Tree-Long Short Term Memory)基于树状长短期记忆网络
  • 原文地址:https://blog.csdn.net/qq_35056891/article/details/132719667