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

news2024/9/23 13:20:00

目录

观察者模式

简介

优缺点

结构

实现

运用场景

访问者模式

简介

优缺点

结构

实现

运用场景

模板方法模式

简介

优缺点

结构

实现

运用场景


观察者模式

简介

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

优缺点

优点:

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

    @Override
    public void register(Observer observer){
        observers.add(observer);
    }
    
    @Override
    public void remove(Observer observer){
        observers.remove(observer);
    }

    @Override
    public void notifyAllObserver(){
        for (Observer observer : observers){
            observer.update();
        }
    }

    @Override
    public void doSomething(){
        this.notifyAllObserver();
    }
}

3.观察者

public interface Observer{

    public void update();
}

4.具体观察者

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

运用场景

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

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

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

访问者模式

简介

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

优缺点

优点:

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

        优秀的扩展性和灵活性

缺点:

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

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

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

结构

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

实现

抽象元素:

public interface Element {

    /**
     * 接受访问者
     * @param visitor
     */
    void accept(Visitor visitor);
}

//具体元素一
public class OneElement implements Element {

    @Override
    public void accept(Visitor visitor) {
        visitor.visitor(this);
    }
}

//具体元素二
public class TwoElement implements Element {

    @Override
    public 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 {

    @Override
    public void visitor(OneElement oneElement) {
        System.out.println("访问者一访问元素一 " + oneElement);
    }

    @Override
    public void visitor(TwoElement twoElement) {
        System.out.println("访问者一访问元素二 " + twoElement);
    }
}

具体访问者二:
public class TwoVisitor implements Visitor {

    @Override
    public void visitor(OneElement oneElement) {
        System.out.println("访问者二访问元素一 " + oneElement);
    }

