【设计模式】设计模式介绍和常见设计模式代码示例

文章目录

  • 设计模式分类
    • 创建型模式
    • 结构型模式
    • 行为型模式
  • 设计模式详解
    • 单例模式(Singleton Pattern)
      • 懒汉模式
      • 饿汉模式
    • 工厂模式(Factory Pattern)
      • 简单工厂模式
      • 工厂方法模式
      • 抽象工厂模式
    • 装饰模式(Decorator Pattern)
      • 主要角色
      • 代码示例
    • 代理模式(Proxy Pattern)
      • 组成部分
      • 代码示例
    • 观察者模式(Observer Pattern)
      • 组成部分
      • 代码示例

设计模式

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式的目的是为了可重用代码、让代码更容易被他人理解、保证代码的可靠性。其中,高可靠性使得系统易于扩展(即当用户需求变更时,只需要做较少的代码修改)。


设计模式分类

GoF(Gang of Four)是指四位软件工程领域的专家,他们共同撰写了《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)一书。这本书首次出版于1994年,它在软件工程领域产生了深远的影响,并且成为了面向对象设计模式的经典参考书籍。

这四位作者分别是:(膜拜一下大佬)

  • Erich Gamma:德国计算机科学家,也是Eclipse平台的创始人之一。
  • Richard Helm:澳大利亚的软件工程师和顾问。
  • Ralph Johnson:美国伊利诺伊大学厄巴纳-香槟分校的计算机科学教授。
  • John Vlissides:美国IBM的研究员,他在2005年不幸去世。

在他们的书中,GoF介绍了23种设计模式,这些模式被分为三大类。这些设计模式提供了解决特定问题的标准模板,通过使用这些设计模式,开发者可以提高代码的可重用性、可维护性和扩展性。GoF的设计模式已经成为软件开发中的重要工具,并且被广泛应用于各种编程语言和开发环境中。

在这里插入图片描述

创建型模式

工厂方法模式:一个工厂类根据传入的参数决定创建出那一种产品类的实例。

抽象工厂模式:创建相关或依赖对象的家族,而无需明确指定具体类。

单例模式:某个类只能有一个实例,提供一个全局的访问点。

建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。

原型模式:通过复制现有的实例来创建新的实例。

结构型模式

适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。

桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。

代理模式:为其他对象提供一个代理以便控制这个对象的访问。

组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。

装饰模式:动态的给对象添加新的功能。

亨元模式:通过共享技术来有效的支持大量细粒度的对象。

外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。

行为型模式

模板方式模式:定义一个算法结构,而将一些步骤延迟到子类实现。

解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。

职责链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。

命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。

迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

中介者模式:用一个中介对象来封装一系列的对象交互。

备忘录模式:在不破坏封装的前提下,保持对象的内部状态。

观察者模式:对象间的一对多的依赖关系。

状态模式:允许一个对象在其对象内部状态改变时改变它的行为。

策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。

访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。

设计模式详解

单例模式(Singleton Pattern)

单例模式,它的定义就是确保某一个类只有一个实例,并且提供一个全局访问点 。单例模式有两种常见的实现方式:懒汉模式(Lazy Initialization)和饿汉模式(Eager Initialization)。

单例模式具备典型的3个特点

1、只有一个实例

2、自我实例化

3、提供全局访问点

懒汉模式

懒汉模式是指在第一次使用时才创建单例对象。这种方式可以延迟对象的创建直到真正需要的时候,从而节省资源。但是,懒汉模式在多线程环境下可能会出现问题,因此需要进行适当的同步处理来保证线程安全。

public class UserMassage {// 创建静态对象private static UserMassage umsg = null;// 全局访问点 对外部提供一个公共的访问方法public synchronized static UserMassage getUserMassage(){if(umsg == null){umsg = new UserMassage();}return umsg;}// 普通方法public void show(){System.out.println("我是单例模式");}
}
// 测试
public static void main(String[] args) {UserMassage msg1 = UserMassage.getUserMassage();UserMassage msg2 = UserMassage.getUserMassage();msg1.show();msg2.show();System.out.println(msg1.equals(msg2)); // true 表示只创建了一次对象
}

