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

目录

观察者模式

简介

优缺点

结构

实现

运用场景

访问者模式

简介

优缺点

结构

实现

运用场景

模板方法模式

简介

优缺点

结构

实现

运用场景


观察者模式

简介

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

优缺点

优点:

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

        2.支持广播

缺点:

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

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

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

结构

角色:

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

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

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

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

实现

1.被观察者

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

2.具体观察者

public class ConcreteSubject implements Subject {private List<Observer> observers = new ArrayList<>();@Overridepublic void register(Observer observer){observers.add(observer);}@Overridepublic void remove(Observer observer){observers.remove(observer);}@Overridepublic void notifyAllObserver(){for (Observer observer : observers){observer.update();}}@Overridepublic void doSomething(){this.notifyAllObserver();}
}

3.观察者

public interface Observer{public void update();
}

4.具体观察者

public class Concrete2 implements Observer{@Overridepublic void update(){System.out.println("收到消息!");}
}

运用场景

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

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

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

访问者模式

简介

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

优缺点

优点:

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

        优秀的扩展性和灵活性

缺点:

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

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

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

结构

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

实现

抽象元素:public interface Element {/*** 接受访问者* @param visitor*/void accept(Visitor visitor);
}//具体元素一
public class OneElement implements Element {@Overridepublic void accept(Visitor visitor) {visitor.visitor(this);}
}//具体元素二
public class TwoElement implements Element {@Overridepublic void accept(Visitor visitor) {visitor.visitor(this);}
}抽象访问者:
public interface Visitor {/*** 访问元素一* @param oneElement*/void visitor(OneElement oneElement);/*** 访问元素二* @param twoElement*/void visitor(TwoElement twoElement);
}具体访问者一:
public class OneVisitor implements Visitor {@Overridepublic void visitor(OneElement oneElement) {System.out.println("访问者一访问元素一 " + oneElement);}@Overridepublic void visitor(TwoElement twoElement) {System.out.println("访问者一访问元素二 " + twoElement);}
}具体访问者二:
public class TwoVisitor implements Visitor {@Overridepublic void visitor(OneElement oneElement) {System.out.println("访问者二访问元素一 " + oneElement);}@Overridepublic void visitor(TwoElement twoElement) {System.out.println("访问者二访问元素二 " + twoElement);}
}对象结构:
public class ObjectStructure {private List<Element> elements;public ObjectStructure() {this.elements = new ArrayList<>();}public void add(Element element) {this.elements.add(element);}public Element get(Integer index) {return this.elements.get(index);}public void remove(Element element) {this.elements.remove(element);}/*** 访问对象内元素* @param visitor*/public void accept(Visitor visitor) {for (Element element : this.elements) {element.accept(visitor);}}
}测试:
public class VisitorTest {public static void main(String[] args) {ObjectStructure objectStructure = new ObjectStructure();objectStructure.add(new OneElement());objectStructure.add(new OneElement());objectStructure.add(new TwoElement());objectStructure.add(new TwoElement());objectStructure.accept(new OneVisitor());System.out.println();objectStructure.accept(new TwoVisitor());}
}

运用场景

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

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

模板方法模式

简介

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

优缺点

优点:

        提高代码复用性

        提高了拓展性

        实现了反向控制

缺点:

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

结构

角色:

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

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

实现

1.抽象类