    @Override
    public 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{

    @Override
    protected String calcAccountType() {
        return "Money Market";
    }

    @Override
    protected 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 {
    @Override
    protected String calcAccountType() {
        return "CD";
    }

    @Override
    protected 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.coloradmin.cn/o/986432.html

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

相关文章

Android 应用程序通过MediaPipe 图片识别

MediaPipe 中使用目标检测模型可以实现实时检测图像或视频中的物体&#xff0c;并标记出物体的位置和类别。MediaPipe 中的目标检测模型基于机器学习算法&#xff0c;经过训练以识别特定的物体类别&#xff1b; 以下是在 Android 应用程序中集成 MediaPipe Object Detection 的…

SpringMVC应用

文章目录 一、常用注解二、参数传递2.1 基础类型String2.2 复杂类型2.3 RequestParam2.4.路径传参 PathVariable2.4 Json数据传参 RequestBody2.5 RequestHeader 三、方法返回值3.1 void3.2 Stringmodel3.3 ModelAndView 一、常用注解 SpringMVC是一个基于Java的Web框架&#…

Nacos启动连接mysql报错

问题 Nacos启动后&#xff0c;访问http://localhost:8848/nacos/index.html一直访问不了&#xff0c;查看nacos安装目录下的logs/config-fatal.log日志文件发现连接mysql报错&#xff0c;但是通过客户端连接工具测试mysql连接正常&#xff1b;核心报错如下&#xff1a; Cause…

【线程池】面试被问到线程池参数如何配置时该如何回答

前言 没有基于业务场景&#xff0c;直接抛出这个问题&#xff0c;等同于耍流氓。 八股文告诉我们CPU密集型就核心数1&#xff0c;IO密集型就核心数*2&#xff0c;那么真实业务中该怎么去配置呢。 方法论还是有的 1.需要分析线程池执行的任务的特性&#xff1a; CPU 密集型还是 …

【操作】安防监控/视频汇聚/视频云存储EasyCVR平台AI智能分析网关V3接入教程2.0

TSINGSEE的边缘计算硬件智能分析网关V3内置多种AI算法模型&#xff0c;包括人脸、人体、车辆、车牌、行为分析、烟火、入侵、聚集、安全帽、反光衣等等&#xff0c;可应用在安全生产、通用园区、智慧食安、智慧城管、智慧煤矿等场景中。将网关硬件结合TSINGSEE青犀的视频汇聚/安…

数据结构-01 数据结构基本概念,算法时间复杂度,空间复杂度

0 数据结构概述 四门课的关系 1 绪论 数据对象、数据元素、数据项关系 1.1 数据结构的基本概念 1.2 算法和算法评价 小练习 空间复杂度中的递归调用 n只是传入 n也是数组&#xff0c;计算存储数组flag的空间大小

HTTPS协议和SOCKS5协议的区别

HTTPS协议和SOCKS5协议是两种不同的网络协议&#xff0c;它们在传输数据的方式、安全性和使用场景等方面都有所不同。下面将介绍HTTPS协议与SOCKS5协议的区别。 传输数据的方式 HTTPS协议是一种基于HTTP协议的安全协议&#xff0c;它使用SSL/TLS协议对数据进行加密和解密。在传…

Kafka3.0.0版本——消费者(独立消费者消费某一个主题数据案例__订阅主题)

目录 一、独立消费者消费某一个主题数据案例1.1、案例需求1.2、案例代码1.3、测试 一、独立消费者消费某一个主题数据案例 1.1、案例需求 创建一个独立消费者&#xff0c;消费firstTopic主题中数据&#xff0c;所下图所示&#xff1a; 注意&#xff1a;在消费者 API 代码中必…

时序预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络时间序列预测(风电功率预测)

时序预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络时间序列预测&#xff08;风电功率预测&#xff09; 目录 时序预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络时间序列预测&#xff08;风电功率预测&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1…

Matlab 如何把频谱图的纵坐标设置为分贝刻度

Matlab 如何把频谱图的纵坐标设置为分贝刻度 Matlab代码如下&#xff1a; % 如何把频谱图的纵坐标设置为分贝刻度 % % pr2_2_6 clc; clear; close all;load pr2_2_6_sndata1.mat % 读入数据 X fft(y); % FFT n2 1:L/21; % 计算正频率…

天翼云不做备案接入,如何绑定域名,不用80端口,443端口。

443&#xff0c;80端口不开启。 第一步&#xff1a; 宝塔更改web端口 搞个复杂的端口。 第二步&#xff1a; 在天翼云策略组上面开启修改过的web端口。 第三步&#xff1a;接入cdn&#xff0c;端口改成修改过的端口。

EVA: Visual Representation Fantasies from BAAI

本文做个简单总结&#xff0c;博主不是做自监督领域的&#xff0c;如果错误&#xff0c;欢迎指正。 链接 Code&#xff1a; Official&#xff1a;baaivision/EVA MMpretrain&#xff1a;open-mmlab/mmpretrain/tree/main/configs/eva02 Paper&#xff1a; EVA01&#xff1a;…

LAMP搭建WordPress

L linux A apache hhtpd M mysql/maridb P PHP1、 安装php yum -y install php php-fpm php-server php-mysql1.1、 启动php-fpm并自启 systemctl enable php-fpm --now[rootecs-1cee ~]# systemctl status php-fpm ● php-fpm.service - The PHP FastCGI Process ManagerLoa…

Ae 效果:CC Cross Blur

模糊和锐化/CC Cross Blur Blur & Sharpen/CC Cross Blur CC Cross Blur&#xff08;CC 交叉模糊&#xff09;可以通过单独控制水平和垂直方向的模糊程度来创建独特的交叉模糊效果&#xff0c;可以产生类似光线交错的视觉外观。 ◆ ◆ ◆ 效果属性说明 Radius X 半径 X 用…

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

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

Python调试学习资料

Python调试学习资料 python -m pdb example.py网络资源 Python代码调试的几种方法总结Python 程序如何高效地调试&#xff1f;Python Debugging With Pdbpdb — The Python DebuggerThe Python Debugger (pdb)Python Debugger with Examples

Redis常用命令和Java操作Redis教程

Redis介绍 关系型数据库&#xff08;RDBMS&#xff09;非关系型数据库&#xff08;NoSql)MysqlRedisOracleMongo dbDB2MemCachedSQLServer Redis是一个基于内存的key-value结构的数据库。 特点&#xff1a; 基于内存存储&#xff0c;读写性能高适合存储热点数据&#xff08;…

Java8特性-Lambda表达式

&#x1f4d5;概述 在Java 8中引入了Lambda表达式作为一项重要的语言特性&#xff0c;可以堪称是一种语法糖。Lambda表达式使得以函数式编程的方式解决问题变得更加简洁和便捷。 Lambda表达式的语法如下&#xff1a; (parameters) -> expression (参数) -> {代码}其中&…

Apache HTTPD 漏洞复现

文章目录 Apache HTTPD 漏洞复现1. Apache HTTPD 多后缀解析漏洞1.1 漏洞描述1.2 漏洞复现1.3 漏洞利用1.4 获取GetShell 2. Apache HTTPD 换行解析漏洞-CVE-2017-157152.1 漏洞描述2.2 漏洞复现2.3 漏洞利用 3. Apache HTTP Server_2.4.49 路径遍历和文件泄露漏洞-CVE-2021-41…

字体图标 IcoMoon

字体图标 展示的是图标&#xff0c;本质是字体。 轻量级︰一个图标字体要比一系列的图像要小。一旦字体加载了&#xff0c;图标就会马上渲染出来&#xff0c;减少了服务器请求 灵活性:本质其实是文字&#xff0c;可以很随意的改变颜色、产生阴影、透明效果、旋转等 兼容性:几乎…