Java23种设计模式(四)

1、备忘录模式

备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象,备忘录模式属于行为型模式。

备忘录模式允许在不破坏封装性的前提下,捕获和恢复对象的内部状态。
实现方式

  • 创建备忘录类:用于存储和封装对象的状态。
  • 创建发起人角色:负责创建备忘录,并根据需要恢复状态。
  • 创建备忘录管理类(可选):负责管理所有备忘录对象。

2、观察者模式

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

观察者模式解决的是一个对象状态改变时,如何自动通知其他依赖对象的问题,同时保持对象间的低耦合和高协作性。

2.1、实现方式

  • 定义观察者接口:包含一个更新方法。
  • 创建具体观察者:实现观察者接口,定义接收到通知时的行为。
  • 定义主题接口:包含添加、删除和通知观察者的方法。
  • 创建具体主题:实现主题接口,管理观察者列表,并在状态改变时通知它们。

2.2、应用实例

拍卖系统:拍卖师作为主题,竞价者作为观察者,拍卖价格更新时通知所有竞价者。

2.3、优点

  • 抽象耦合:观察者和主题之间是抽象耦合的。
  • 触发机制:建立了一套状态改变时的触发和通知机制。

2.4、缺点

  • 性能问题:如果观察者众多,通知过程可能耗时。
  • 循环依赖:可能导致循环调用和系统崩溃。
  • 缺乏变化详情:观察者不知道主题如何变化,只知道变化发生。

2.4、使用建议

在需要降低对象间耦合度,并且对象状态变化需要触发其他对象变化时使用。
考虑使用Java内置的观察者模式支持类,如java.util.Observable和java.util.Observer。

2.5、注意事项

避免循环引用:注意观察者和主题之间的依赖关系,避免循环引用。
异步执行:考虑使用异步通知避免单点故障导致整个系统卡壳。

2.6、实现

观察者模式使用三个类 Subject、Observer 和 Client。Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。我们创建 Subject 类、Observer 抽象类和扩展了抽象类 Observer 的实体类。

ObserverPatternDemo,我们的演示类使用 Subject 和实体类对象来演示观察者模式。
在这里插入图片描述

步骤 1
创建 Subject 类。

Subject.java
import java.util.ArrayList;
import java.util.List;
 
public class Subject {
   
   private List<Observer> observers 
      = new ArrayList<Observer>();
   private int state;
 
   public int getState() {
      return state;
   }
 
   public void setState(int state) {
      this.state = state;
      notifyAllObservers();
   }
 
   public void attach(Observer observer){
      observers.add(observer);      
   }
 
   public void notifyAllObservers(){
      for (Observer observer : observers) {
         observer.update();
      }
   }  
}

步骤 2
创建 Observer 类。

Observer.java
public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}

步骤 3
创建实体观察者类。

BinaryObserver.java
public class BinaryObserver extends Observer{
 
   public BinaryObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }
 
   @Override
   public void update() {
      System.out.println( "Binary String: " 
      + Integer.toBinaryString( subject.getState() ) ); 
   }
}
OctalObserver.java
public class OctalObserver extends Observer{
 
   public OctalObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }
 
   @Override
   public void update() {
     System.out.println( "Octal String: " 
     + Integer.toOctalString( subject.getState() ) ); 
   }
}
HexaObserver.java
public class HexaObserver extends Observer{
 
   public HexaObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }
 
   @Override
   public void update() {
      System.out.println( "Hex String: " 
      + Integer.toHexString( subject.getState() ).toUpperCase() ); 
   }
}

步骤 4
使用 Subject 和实体观察者对象。

ObserverPatternDemo.java
public class ObserverPatternDemo {
   public static void main(String[] args) {
      Subject subject = new Subject();
 
      new HexaObserver(subject);
      new OctalObserver(subject);
      new BinaryObserver(subject);
 
      System.out.println("First state change: 15");   
      subject.setState(15);
      System.out.println("Second state change: 10");  
      subject.setState(10);
   }
}

步骤 5
执行程序,输出结果:

First state change: 15
Hex String: F
Octal String: 17
Binary String: 1111
Second state change: 10
Hex String: A
Octal String: 12
Binary String: 1010

3、状态模式

在状态模式(State Pattern)中,类的行为是基于它的状态改变的,这种类型的设计模式属于行为型模式。

在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