package com.charon.template;/*** @className: Account* @description:* @author: charon* @create: 2022-03-26 14:57*/
public abstract class Account {private String accountNum;/*** 无参构造,帮助子类初始化* 如果没有显示声明父类的无参的构造方法,系统会自动默认生成一个无参构造方法。* 但是,如果声明了一个有参的构造方法,而没有声明无参的构造方法,这时系统不会动默认生成一个无参构造方法。*/public Account() {}public Account(String accountNum) {this.accountNum = accountNum;}/*** 模板方法,计算利息的数额* 使用final修饰,可以防止子类重写模板方法* @return*/public final double calcInterest(){double interestRate = calcInterestRate();String accountType = calcAccountType();double amount = getAmount(accountType,accountNum);return interestRate * amount;}/*** 获取账户金额* @param accountType 账户类型* @param accountNum 账号* @return*/protected double getAmount(String accountType, String accountNum){// 从数据库中获取该账户的金额,这里直接返回一个return 5000;}/*** 计算账户类型*/protected abstract String calcAccountType();/*** 计算利息利率* @return*/protected abstract double calcInterestRate();
}

2.子类

package com.charon.template;/*** @className: MoneyMarketAccount* @description:* @author: charon* @create: 2022-03-26 15:12*/
public class MoneyMarketAccount extends Account{@Overrideprotected String calcAccountType() {return "Money Market";}@Overrideprotected double calcInterestRate() {return 0.045;}
}package com.charon.template;/*** @className: CDAccount* @description:* @author: charon* @create: 2022-03-26 15:22*/
public class CDAccount extends Account {@Overrideprotected String calcAccountType() {return "CD";}@Overrideprotected double calcInterestRate() {return 0.065;}
}

3.调用

package com.charon.template;/*** @className: Client* @description: * @author: charon* @create: 2022-03-25 23:44*/
public class Client {public static void main(String[] args) {Account mmAccount = new MoneyMarketAccount();System.out.println("货币市场账号的利息是:" + mmAccount.calcInterest());Account cdAccount = new CDAccount();System.out.println("定期账号的利息是:" + cdAccount.calcInterest());}
}打印:货币市场账号的利息是:225.0定期账号的利息是:325.0	

运用场景

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

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