UserMassage 类使用了同步方法来确保在多线程环境下的安全性。然而,这种方法会在每次调用 getUserMassage() 方法时都进行同步,这可能会导致性能问题。为了减少这种开销,可以使用双重检查锁定(Double-Checked Locking)模式。

public class UserMassage {// 创建静态对象private static volatile UserMassage umsg = null; // 使用volatile关键字// 私有构造函数防止外部直接实例化private UserMassage() {}// 全局访问点 对外部提供一个公共的访问方法public static UserMassage getUserMassage() {if (umsg == null) { // 第一次检查synchronized (UserMassage.class) { // 加锁if (umsg == null) { // 第二次检查umsg = new User Massage();}}}return umsg;}
}

使用 volatile 关键字来禁止指令重排序,确保当 umsg 变量被初始化成 UserMassage 实例时,多个线程能够正确处理 UserMassage 实例。

饿汉模式

饿汉模式是指在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。这种模式天生就是线程安全的,因为虚拟机保证了类加载过程中的线程安全性。

public class UserMassage {private static UserMassage umsg = new UserMassage();public static UserMassage getUserMassage(){return umsg;}...
}
// 测试
public static void main(String[] args) {UserMassage msg1 = UserMassage.getUserMassage();UserMassage msg2 = UserMassage.getUserMassage();System.out.println(msg1.equals(msg2)); // true
}

饿汉模式的另一种实现方式。通过静态初始化块(static block)来确保 userMassage 实例在 UserMassage 类第一次被加载到JVM中时就立即被创建。

public class UserMassage {public static UserMassage userMassage;static {userMassage = new UserMassage();}
}
public static void main(String[] args) {UserMassage msg1 = UserMassage.userMassage;UserMassage msg2 = UserMassage.userMassage;System.out.println(msg1 == msg2); // true
}

单例模式使用场景

1、要求生产唯一序列号

2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来

3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等

工厂模式(Factory Pattern)

在工厂模式(Factory Pattern)中,当创建对象时,我们不会直接使用new关键字来实例化对象,而是定义一个用于创建对象的接口,并让子类决定实例化哪一个类。这样做的目的是将对象的创建过程与具体的业务逻辑解耦合,提高代码的灵活性和可维护性。工厂模式主要分为三种类型:简单工厂模式(Simple Factory Pattern)、工厂方法模式(Factory Method Pattern)和抽象工厂模式(Abstract Factory Pattern)。

简单工厂模式

简单工厂模式不是GoF定义的23种设计模式之一,但它是一种常用的设计思想。它提供了一个创建对象的接口,但由一个单独的类来决定实例化哪一个类。

  • 通常包含一个静态工厂方法。
  • 客户端不需要知道具体的产品类,只需要传递一个参数给工厂方法即可获得所需的产品实例。
  • 封装了创建对象的过程,降低了系统的耦合度。
  • 当产品种类增加时,需要修改工厂逻辑,违反开闭原则。
// 面条接口
public interface MianTiao {void desc();
}// 兰州拉面
public class LzNoodles implements MianTiao {public void desc() {System.out.println("这是兰州拉面");}
}// 泡面
public class PaoNoodles implements MianTiao {public void desc() {System.out.println("这是泡面");}
}// 河南烩面
public class HuiNoodles implements MianTiao {public void desc() {System.out.println("这是河南烩面");}
}// 简单工厂类
public class SimpleNoodlesFactory {public static final int TYPE_LZ = 1; // 兰州拉面public static final int TYPE_PM = 2; // 泡面public static final int TYPE_HM = 3; // 河南烩面// 根据用户的选择创建不同的面public static MianTiao createNoodles(int type) {switch (type) {case TYPE_LZ:return new LzNoodles();case TYPE_PM:return new PaoNoodles();case TYPE_HM:default:return new HuiNoodles();}}
}// 测试
public class Main {public static void main(String[] args) {MianTiao mian = SimpleNoodlesFactory.createNoodles(SimpleNoodlesFactory.TYPE_LZ);mian.desc(); // 输出: 这是兰州拉面}
}

查看类图

在这里插入图片描述

工厂方法模式

工厂方法模式是GoF设计模式之一,它定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