状态模式允许对象在内部状态改变时改变其行为,使得对象在不同的状态下有不同的行为表现。通过将每个状态封装成独立的类,可以避免使用大量的条件语句来实现状态切换。

3.1、使用场景

当代码中存在大量条件语句,且这些条件语句依赖于对象的状态时。

3.2、使用建议

当对象的行为随状态改变而变化时,考虑使用状态模式。
状态模式适用于替代复杂的条件或分支语句。

3.3、注意事项

状态模式适用于状态数量有限(通常不超过5个)的情况。
谨慎使用,以避免系统变得过于复杂。

4、空对象模式

在空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。

在空对象模式中,我们创建一个指定各种要执行的操作的抽象类和扩展该类的实体类,还创建一个未对该类做任何实现的空对象类,该空对象类将无缝地使用在需要检查空值的地方。

使用一个空对象代替 null 值,这个空对象实现了相同的接口,但对请求不做任何操作或提供默认操作。

5、策略模式

在策略模式(Strategy Pattern)中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

将每个算法封装起来,使它们可以互换使用。

5.1、应用实例

  • 锦囊妙计:每个锦囊代表一个策略,包含不同的计策。
  • 旅行方式选择:骑自行车、坐汽车等,每种方式都是一个可替换的策略。
  • Java AWT的LayoutManager:不同的布局管理器实现了相同的接口,但提供了不同的布局算法。

5.2、优点

  • 算法切换自由:可以在运行时根据需要切换算法。
  • 避免多重条件判断:消除了复杂的条件语句。
  • 扩展性好:新增算法只需新增一个策略类,无需修改现有代码。

5.3、缺点

  • 策略类数量增多:每增加一个算法,就需要增加一个策略类。
  • 所有策略类都需要暴露:策略类需要对外公开,以便可以被选择和使用。

如果系统中策略类数量过多,考虑使用其他模式或设计技巧来解决类膨胀问题。

6、模板模式

在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

在父类中定义了算法的骨架,并允许子类在不改变算法结构的前提下重定义算法的某些特定步骤。

6.1、实现方式

定义抽象父类:包含模板方法和一些抽象方法或具体方法。
实现子类:继承抽象父类并实现抽象方法,不改变算法结构。

6.2、优点

  • 封装不变部分:算法的不变部分被封装在父类中。
  • 扩展可变部分:子类可以扩展或修改算法的可变部分。
  • 提取公共代码:减少代码重复,便于维护。

为了防止恶意修改,模板方法通常使用final关键字修饰,避免被子类重写。

6.3、实现

我们将创建一个定义操作的 Game 抽象类,其中,模板方法设置为 final,这样它就不会被重写。Cricket 和 Football 是扩展了 Game 的实体类,它们重写了抽象类的方法。

TemplatePatternDemo,我们的演示类使用 Game 来演示模板模式的用法。
在这里插入图片描述
步骤 1
创建一个抽象类,它的模板方法被设置为 final。

Game.java
public abstract class Game {
   abstract void initialize();
   abstract void startPlay();
   abstract void endPlay();
 
   //模板
   public final void play(){
 
      //初始化游戏
      initialize();
 
      //开始游戏
      startPlay();
 
      //结束游戏
      endPlay();
   }
}

步骤 2
创建扩展了上述类的实体类。

Cricket.java
public class Cricket extends Game {
 
   @Override
   void endPlay() {
      System.out.println("Cricket Game Finished!");
   }
 
   @Override
   void initialize() {
      System.out.println("Cricket Game Initialized! Start playing.");
   }
 
   @Override
   void startPlay() {
      System.out.println("Cricket Game Started. Enjoy the game!");
   }
}
Football.java
public class Football extends Game {
 
   @Override
   void endPlay() {
      System.out.println("Football Game Finished!");
   }
 
   @Override
   void initialize() {
      System.out.println("Football Game Initialized! Start playing.");
   }
 
   @Override
   void startPlay() {
      System.out.println("Football Game Started. Enjoy the game!");
   }
}

步骤 3
使用 Game 的模板方法 play() 来演示游戏的定义方式。

TemplatePatternDemo.java
public class TemplatePatternDemo {
   public static void main(String[] args) {
 
      Game game = new Cricket();
      game.play();
      System.out.println();
      game = new Football();
      game.play();      
   }
}

步骤 4
执行程序,输出结果:

Cricket Game Initialized! Start playing.
Cricket Game Started. Enjoy the game!
Cricket Game Finished!

Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!