        控制子类的扩展

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/71699.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3自定义指令

自定义指令是用来增强与扩展元素功能的 在vue官方文当中写的很明白,这里我就不在赘述官方文档.只记录在项目中的实际应用. 局部定义 <template><h4>指令</h4><p v-color-red>字体固定颜色的指令</p><p v-color-value"blue">可…

【List篇】ArrayList 详解(含图示说明)

Java中的ArrayList是一个动态数组&#xff0c;可以自动扩展容量以适应数据的添加和删除。它可以用来存储各种类型的数据&#xff0c;例如String&#xff0c;Integer&#xff0c;Boolean等。ArrayList实现了List接口&#xff0c;可以进行常见的List操作&#xff0c;例如添加、插…

Gridea+GitPage+Gittalk 搭建个人博客

&#x1f44b;通过GrideaGitPage 搭建属于自己的博客&#xff01; &#x1f47b;GitPage 负责提供 Web 功能&#xff01; &#x1f63d;Gridea 作为本地编辑器&#xff0c;方便 push 文章&#xff01; &#x1f3f7;本文讲解如何使用 GrideaGitPage 服务域名&#xff08;可选&a…

原生Js Canvas去除视频绿幕背景

Js去除视频背景 注&#xff1a; 这里的去除视频背景并不是对视频文件进行操作去除背景 如果需要对视频扣除背景并导出可以使用ffmpeg等库&#xff0c;这里仅作播放用所以采用这种方法 由于uniapp中的canvas经过封装&#xff0c;且 uniapp 的 drawImage 无法绘制视频帧画面&…

TOWE雷达光敏感应开关,让生活更智能、更安全

现代生活中&#xff0c;智能家居成为人们追求品质生活的必备之选。其中&#xff0c;照明控制的智能化已然成为一种趋势&#xff0c;传统的灯光开关需要人们手动操作&#xff0c;既不方便&#xff0c;有时候也会造成资源的过度浪费&#xff0c;而雷达光敏感应开关的出现&#xf…

QT之形态学操作

形态学操作包含以下操作&#xff1a; 腐蚀 (Erosion)膨胀 (Dilation)开运算 (Opening)闭运算 (Closing)形态梯度 (Morphological Gradient)顶帽 (Top Hat)黑帽(Black Hat) 其中腐蚀和膨胀操作是最基本的操作&#xff0c;其他操作由这两个操作变换而来。 腐蚀 用一个结构元素…

【MySQL】MySQL的安装,登录,配置和相关命令

文章目录 前言一. 卸载不需要的环境二. 获取MySQL的yum源三. 安装MySQL和启动四. 尝试登录MySQL方法1&#xff1a;获取临时root密码方法2&#xff1a;没有密码方法3&#xff1a;配置文件 五. 简单配置结束语 前言 本篇文章是基于云服务器&#xff1b;Linux&#xff1a;Centos7…

Leetcode.174 地下城游戏

题目链接 Leetcode.174 地下城游戏 hard 题目描述 恶魔们抓住了公主并将她关在了地下城 d u n g e o n dungeon dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里&#xff0c;他必须穿过地下城并通过对抗恶魔来拯救公…

Vuepress样式修改内容宽度

1、相关文件 一般所在目录node_modules\vuepress\theme-default\styles\wrapper.styl 2、调整宽度&#xff0c;截图中是已经调整好的&#xff0c;在我电脑上显示刚刚好。

【Maven教程】(五)仓库:解析Maven仓库—布局、分类和配置,远程仓库的认证与部署,快照版本,依赖解析机制,镜像和搜索服务 ~

Maven 仓库 1️⃣ 什么是Maven仓库2️⃣ 仓库的布局3️⃣ 仓库的分类3.1 本地仓库3.2 远程仓库3.3 中央仓库3.4 私服 4️⃣ 远程仓库的配置4.1 远程仓库的认证4.2 部署至远程仓库 5️⃣ 快照版本6️⃣ 从仓库解析依赖的机制7️⃣ 镜像8️⃣ 仓库搜索服务8.1 Sonatype Nexus8.2…

微信小程序开发---事件的绑定

目录 一、事件的概念 二、小程序中常用的事件 三、事件对象的属性列表 四、bindtap的语法格式 &#xff08;1&#xff09;绑定tap触摸事件 &#xff08;2&#xff09;编写处理函数 五、在事件处理函数中为data中的数据赋值 六、事件传参 七、bindinput的语法格式 八、…

⛳ MVCC 原理详解

&#x1f38d;目录 ⛳ MVCC 原理详解&#x1f43e; 一、事务回顾&#x1f4d0; 1.1、什么是数据库事务&#xff0c;为什么要有事务&#x1f389; 1.2、事务包括哪几个特性&#xff1f;&#x1f38d; 1.3、事务并发存在的问题1.3.1、脏读1.3.2、不可重复读1.3.3、幻读 &#x1f…

Linux命令200例:Yum强大的包管理工具使用(常用)

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0…

LeetCode 1123. Lowest Common Ancestor of Deepest Leaves【树,DFS,BFS,哈希表】1607

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

09-JVM垃圾收集底层算法实现

上一篇&#xff1a;08-JVM垃圾收集器详解 1.三色标记 在并发标记的过程中&#xff0c;因为标记期间应用线程还在继续跑&#xff0c;对象间的引用可能发生变化&#xff0c;多标和漏标的情况就有可能发生。 这里我们引入“三色标记”来给大家解释下&#xff0c;把Gcroots可达性…

Java 内部类

目录 一、什么是内部类及为何要有内部类 二、四种内部类 1.成员内部类 成员内部类定义&#xff1a; 获取成员内部类对象的方法&#xff1a; 成员内部类获取外部类变量: 额外&#xff1a; 2.局部内部类 局部内部类定义: 如何实现内部类当中的方法&#xff1a; 3.静态内…

【opencv】多版本安装

安装opencv3.2.0以及对应的付费模块 一、安装多版本OpenCV如何切换 按照如下步骤安装的OpenCV&#xff0c;在CMakeLists.txt文件中&#xff0c;直接指定opencv的版本就可以找到相应版本的OpenCV&#xff0c;为了验证可以在CMakeLists.txt文件中使用如下指令输出版本验证&…

二、创建个人首页页面

简介 改造 App.vue 创建一个展示页面,实现一个可以轮播的功能效果。欢迎访问个人的简历网站预览效果 本章涉及修改与新增的文件:style.css、App.vue、assets 一、 自定义全局样式 将 style.css 中的文件样式内容替换为如下代码 /* 初始化样式 --------------------------…

python-爬虫-xpath方法-批量爬取王者皮肤图片

import requests from lxml import etree获取NBA成员信息 # 发送的地址 url https://nba.hupu.com/stats/players # UA 伪装 google header {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.3…

CSS笔记(黑马程序员pink老师前端)盒子阴影,文字阴影

盒子阴影 属性值为box-shadow,盒子阴影不占空间,不影响盒子之间的距离. 值说明h-shadow必需,水平阴影位置,允许为负值v-shadow必需,水平阴影位置,允许为负值blur可选,模糊距离,数值越大影子越模糊spread可选,影子的尺寸color可选,影子的颜色inset可选, 将外阴影改为内阴影(省…