软件设计模式(四):观察者、组合、享元模式

news2024/11/28 2:38:48

前言

在这篇文章中,荔枝将会梳理软件设计模式中有关观察者模式、组合模式和享元模式的内容。其中组合模式和享元模式比较简单,重点需要理解观察者模式的机制以及为什么该模式实现了对象之间的松耦合。希望荔枝的梳理能对需要的小伙伴有帮助~~~


文章目录

前言

一、观察者模式Observer

二、组合模式Composite

三、享元模式Flyweight

总结


一、观察者模式Observer

        观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,依赖它的所有观察者对象都会收到通知并自动更新。这种模式跟发布订阅的流程类似,因此有些人又称该模式为发布订阅模式。观察者模式的核心思想是解耦,观察者模式通过将主题和观察者解耦,实现了对象之间的松耦合。当主题的状态发生改变时,所有依赖于它的观察者都会收到通知并进行相应的更新。在观察者模式中,主题只知道观察者的存在,并不知道观察者的具体实现。当主题状态发生改变时,它只需要通知观察者即可,而不需要关心观察者的具体实现。

关于观察者模式和发布订阅模式的异同,也有些博主的文章进行区分,这里可以看:

https://juejin.cn/post/6844903603107266567?searchId=202309082143393C968B11EA74CF13EAF1

观察者模式的结构组成如下,一般来说我们都会定义一个事件类,当触发了这个事件就会由观察者调用对应的事件执行方法。

        在下面的demo中,我们定义了事件类、观察者接口及其对应的实现类。当我们实例化观察者对象之后就会调用addActionListener方法并将传入对应的观察者实例对象作为参数。这个时候所有的观察者对象就会被加入到对应的观察者列表中并等待对应事件触发,这里是一个按键的操作buttonPressed()。当事件触发后观察者对象就会被观察者列表中一 一获取出来并调用对应的actionPerformed方法。

package com.crj.observer;

import java.util.ArrayList;
import java.util.List;


public class Test {
	public static void main(String[] args) {
		Button b = new Button();
		b.addActionListener(new MyActionListener1());
		b.addActionListener(new MyActionListener2());
		b.buttonPressed();
	}
}

/**
 * 观察者观察的对象
 */
class Button {
	
	private List<ActionListener> actionListeners = new ArrayList<ActionListener>();
	
	public void buttonPressed() {
		ActionEvent e = new ActionEvent(System.currentTimeMillis(),this);
		for(int i=0; i<actionListeners.size(); i++) {
			ActionListener l = actionListeners.get(i);
			l.actionPerformed(e);
		}
	}
	
	public void addActionListener(ActionListener l) {
		actionListeners.add(l);
	}
}

//观察者
interface ActionListener {
	public void actionPerformed(ActionEvent e);
}

class MyActionListener1 implements ActionListener {

	public void actionPerformed(ActionEvent e) {
		System.out.println("button pressed!");
	}
}

class MyActionListener2 implements ActionListener {

	public void actionPerformed(ActionEvent e) {
		System.out.println("button pressed 2!");
	}
}

/**
 * 事件类
 */
class ActionEvent {
	
	long when;
	Object source;
	
	public ActionEvent(long when, Object source) {
		super();
		this.when = when;
		this.source = source;
	}

	public long getWhen() {
		return when;
	}

	//事件源对象
	public Object getSource() {
		return source;
	}
	
}

        这里可能会有疑问,为什么这就是观察者模式了?在前面我们提及观察者模式其实是一种松耦合的机制,它降低了对象之间的耦合度,当一个对象的状态发生改变时,依赖它的所有观察者对象都会收到通知并自动更新。在这段demo中我们借助一个事件类定义获取事件源对象的方法,同时将不同观察者对象的实例放入了一个list列表,此时观察者们会等待事件触发并获得它们所依赖的(观察 | 监听)对象的变化信息(这里指的是方法的触发),这其实就是观察者模式的机制。


二、组合模式Composite

        组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次,它创建了对象组的树形结构,也提供了修改相同对象组的方式。可以说,组合模式是树状结构的专用模式。

其实跟树形数据结构类似,区分根节点和叶节点,将LeafNode和BranchNode通过tree的方式组合在一起,根节点下的子节点可以是一个根节点。

package com.crj.composite;

import java.util.ArrayList;
import java.util.List;

abstract class Node {
    abstract public void p();
}

class LeafNode extends Node {
    String content;
    public LeafNode(String content) {this.content = content;}

    @Override
    public void p() {
        System.out.println(content);
    }
}

class BranchNode extends Node {
    List<Node> nodes = new ArrayList<>();

    String name;
    public BranchNode(String name) {this.name = name;}

    @Override
    public void p() {
        System.out.println(name);
    }

    public void add(Node n) {
        nodes.add(n);
    }
}