  • 包含一个抽象工厂角色和多个具体工厂角色。
  • 抽象工厂声明了工厂方法,该方法返回一个产品类型的对象。
  • 具体工厂实现了抽象工厂中的工厂方法,负责创建特定类型的产品。
  • 每次添加新产品时都需要添加新的具体工厂类,可能导致系统中类的数量增多。
// 面条接口
public interface MianTiao {void desc();
}// 兰州拉面
public class LzNoodles implements MianTiao {public void desc() {System.out.println("这是兰州拉面");}
}// 泡面
public class PaoNoodles implements MianTiao {public void desc() {System.out.println("这是泡面");}
}// 抽象工厂接口
public interface NoodlesFactory {MianTiao createNoodles();
}// 兰州拉面工厂
public class LzNoodlesFactory implements NoodlesFactory {public MianTiao createNoodles() {return new LzNoodles();}
}// 泡面工厂
public class PaoNoodlesFactory implements NoodlesFactory {public MianTiao createNoodles() {return new PaoNoodles();}
}// 测试
public class Main {public static void main(String[] args) {NoodlesFactory lzFactory = new LzNoodlesFactory();MianTiao lzNoodles = lzFactory.createNoodles();lzNoodles.desc(); // 输出: 这是兰州拉面}
}

查看类图

在这里插入图片描述

抽象工厂模式

抽象工厂模式也是GoF设计模式之一,它提供了一种方式,能够封装一组具有相关性的具体工厂类。每个具体工厂类都能创建一系列相关的产品。

  • 定义了一个创建产品族的接口,而每个具体工厂则负责创建一个完整的产品系列。
  • 抽象工厂接口声明了一组用于创建不同产品的工厂方法。
  • 具体工厂实现了抽象工厂接口,创建的是同一主题下的多种产品。
  • 增加新产品系列比较复杂,因为需要改变抽象工厂以及所有具体的工厂类;当产品族中产品变化较多时,可能会导致大量的具体工厂类。
// 面条接口
public interface MianTiao {void desc();
}// 调料接口
public interface Sauce {void desc();
}// 兰州拉面及其调料
public class LzNoodles implements MianTiao {public void desc() {System.out.println("这是兰州拉面");}
}public class LzSauce implements Sauce {public void desc() {System.out.println("这是兰州拉面的调料");}
}// 泡面及其调料
public class PaoNoodles implements MianTiao {public void desc() {System.out.println("这是泡面");}
}public class PaoSauce implements Sauce {public void desc() {System.out.println("这是泡面的调料");}
}// 抽象工厂接口
public interface NoodlesFactory {MianTiao createNoodles();Sauce createSauce();
}// 兰州拉面工厂
public class LzNoodlesFactory implements NoodlesFactory {public MianTiao createNoodles() {return new LzNoodles();}public Sauce createSauce() {return new LzSauce();}
}// 泡面工厂
public class PaoNoodlesFactory implements NoodlesFactory {public MianTiao createNoodles() {return new PaoNoodles();}public Sauce createSauce() {return new PaoSauce();}
}// 测试
public class Main {public static void main(String[] args) {NoodlesFactory lzFactory = new LzNoodlesFactory();MianTiao lzNoodles = lzFactory.createNoodles();Sauce lzSauce = lzFactory.createSauce();lzNoodles.desc(); // 输出: 这是兰州拉面lzSauce.desc();   // 输出: 这是兰州拉面的调料}
}

查看类图

在这里插入图片描述

装饰模式(Decorator Pattern)

装饰模式是用来替代继承的一种设计模式。它允许动态地向一个对象添加新的功能,而无需修改其结构。这种模式创建了一个装饰类,用来包装原有的类,并且可以在保持原有类行为的同时,增加额外的行为或状态。

主要角色

1、Component(抽象组件)

  • 定义了可以被装饰的基本行为接口。
  • 可以是抽象类或者是接口。

2、ConcreteComponent(具体组件)

