文章目录
- 设计模式分类
- 创建型模式
- 结构型模式
- 行为型模式
- 设计模式详解
- 单例模式(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("加油泼辣子");}
}
BeefDecorator
和 OilChiliDecorator
都继承自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
是原始的面条类,BeefDecorator
和 OilChiliDecorator
是两个装饰器,它们都继承自 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();// 奶厂发布消息 订阅者接收
}