public class Main {
    public static void main(String[] args) {

        BranchNode root = new BranchNode("root");
        BranchNode chapter1 = new BranchNode("chapter1");
        BranchNode chapter2 = new BranchNode("chapter2");
        Node r1 = new LeafNode("r1");
        Node c11 = new LeafNode("c11");
        Node c12 = new LeafNode("c12");
        BranchNode b21 = new BranchNode("section21");
        Node c211 = new LeafNode("c211");
        Node c212 = new LeafNode("c212");

        root.add(chapter1);
        root.add(chapter2);
        root.add(r1);
        chapter1.add(c11);
        chapter1.add(c12);
        chapter2.add(b21);
        b21.add(c211);
        b21.add(c212);

        tree(root, 0);

    }

    static void tree(Node b, int depth) {
        for(int i=0; i<depth; i++) System.out.print("--");
        b.p();

        if(b instanceof BranchNode) {
            for (Node n : ((BranchNode)b).nodes) {
                tree(n, depth + 1);
            }
        }
    }
}

三、享元模式Flyweight

        享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。​​​​​​​享元模式在Java里面的一个比较常见的应用就是对字符和字符串的存储。

package com.crj.flyweight;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

class Bullet{
    public UUID id = UUID.randomUUID();
    boolean living = true;

    @Override
    public String toString() {
        return "Bullet{" +
                "id=" + id +
                '}';
    }
}

public class BulletPool {
    List<Bullet> bullets = new ArrayList<>();
    {
        for(int i=0; i<5; i++) bullets.add(new Bullet());
    }

    public Bullet getBullet() {
        for(int i=0; i<bullets.size(); i++) {
            Bullet b = bullets.get(i);
            if(!b.living) return b;
        }

        return new Bullet();
    }

    public static void main(String[] args) {
        BulletPool bp = new BulletPool();

        for(int i=0; i<10; i++) {
            Bullet b = bp.getBullet();
            System.out.println(b);
        }
    }

}

享元模式相当于将创建的对象放进一个类似对象池的东西,需要的时候就会将对象激活并给出去,只有在不够的时候才会重新new一个对象。这其实也是一种池化的思想。


总结

        到现在荔枝已经学习了十种软件设计模式了,这些设计模式确实会在代码结构层面带来较好的优化,使得可拓展性更好,同时设计模式之间也是存在关联的,我们正常在使用的时候也可能会同时使用一种或多种设计模式。而对于设计模式的选择,则应该是适合业务的才是最好的哈哈哈哈。

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~

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

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

相关文章

光伏并网双向计量表ADL400

安科瑞 华楠 ADL400 导轨式多功能电能表&#xff0c;是主要针对电力系统&#xff0c;工矿企业&#xff0c;公用设施的电能统计、 管理需求而设计的一款智能仪表&#xff0c;产品具有精度高、体积小、安装方便等优点。集成常见电 力参数测量及电能计量及考核管理&#xff0c;…

python回调函数之获取jenkins构建结果

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 需求背景&#xff1a; 现在用jenkins构建自动化测试&#xff08;2个job&#xff09;&#xff0c;公司现将自动化纳入到发布系统 要求每次构建成功之后&am…

Neo4j图数据库实践——基于知识图谱方法开发构建猪类养殖疾病问答查询系统

Neo4j是一个开源的、高性能的图形数据库。它被设计用于存储、检索和处理具有复杂关系的大规模数据。与传统的关系型数据库不同&#xff0c;Neo4j使用图形结构来表示数据&#xff0c;其中节点表示实体&#xff0c;边表示实体之间的关系。这使得Neo4j在处理关系密集型数据时非常强…

【C++基础】5. 常量

文章目录 【 1. 常量的分类 】1.1 整型常量1.2 浮点常量1.3 字符常量1.4 字符串常量1.5 布尔常量 【 2. 常量的定义 】2.1 #define 预处理器2.2 const 关键字 常量 是固定值&#xff0c;在程序执行期间不会改变。这些固定的值&#xff0c;又叫做字面量。常量可以是任何的基本数…

【Flutter】Flutter 使用 flutter_dotenv 管理环境变量

【Flutter】Flutter 使用 flutter_dotenv 管理环境变量 文章目录 一、前言二、flutter_dotenv包简介三、安装和基本使用1. 安装flutter_dotenv2. 导入flutter_dotenv3. 使用flutter_dotenv 四、高级使用方法1. 变量引用2. 合并3. 在测试中使用 五、完整示例六、总结 一、前言 …

Codeforces-Round-895-Div-3

A. Two Vessels 题目翻译 你有两个装有水的容器。第一个容器含有 a a a克水&#xff0c;第二个容器含有 b b b克水。这两艘船都非常大&#xff0c;可以容纳任意数量的水。 您还有一个空杯子&#xff0c;最多可容纳 c c c克水。 一次&#xff0c;您可以从任何容器中舀出多 c…

UNet pytorch 胎教级介绍 使用DRIVE眼底血管分割数据集进行入门实战

同门的学妹做语义分割&#xff0c;于是打算稍微研究一下&#xff0c;最后的成果就是这篇文章&#xff0c;包括使用数据集进行测试&#xff0c;以及每一个部分的代码&#xff0c;还有一些思考改动和经验。 充分吸收本文知识你需要有pytorch的基础 U-net U-Net&#xff1a;深度…

您的密码是如何落入坏人之手的?