  • 实现了抽象组件定义的接口,提供基本的功能实现。

3、Decorator(装饰器)

  • 也是抽象组件的一个子类,用于给具体组件增加职责。
  • 通常会持有一个对抽象组件的引用。

4、ConcreteDecorator(具体装饰器)

  • 继承自装饰器类,并实现了具体的装饰逻辑。
  • 可以有多个具体装饰器来添加不同的功能。

代码示例

仍以上述面条案例为基础,假设我们有一个基础的面条类,现需要为面条添加调料和配料等额外的功能,但又不希望修改原有的面条类。我们可以使用装饰模式来实现这一点。

// 抽象组件:面条
public interface Noodles {void desc();
}

定义了一个名为Noodles的接口,其中包含一个抽象方法desc(),该方法将被用来描述面条。

// 具体组件:兰州拉面
public class LzNoodles implements Noodles {public void desc() {System.out.println("这是兰州拉面");}
}

LzNoodles是一个实现了Noodles接口的具体类,代表了具体的面条种类——兰州拉面。

// 装饰器
public abstract class NoodlesDecorator implements Noodles {protected Noodles noodles;// 构造函数接收一个面条实例public NoodlesDecorator(Noodles noodles) {this.noodles = noodles;}// 调用面条的描述方法public void desc() {noodles.desc();}
}

NoodlesDecorator是一个抽象类,也实现了Noodles接口。它持有一个Noodles类型的成员变量noodles,并在构造函数中初始化这个变量。

通过让NoodlesDecorator实现Noodles接口,我们可以确保装饰器可以用来装饰任何实现了Noodles接口的具体面条类型,而不仅仅是LzNoodles。这意味着你可以很容易地添加新的面条种类(如 PaoNoodles HuiNoodles),而不需要修改现有的装饰器代码。

// 具体装饰器:加牛肉
public class BeefDecorator extends NoodlesDecorator {public BeefDecorator(Noodles noodles) {super(noodles);}@Overridepublic void desc() {super.desc();addBeef();}private void addBeef() {System.out.println("加牛肉");}
}// 具体装饰器:加油泼辣子
public class OilChiliDecorator extends NoodlesDecorator {public OilChiliDecorator(Noodles noodles) {super(noodles);}@Overridepublic void desc() {super.desc();addOilChili();}private void addOilChili() {System.out.println("加油泼辣子");}
}

BeefDecoratorOilChiliDecorator 都继承自NoodlesDecorator,分别添加了加牛肉和加油泼辣子的行为。在 desc() 方法中,除了调用父类的方法外,还调用了addBeef() addOilChili()方法来表示加牛肉和加油泼辣子的动作。

// 测试
public class Main {public static void main(String[] args) {// 创建兰州拉面实例Noodles lzNoodles = new LzNoodles();// 用加牛肉装饰兰州拉面Noodles beefLzNoodles = new BeefDecorator(lzNoodles);// 再用加油泼辣子装饰加了牛肉的兰州拉面Noodles oilChiliBeefLzNoodles = new OilChiliDecorator(beefLzNoodles);oilChiliBeefLzNoodles.desc();}
}
输出: 这是兰州拉面 加牛肉 加油泼辣子

在这个例子中,LzNoodles 是原始的面条类,BeefDecoratorOilChiliDecorator 是两个装饰器,它们都继承自 NoodlesDecorator 并实现了额外的功能。通过这种方式,我们可以动态地为面条添加多种调料,而不需要改变面条类本身。

查看类图
在这里插入图片描述

代理模式(Proxy Pattern)

代理模式允许你提供一个代理对象来控制对另一个对象的访问。代理模式的主要目的是在直接访问对象时增加一层间接性,以便于执行额外的操作或限制某些功能。

组成部分

1、Subject(主题接口)

  • 定义了RealSubject和Proxy共同实现的接口,这样Proxy就可以替代RealSubject。

2、RealSubject(真实主题)

  • 实现了Subject接口,是真正的业务逻辑执行者。

3、Proxy(代理)