7、访问者模式

在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

旨在将数据结构与在该数据结构上执行的操作分离,从而使得添加新的操作变得更容易,而无需修改数据结构本身。

使用场景
当需要对一个对象结构中的对象执行多种不同的且不相关的操作时,尤其是这些操作需要避免"污染"对象类本身。
实现方式
定义访问者接口:声明一系列访问方法,一个访问方法对应数据结构中的一个元素类。
创建具体访问者:实现访问者接口,为每个访问方法提供具体实现。
定义元素接口:声明一个接受访问者的方法。
创建具体元素:实现元素接口,每个具体元素类对应数据结构中的一个具体对象

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

反激式开关电源是如何工作的

反激的变压器可以看作一个带变压功能的电感&#xff0c;是一个buck-boost电路。 反击式开关变压器 反激式开关电源是指使用反激高频变压器隔离输入输出回路的开关电源。“反激”指的是在开关管接通的情况下&#xff0c;当输入为高电平时输出线路中串联的电感为放电状态&#x…

硬盘格式化NTFS好还是exFAT好 U盘存储文件用哪个格式好? 硬盘用exfat还是ntfs mac不能读取移动硬盘怎么解决

在计算机世界中&#xff0c;文件系统是数据管理的基石&#xff0c;而NTFS和exFAT无疑是这块基石上的两大巨头。它们各自拥有独特的特点和优势&#xff0c;并在不同的使用场景中发挥着重要作用。 什么是文件系统 文件系统提供了组织驱动器的方法。它规定了如何在驱动器上存储数…

2024/06/18--代码随想录算法8/17| 股票问题