对于我们大多数人来说&#xff0c;密码只是无数在线服务最常用的身份验证方法。但对于网络犯罪分子而言&#xff0c;它的意义远不止于此——进入他人生活的捷径、至关重要的作案工具以及可以出售的商品。 对于我们大多数人来说&#xff0c;密码只是无数在线服务最常用的身份验证…

【MySql】数据库的聚合查询

写在最前面的话 哈喽&#xff0c;宝子们&#xff0c;今天给大家带来的是MySql数据库的聚合查询。在前面CRUD章节我们学习了表达式查询&#xff0c;表达式查询是针对列和列之间进行运算的&#xff0c;那么如果想在行和行之间进行运算&#xff0c;那么就需要用到聚合查询。聚合查…

计组+系统01:思维导图10分钟复习 I/O系统与中断

&#x1fa99;前言 考研笔记整理&#xff0c;纯复习向&#xff0c;思维导图基本就是全部内容了&#xff0c;不会涉及较深的知识点~~&#x1f95d;&#x1f95d; 第1版&#xff1a;查资料、画思维导图~&#x1f9e9;&#x1f9e9; 编辑&#xff1a; 梅头脑 BING AI 参考用书…

Deep Java Library(六)DJLServing自定义模型,自定义Translator注意事项

DJLServing自定义模型中自定义Translator注意事项需要仔细读一下DJLServing源码中的ServingTranslatorFactory类&#xff0c;&#xff0c;一开始不了解以为DJLServing选择Translator像玄学&#xff0c;后来看了像迷宫一样ServingTranslatorFactory类大致明白了&#xff0c;以下…

SAP-写了一个FUNCTION,用于读取订单中,指定工序的状态。

需求 在开发一个涉及工序的Report的过程中发现,SAP仅提供了Function去获取订单的状态,没有提供获取订单工序状态的Function。考虑到后续开发的过程中,还需要复用到这个功能,于是打算按这个需求去开发一个Function,传入AFVC表的主键,返回的是把该工序的全部已激活的状态,…

八 动手学深度学习v2 ——卷积神经网络之卷积+填充步幅+池化+LeNet

目录 1. 图像卷积总结2. 填充和步幅 padding和stride3. 多输入多输出通道4. 池化层5. LeNet 1. 图像卷积总结 二维卷积层的核心计算是二维互相关运算。最简单的形式是&#xff0c;对二维输入数据和卷积核执行互相关操作&#xff0c;然后添加一个偏置。核矩阵和偏移是可学习的参…

储能直流侧计量表DJSF1352

安科瑞 华楠 具有CE/UL/CPA/TUV认证 DJSF1352-RN导轨式直流电能表带有双路直流输入&#xff0c;主要针对电信基站、直流充电桩、太阳能光伏等应用场合而设计&#xff0c;该系列仪表可测量直流系统中的电压、电流、功率以及正反向电能等。在实际使用现场&#xff0c;即可计量总…

概念解析 | U-Net:医学图像分割的强大工具

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:U-Net。 U-Net:医学图像分割的强大工具 U-Net Architecture Explained - GeeksforGeeks 在医学图像分析的领域,一种称为U-Net的深度学习模型已经成为了一个标准工具。U-Net…

牛客练习赛115 A Mountain sequence

题目&#xff1a; 样例&#xff1a; 输入 3 5 1 2 3 4 5 3 3 3 3 3 1 2 1 输出 16 1 3 思路&#xff1a; 依据题意&#xff0c;再看数据范围&#xff0c;可以知道暴力肯定是不可能了&#xff0c;然后通过题目意思&#xff0c;我们可以排列模拟一下&#xff0c;这里排列所得结…

Decord库快速抽帧

Decord比Opencv块6倍&#xff01;&#xff01; 1. 使用教程 读取视频 # 1、读取使用 from decord import VideoReader from decord import cpu, gpuvr VideoReader(tiny-Kinetics-400\\abseiling\\_4YTwq0-73Y_000044_000054.mp4, ctxcpu(0))print(video frames:, len(vr)…

ArcGIS API for JavaScript 4.x 实现动态脉冲效果

1. 设计思路 主要通过定时刷新&#xff0c;每一次的脉冲渲染圈不停的放大&#xff0c;并且透明度缩小&#xff0c;直到达到一定的大小再退回0。 2. 实现代码 import MapView from "arcgis/core/views/MapView"; import GraphicsLayer from "arcgis/core/laye…

数学建模B多波束测线问题B

数学建模多波束测线问题 1.问题重述&#xff1a; 单波束测深是一种利用声波在水中传播的技术来测量水深的方法。它通过测量从船上发送声波到声波返回所用的时间来计算水深。然而&#xff0c;由于它是在单一点上连续测量的&#xff0c;因此数据在航迹上非常密集&#xff0c;但…

云备份——服务端客户端联合测试

一&#xff0c;准备工作 服务端清空备份文件信息、备份文件夹、压缩文件夹 客户端清空备份文件夹 二&#xff0c;开始测试 服务端配置文件 先启动服务端和客户端 向客户端指定文件夹放入稍微大点的文件&#xff0c;方便后续测试断点重传 2.1 上传功能测试 客户端自动上传成功…