  • 也实现了Subject接口,并持有一个对RealSubject的引用。
  • Proxy通常负责创建和管理RealSubject的生命周期,并且在转发请求给RealSubject之前或之后进行额外处理。

类型

虚拟代理:当对象的创建成本很高时,可以用轻量级的代理代替实际对象,直到真正需要的时候才创建实际对象。

远程代理:为位于不同地址空间的对象提供本地代表。

保护代理:基于调用者的权限控制对目标对象的访问。

智能引用:当对象被引用时,做一些额外的事情,比如计数器增加。

代码示例

// Subject 接口
public interface Noodles {void prepare();
}

Noodles接口定义了所有面条类型都必须实现的prepare()方法。

// RealSubject 类
public class LzNoodles implements Noodles {@Overridepublic void prepare() {System.out.println("正在准备兰州拉面...");}
}

LzNoodles是实现了Noodles接口的真实主题(RealSubject),它负责准备面条的实际工作。

// Proxy 类
public class LzNoodlesProxy implements Noodles {private final Noodles lzNoodles;private int stock = 10; // 假设初始库存为10份public LzNoodlesProxy(Noodles lzNoodles) {this.lzNoodles = lzNoodles;}@Overridepublic void prepare() {if (stock > 0) {System.out.println("库存充足,开始准备面条。");lzNoodles.prepare(); // 调用真实对象的方法stock--; // 减少库存System.out.println("剩余库存: " + stock);} else {System.out.println("库存不足,无法准备面条!");}}
}

LzNoodlesProxy是代理类(Proxy),它也实现了Noodles接口。这个代理类持有一个LzNoodles实例,并且在调用prepare()方法时会先检查库存。如果库存足够,它会调用真实对象的方法;如果库存不足,则不会调用并且给出相应的提示。

// 测试
public class Main {public static void main(String[] args) {Noodles lzNoodles = new LzNoodles();Noodles proxy = new LzNoodlesProxy(lzNoodles);// 第一次尝试准备面条proxy.prepare();// 继续尝试直到库存耗尽for (int i = 0; i < 20; i++) {proxy.prepare();}}
}

查看类图

在这里插入图片描述

再举一例

通过代理模式实现一个简单的网络浏览控制功能。包括身份验证,访问控制和日志记录。

// 定义Network接口
public interface Network {public abstract void browse(); // 定义浏览的抽象方法
}// 真实的上网操作
public class RealNetwork implements Network {@Overridepublic void browse() {System.out.println("上网浏览信息!");}
}// 代理上网
public class NetworkProxy implements Network {private final Network realNetwork;private final String username;private final String password;// 设置代理的真实操作,并传递用户名和密码public NetworkProxy(Network realNetwork, String username, String password) {this.realNetwork = realNetwork; // 设置代理的子类this.username = username;this.password = password;}// 身份验证操作private boolean checkCredentials() {// 假设合法的用户名和密码if ("user".equals(username) && "pass".equals(password)) {System.out.println("检查用户是否合法!合法");return true;} else {System.out.println("检查用户是否合法!非法");return false;}}// 代码实现上网@Overridepublic void browse() {if (checkCredentials()) {System.out.println("用户已通过身份验证,允许上网。");realNetwork.browse(); // 调用真实的上网操作logActivity(); // 记录上网活动} else {System.err.println("用户未通过身份验证,禁止上网。");}}// 日志记录private void logActivity() {System.out.println("记录上网活动: 用户 " + username + " 上网浏览信息。");}
}// 测试
public class Main {public static void main(String[] args) {Network net = null; // 定义接口对象// 实例化代理,同时传入代理的真实操作以及用户名和密码net = new NetworkProxy(new RealNetwork(), "user", "pass");// 调用代理的上网操作net.browse();// 尝试使用错误的用户名和密码net = new NetworkProxy(new RealNetwork(), "invalid", "wrong");net.browse();}
}
检查用户是否合法!合法
用户已通过身份验证,允许上网。
上网浏览信息!
记录上网活动: 用户 user 上网浏览信息。
检查用户是否合法!非法
用户未通过身份验证,禁止上网。

观察者模式(Observer Pattern)

观察者模式允许定义一种订阅机制,可以在对象事件发生时通知多个“观察”该对象的其他对象。这种模式通常用于实现分布式的事件处理系统,如事件驱动的应用程序。

组成部分

1、Subject(主题/被观察者)