121.买卖股票的最佳时机 力扣链接 动规五部曲 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][0] 表示第i天持有股票所得最多现金,dp[i][1] 表示第i天不持有股票所得最多现金确定递推公式 dp[i][0] max(dp[i-1][0], -price[i]) dp[i][1]max(dp[i-1][1], …

spark独立集群搭建

spark独立集群搭建(不依赖Hadoop) 1、上传spark-2.4.5-bin-hadoop2.7.tgz至 /usr/local/moudel &#xff0c;再解压到 /usr/local/soft tar -zxvf spark-2.4.5-bin-hadoop2.7.tgz -C /usr/local/soft/ 重命名 mv spark-2.4.5-bin-hadoop2.7/ spark-2.4.5 配…

深入理解计算机系统 CSAPP 家庭作业6.45

CS:APP3e, Bryant and OHallaron 可以参考这里

mediasoup源码分析(三)channel创建及信令交互

mediasoup源码分析--channel创建及信令交互 概述跨职能图业务流程图代码剖析 概述 在golang实现mediasoup的tcp服务及channel通道一文中&#xff0c;已经介绍过信令服务中tcp和channel的创建&#xff0c;本文主要讲解c中mediasoup的channel创建&#xff0c;以及信令服务和medi…

深圳比创达电子EMC|EMC与EMI滤波器:守护电子设备的电磁防火墙

随着科技的飞速发展&#xff0c;电子设备在我们日常生活中的普及率越来越高&#xff0c;从智能手机到大型工业设备&#xff0c;无一不体现出电子技术的重要地位。然而&#xff0c;随之而来的电磁兼容性问题&#xff08;EMC&#xff09;和电磁干扰问题&#xff08;EMI&#xff0…

基于稀疏学习现代信号处理方法的旋转机械故障诊断(MATLAB)

通过对滚动轴承故障诊断研究现状及稀疏表示方法在滚动轴承故障诊断领域中应用现状的调研&#xff0c;发现稀疏表示方法与故障特征提取和故障分类的关联&#xff0c;针对故障诊断问题&#xff0c;通过构造合理的故障稀疏表示模型&#xff0c;选取适合的模型优化算法&#xff0c;…

华为HCIP Datacom H12-821 卷6

1.单选题 下面是一台路由器的部分配置&#xff0c;关于该部分配置描述正确的是&#xff0c;[HUAWEllJip ip-prefix plpermit 10.0.192.0 8 greater-equal 17 less-equal 18 A、10.0.192.0/8 网段内&#xff0c;掩码长度为 20 的路由会匹配到该前缀列表&#xff0c;匹配规则为…

[保姆级教程]uniapp配置vueX

文章目录 注意新建文件简单的使用 注意 uniapp是支持vueX的只需配置一下就好 新建文件 在src文件中&#xff0c;新建一个store&#xff08;如果有的话跳过&#xff09; 在store中新建一个js文件&#xff0c;修改js文件名称和选择模板为default 在 uni-app 项目根目录下&…

牛客周赛 F-花花的地图

原题链接&#xff1a;F-花花的地图 题目大意&#xff1a;的网格里面&#xff0c;.为可以通行&#xff0c;#为不可以通行&#xff0c;如果想要通行可以花费代价将一列的障碍全部清除&#xff0c;求从到的最小花费。 思路&#xff1a;迪杰斯特拉的变种&#xff0c;优先队列里面…

用研究的眼光解读如何基于UVM搭建验证平台《UVM实战》(可下载)

UVM&#xff08;Universal Verification Methodology&#xff0c;通用验证方法学&#xff09;是一种用于硬件设计和验证的标准化方法学&#xff0c;它基于SystemVerilog语言扩展&#xff0c;由Accellera组织推出&#xff0c;并得到了主要的EDA&#xff08;Electronic Design Au…

重磅!首个跨平台的通用Linux端间互联组件Klink在openKylin开源

随着智能终端设备的普及&#xff0c;多个智能终端设备之间的互联互通应用场景日益丰富&#xff0c;多设备互联互通应用场景需要开发者单独实现通讯协议。因此&#xff0c;为解决跨平台互联互通问题&#xff0c;由openKylin社区理事单位麒麟软件旗下星光麒麟团队成立的Connectiv…

音频处理软件adobe audition使用教程

教程1笔记 基本操作 点击文件-》新建-》多轨会话&#xff1a; 编辑-》首选项&#xff0c;设置自动保存时间&#xff1a; 导入素材&#xff0c;文件-》导入素材&#xff0c;或者直接拖动进来文件&#xff01; 导出多轨混音&#xff1a; 更改为需要导出的格式wav,mp3等格式&am…

ROS程序设计系列 - 2.ROS Package

ROS程序设计系列 - 2.ROS Package 1. 源由2. 关键要点2.1 ROS包组成2.2 消息解耦2.3 包版本管理2.4 编译配置2.5 ROS C Client Library2.5.1 Initialization and spinning2.5.2 Node handle2.5.3 Logging2.5.4 Subscribe and Publisher2.5.5 Parameters 2.6 Object Oriented Pr…

【Android】使用Binder(AIDL)实现利用自定义Bean进行的进程间通信(二)

项目前置 这是我之前写的关于Binder的一些知识点和使用基本数据类型在通信的文章&#xff0c;感兴趣的可以看一下: Binder&#xff08;一&#xff09;Binder的介绍和AIDL使用Binder的实例 项目目标 在两个APP之间进行数据传递&#xff0c;使用Android推荐的Binder通讯&#…

产业生态远超预期,商用进程全面提速:5G RedCap,凭什么这么火?

2022年6月&#xff0c;3GPP R17版本正式宣布冻结。除了针对传统5G技术标准进行完善之外&#xff0c;R17还推出了一项新的中高速物联技术标准&#xff0c;也就是我们今天文章的主角——RedCap。 RedCap推出后&#xff0c;受到了业界上下的广泛关注。它在传统5G的基础上&#xff…

新手小白系列——关于 Docker 安装的方法

Docker 是一个应用打包、分发、部署的工具基础概念&#xff1a; 镜像&#xff1a;软件安装包&#xff0c;可以方便的进行传播和安装。 容器&#xff1a;软件安装之后的状态&#xff0c;每个软件运行环境都是独立的、隔离的&#xff0c;称之为容器 仓库&#xff1a;专门用来传播…

k8s集群新增计算节点使用华为iscsi存储创建的pvc存储挂载报错:FailedMount

背景&#xff1a; 因公司业务需求的增长&#xff0c;导致kubernetes集群测试环境的计算节点资源不够使用了&#xff0c;这时候就申请了几台服务器加入到kubernetes集群中&#xff0c;因为维护的kubernetes集群的对接华为了iscsi存储&#xff0c;通过storageclass组件来创建pvc存…

Vue3中的常见组件通信之插槽

Vue3中的常见组件通信之插槽 概述 ​ 在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refs、parent、provide、inject、pinia、slot等。不同的组件关系用不同的传递方式。常见的撘配形式如下表所示。 组件关系传递方式父传子1. props2. v-model3. $refs…