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

文章目录

  • 设计模式分类
    • 创建型模式
    • 结构型模式
    • 行为型模式
  • 设计模式详解
    • 单例模式(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.mzph.cn/web/54595.shtml

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

相关文章

设计模式的学习

OO:Object-Oriented 面向对象 --- 《Head First设计模式》 这本书是用java写的&#xff0c;我是写C的&#xff0c;用C来写相关的代码 --- p2&#xff08;第二页&#xff09; #ifndef DUCK_H #define DUCK_H/*** brief The Duck class 鸭子类*/ class Duck { public:D…

No.8 笔记 | SQL 查询语句:数据探索的钥匙

2024/10/7 心记 - 致在路上默默奋斗的你 在当今数字化的时代&#xff0c;网络安全已成为我们生活中不可或缺的一部分。它如同守护数字世界的隐形盾牌&#xff0c;保护着我们的隐私、数据和整个社会的稳定运行。 学习网络安全&#xff0c;是踏上一段充满挑战与机遇的征程。 每一…

软件设计师(软考学习)

数据库技术 数据库基础知识 1. 数据库中的简单属性、多值属性、复合属性、派生属性简单属性&#xff1a;指不能够再分解成更小部分的属性&#xff0c;通常是数据表中的一个列。例如学生表中的“学号”、“姓名”等均为简单属性。 多值属性&#xff1a;指一个属性可以有多个值…

【网络原理】面试高频考点!!TCP协议“三次握手,四次挥手”,保姆级详解,建议收藏!

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;计算机网络那些事 通过上篇文章&#xff0c;我们可以得知TCP通过 “确认应答” 和 “超时重传”机制相辅相成&#xff0c;共同构建了 TCP的“可靠传输机制”。而为了保障建立通信和断开通信的可靠性…

解决磁盘负载不均——ElasticSearch 分片分配和路由设置

ES 分片分配&#xff08;Shard Allocation&#xff09;时间点&#xff1a; 初始恢复&#xff08;Initial Recovery&#xff09;副本分配&#xff08;Replica Allocation&#xff09;重平衡&#xff08;Rebalance&#xff09;节点添加或移除 小结&#xff1a; 准备移除节点时&a…

CAN转WiFi模块在仓库系统中应用

CAN转WiFi模块在仓库系统中应用 我们的LCWLAN设备在实际使用中以裸板的形式放在客户的智能总线控制器中&#xff0c;客户的智能总线刀片灯&#xff0c;柔性灯货架&#xff0c;柔性感应钢网柜以及智能电子料架等设备都是接到总线控制器中&#xff0c;然后总控制器通过CAN总线和…

Qt-QSpacerItem布局相关控件(45)

目录 描述 属性 使用 控件小结 描述 使⽤布局管理器的时候,可能需要在控件之间,添加⼀段空⽩.就可以使⽤ QSpacerItem 来表⽰ 属性 width宽度height⾼度hData⽔平⽅向的 sizePolicy • QSizePolicy::Ignored : 忽略控件的尺⼨&#xff0c;不对布局产⽣影响。 • QSizePol…

免费高可用软件

高可用软件是指那些能够提供高可用性、高可靠性的软件&#xff0c;它们在各种应用场景下都能确保系统的稳定运行。以下是四款免费的高可用软件&#xff0c;它们在不同领域都表现出色&#xff0c;能够满足各种高可用性需求。 一、PanguHA PanguHA是一款专为Windows平台设计的双…

贪心算法c++

贪心算法C概述 一、贪心算法的基本概念 贪心算法&#xff08;Greedy Algorithm&#xff09;&#xff0c;又名贪婪法&#xff0c;是一种解决优化问题的常用算法。其基本思想是在问题的每个决策阶段&#xff0c;都选择当前看起来最优的选择&#xff0c;即贪心地做出局部最优的决…

网络通信——OSPF协议(基础篇)

这里基础是因为没有讲解OSPF中的具体算法过程&#xff0c;以及其中很多小细节。后续会更新。 目录 一.OSPF的基础信息 二.认识OSPF中的Router ID 三.OSPF中的三张表 四.OSPF中的度量方法&#xff08;计算开销值&#xff09; 五. OSPF选举DR和BDR&#xff08;就是这个区域…

音视频入门基础:FLV专题(9)——Script Tag简介

一、SCRIPTDATA 根据《video_file_format_spec_v10_1.pdf》第75页到76页&#xff0c;如果某个Tag的Tag header中的TagType值为18&#xff0c;表示该Tag为Script Tag&#xff08;脚本Tag&#xff0c;又称Data Tag、SCRIPTDATA tag&#xff09;。这时如果Filter的值不为1表示未加…

【Android】Jetpack组件之LifeCycle

引言 Lifecycle组件是Android Jetpack架构组件之一&#xff0c;它提供了一种方法来管理Android组件&#xff08;如Activity、Fragment和服务&#xff09;的生命周期。Lifecycle组件帮助你执行与生命周期相关联的操作&#xff0c;确保在适当的时间发生适当的事情&#xff0c;例…

深度学习项目----用LSTM模型预测股价(包含LSTM网络简介,代码数据均可下载)

前言 前几天在看论文&#xff0c;打算复现&#xff0c;论文用到了LSTM&#xff0c;故这一篇文章是小编学LSTM模型的学习笔记&#xff1b;LSTM感觉很复杂&#xff0c;但是结合代码构建神经网络&#xff0c;又感觉还行&#xff1b;本次学习的案例数据来源于GitHub&#xff0c;在…

越差越好?为什么简单反而赢了,这背后究竟有什么秘诀?

你有没有发现,软件界里那些最成功的产品,往往并不是最复杂、最强大的?我们用的很多东西,看起来功能普通,甚至有些粗糙,但就是这样简陋的设计,反而成了市场上的赢家。 也许你玩过Flappy Bird这个游戏:它的设计非常简单,玩家只需要点击屏幕让小鸟飞行,避开管道障碍。游…

Llama 3.2 智能代理开发教程

构建研究代理可能很复杂&#xff0c;但使用 LangChain 和 Ollama&#xff0c;它会变得更加简单和模块化。 在本教程中&#xff0c;我们将向你展示如何基于Llama 3.2创建一个研究代理&#xff0c;该代理可以路由查询、执行网络搜索并使用工作流和 LLM 的组合生成详细响应。最后…

出栈入栈次序匹配

学习栈的过程中&#xff0c;我们一定见过下面两题&#xff0c;在当时我们可能费尽心思才找出不可能的一个出栈序列&#xff0c;但是如果进栈元素很多&#xff0c;那么找出出栈序列时4&#xff0c;头发就要掉光了&#xff01;那么我们是否可以实现一串代码&#xff0c;来帮助我们…

MySQL 篇-深入了解视图、SQL 优化(主键优化、order by 优化、group by 优化、update 优化等)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 SQL 优化 1.1 SQL 优化 - 插入数据 1.2 SQL 优化 - 主键优化 1.2.1 页分裂 1.2.2 页合并 1.2.3 主键设计原则 1.3 SQL 优化 - order by 优化 1.3.1 单字段排序 1.…

重学SpringBoot3-集成Redis(五)之布隆过滤器

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-集成Redis&#xff08;五&#xff09;之布隆过滤器 1. 什么是布隆过滤器&#xff1f;基本概念适用场景 2. 使用 Redis 实现布隆过滤器项目依赖Redis 配置…

【python实操】python小程序之对象的属性操作

引言 python小程序之对象的属性操作 文章目录 引言一、对象的属性操作1.1 题目1.2 代码1.3 代码解释 二、思考2.1 添加属性2.2 获取属性 一、对象的属性操作 1.1 题目 给对象添加属性 1.2 代码 class Cat:# 在缩进中书写⽅法def eat(self):# self 会⾃动出现,暂不管print(f…

【前端开发入门】前端开发环境配置

目录 引言一、Vscode编辑器安装1. 软件下载2. 软件安装3. 插件安装 二、Nodejs环境安装及版本控制1. 安装内容2. 使用nvm安装2.1 软件下载并安装2.2 nvm基本指令2.3 nvm下载过慢导致超时解决 三、git安装及配置1. 软件下载2. 软件安装3. 基础配置 四、总结 引言 本系列教程旨在…