  • 通常是抽象类或接口,它包含添加、删除和通知观察者的操作。

2、ConcreteSubject(具体主题/具体的被观察者)

  • 实现了Subject接口,并维护了一个观察者列表。状态发生变化时,它会通知所有的观察者。

3、Observer(观察者)

  • 抽象类或接口,定义了更新的方法,以便在主题状态变化时进行更新。

4、ConcreteObserver(具体观察者)

  • 实现了Observer接口,并维护与具体主题的关系。当接收到通知时,它会更新自己的状态。

代码示例

// Observer 接口 观察者订阅人对象
interface NoodlesObserver {void update(String noodlesType);
}// Subject 接口 被观察者主题对象
interface NoodlesSubject {void registerObserver(NoodlesObserver observer);void removeObserver(NoodlesObserver observer);void notifyObservers();void addNewNoodles(String noodlesType);
}// ConcreteSubject 类 具体的某家面馆
class NoodlesShop implements NoodlesSubject {// 本面馆关注者集合private List<NoodlesObserver> observers = new ArrayList<>();private String latestNoodles;@Overridepublic void registerObserver(NoodlesObserver observer) {observers.add(observer);}@Overridepublic void removeObserver(NoodlesObserver observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (NoodlesObserver observer : observers) {observer.update(latestNoodles);}}public void addNewNoodles(String noodlesType) {this.latestNoodles = noodlesType;System.out.println("新面条上架: " + noodlesType);notifyObservers(); // 通知所有观察者}
}// ConcreteObserver 类 具体订阅人
class Customer implements NoodlesObserver {private String name;public Customer(String name) {this.name = name;}@Overridepublic void update(String noodlesType) {System.out.println(name + " 收到通知: 新面条上架 - " + noodlesType);}
}
// 测试
public class Main {public static void main(String[] args) {NoodlesSubject shop = new NoodlesShop();// 创建一些顾客 NoodlesObserver customer1 = new Customer("张三");NoodlesObserver customer2 = new Customer("李四");// 注册顾客shop.registerObserver(customer1);shop.registerObserver(customer2);shop.addNewNoodles("兰州拉面"); // 添加新面条shop.removeObserver(customer1); // 移除一个顾客shop.addNewNoodles("重庆小面"); // 再次添加新面条}
}
新面条上架: 兰州拉面
张三 收到通知: 新面条上架 - 兰州拉面
李四 收到通知: 新面条上架 - 兰州拉面
新面条上架: 重庆小面
李四 收到通知: 新面条上架 - 重庆小面

观察者模式有一个别名叫“发布-订阅模式”,或者说是“订阅-发布模式”,订阅者和订阅目标是联系在一起的,当订阅目标发生改变时,逐个通知订阅者。我们可以用报纸期刊的订阅来形象的说明,当你订阅了一份报纸,每天都会有一份最新的报纸送到你手上,有多少人订阅报纸,报社就会发多少份报纸,报社和订报纸的客户就是上面文章开头所说的“一对多”的依赖关系。

再举一例

我们按照定牛奶的方式来理解, Subject 实际上可以理解成奶厂, Observer 可以理解成为我们每个用户,而观察者模式就是在 Subject 发生变化的时候,去通知每一个 Observer 对象,以达到消息通知目的。

观察者主题对象

public interface Subject {// 订阅操作void attach(Observer observer);// 取消订阅操作void detach(Observer observer);// 通知变动void notifyChanged();
}

观察者订阅人对象

public interface Observer {// 接收变动通知void update();
}

具体订阅人

public static class RealObject implements Observer {@Overridepublic void update() {System.out.println("接收到了通知");}
}

具体的某家奶厂

public static class RealSubject implements Subject{//本奶厂下订奶的人集合private List<Observer> observerList = new ArrayList<Observer>();//添加订阅者@Overridepublic void attach(Observer observer) {observerList.add(observer);}//删除订阅者@Overridepublic void detach(Observer observer) {observerList.remove(observer);}//消息通知订阅者@Overridepublic void notifyChanged() {for (Observer observer : observerList) {observer.update();}}
}

测试

public static void main(String[] args) {Subject subject = new RealSubject(); // 创建奶厂Observer observer = new RealObject();// 创建订阅人subject.attach(observer);// 订阅subject奶厂subject.notifyChanged();// 奶厂发布消息 订阅者接收
}

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

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

相关文章

C#多线程基本使用和探讨

线程是并发编程的基础概念之一。在现代应用程序中&#xff0c;我们通常需要执行多个任务并行处理&#xff0c;以提高性能。C# 提供了多种并发编程工具&#xff0c;如Thread、Task、异步编程和Parallel等。 Thread 类 Thread 类是最基本的线程实现方法。使用Thread类&#xff0…

论文阅读笔记-XLNet: Generalized Autoregressive Pretraining for Language Understanding

前言 Google发布的XLNet在问答、文本分类、自然语言理解等任务上都大幅超越BERT,XLNet提出一个框架来连接语言建模方法和预训练方法。我们所熟悉的BERT是denoising autoencoding模型,最大的亮点就是能够获取上下文相关的双向特征表示,所以相对于标准语言模型(自回归)的预…

【基础算法总结】字符串篇

目录 一&#xff0c;算法简介二&#xff0c;算法原理和代码实现14.最长公共前缀5.最长回文子串67.二进制求和43.字符串相乘 三&#xff0c;算法总结 一&#xff0c;算法简介 字符串 string 是一种数据结构&#xff0c;它一般和其他的算法结合在一起操作&#xff0c;比如和模拟&…

【算法笔记】二分算法原理的深度剖析

【算法笔记】二分算法原理的深度剖析 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;算法笔记 文章目录 【算法笔记】二分算法原理的深度剖析前言一.二分查找1.1题目1.2朴素二分1.3细节问题1.4代码实现1.5朴素模版总结 二.在排序数组中查找…

Rust编程的匹配控制语句match

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust编程与项目实战_夏天又到了的博客-CSDN博客 学过C语言的同学或许在等switch&#xff0c;明确告诉你们&#xff0c;Rust没有switc…

Jenkins打包,发布,部署

一、概念 Jenkins是一个开源的持续集成工具&#xff0c;主要用于自动构建和测试软件项目&#xff0c;以及监控外部任务的运行。与版本管理工具&#xff08;如SVN&#xff0c;GIT&#xff09;和构建工具&#xff08;如Maven&#xff0c;Ant&#xff0c;Gradle&#xff09;结合使…

Android Studio实现安卓心理健康咨询

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动 项目代号161 1.开发环境 android stuido3.6 jdk1.8 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.心理测评 3.测评结果 4.心理咨询预约 5.心理综合辅导 6.个人中心 7.历史咨…

知识图谱入门——11:构建动态图谱渲染应用:Vue3与Neo4j的集成与实践

在知识图谱与大数据技术领域&#xff0c;构建动态图谱是一项非常重要的任务。这篇博客将带你深入了解如何利用Vue.js、D3.js以及Neo4j&#xff0c;开发一个能够实时渲染图谱节点和关系的应用。我们将从零开始&#xff0c;介绍如何搭建开发环境、安装依赖、与Neo4j数据库交互、到…

考研笔记之操作系统(三)- 存储管理

操作系统&#xff08;三&#xff09;- 存储管理 1. 内存的基础知识1.1 存储单元与内存地址1.2 按字节编址和按字编址1.3 指令1.4 物理地址和逻辑地址1.5 从写程序到程序运行1.6 链接1.6.1 静态链接1.6.2 装入时动态链接1.6.3 运行时动态链接 1.7 装入1.7.1 概念1.7.2 绝对装入1…

分支预测器BPU

分支预测器BPU 0 Intro0.1 CPU执行过程0.2 分支预测0.2.1 TAGE预测器0.2.2 跳转地址 分支预测器BPU是深入研究一个高性能处理器的一个很好的开始项目&#xff1b; 0 Intro 条件分支是指后续具有两路可执行的分支。可以分为跳转分支(taken branch)和不跳转分支(not-taken branc…

ES创建文档,使用postman调用请求

请求的url 地址是http://192.168.1.108:9200/shopping/_doc&#xff0c;请求方式为post, 请求参数为: { "title":"小米手机", "category":"小米", "images":"http://www.gulixueyuan.com/xm.jpg", "price&…

IDEA 编译报错 “java: 常量字符串过长” 的解决办法

目录 一、问题描述二、问题原因2.1 理论角度2.2 源码角度 三、解决方案解决方案①&#xff1a;StringBuilder 拼接解决方案②&#xff1a;读取文件内容 四、方案验证 在线文本换行工具&#xff1a; https://lzltool.cn/Toolkit/WrapWordsInText 一、问题描述 今天在开发过程中…

CPU、GPU、显卡

CPU VS GPUCPU&#xff08;Central Processing Unit&#xff09;&#xff0c;中央处理器GPU&#xff08;Graphics Processing Unit&#xff09;&#xff0c;图形处理单元GPU 的技术演变CUDA&#xff08;Compute Unified Device Architecture&#xff09; 显卡&#xff08;Video…

016 规格参数

文章目录 新增AttrController.javaAttrVo.javaAttrServiceImpl.javaAttrAttrgroupRelationEntity.javaAttrEntity.javaAttrGroupEntity.java 查询AttrController.javaAttrServiceImpl.javaAttrRespVo.java 修改回显AttrController.javaAttrServiceImpl.java 修改提交AttrContro…

Word 插入表格的具体步骤图解

Word 是工作和学习中比较常用的软件之一&#xff0c;有时候在使用的过程中可能需要插入一个表格来整理一些数据&#xff0c;但是有的人可能不知道如何插入表格&#xff0c;下面就给大家总结了 Word 怎么插入表格。 Word 插入表格 Word 插入表格之后可以在里面填写数据和文本&…

时序约束进阶四:set_input_delay和set_output_delay详解

目录 一、前言 二、set_input_delay/set_output_delay 2.1 延时约束 2.2 约束设置界面 2.3 示例工程 2.4 Delay Value 2.5 Delay value is relative to clock edge 2.6 Delay value already includes latencies of the specified clock edge 2.7 Rise/Fall 2.8 Max/M…

更新C语言题目

1.以下程序输出结果是() int main() {int a 1, b 2, c 2, t;while (a < b < c) {t a;a b;b t;c--;}printf("%d %d %d", a, b, c); } 解析:a1 b2 c2 a<b 成立 ,等于一个真值1 1<2 执行循环体 t被赋值为1 a被赋值2 b赋值1 c-- c变成1 a<b 不成立…

使用IOT-Tree Server制作一个边缘计算设备(Arm Linux)

最近实现了一个小项目&#xff0c;现场有多个不同厂家的设备&#xff0c;用户需要对此进行简单的整合&#xff0c;并实现一些联动控制。 我使用了IOT-Tree Server这个软件轻松实现了&#xff0c;不外乎有如下过程&#xff1a; 1&#xff09;使用Modbus协议对接现有设备&#…

9-贪心算法

PDF文档下载&#xff1a;LeetCode-贪心算法-java 参考&#xff1a;代码随想录 题目分类大纲如下&#xff1a; 贪心算法理论基础 什么是贪心&#xff1f; 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 贪心的套路&#xff08;什么时候用贪心&#xff…

C++ STL容器(五) —— priority_queue 底层剖析

这篇来讲下 priority_queue&#xff0c;其属于 STL 的容器适配器&#xff0c;容器适配器是在已有容器的基础上修改活泼限制某些数据接口以适应更特定的需求&#xff0c;比如 stack 栈使数据满足后进先出&#xff0c;queue 队列使数据满足先进先出&#xff0c;其都是在已有容器上…