自学内容网 自学内容网

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

设计模式

设计模式(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);
    }

    @Override
    public void desc() {
        super.desc();
        addBeef();
    }

    private void addBeef() {
        System.out.println("加牛肉");
    }
}

// 具体装饰器:加油泼辣子
public class OilChiliDecorator extends NoodlesDecorator {
    public OilChiliDecorator(Noodles noodles) {
        super(noodles);
    }

    @Override
    public 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 {
    @Override
    public 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;
    }

    @Override
    public 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 {
    @Override
    public 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;
        }
    }

    // 代码实现上网
    @Override
    public 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;
    @Override
    public void registerObserver(NoodlesObserver observer) {
        observers.add(observer);
    }
    @Override
    public void removeObserver(NoodlesObserver observer) {
        observers.remove(observer);
    }
    @Override
    public 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;
    }
    @Override
    public 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 {
    @Override
    public void update() {
        System.out.println("接收到了通知");
    }
}

具体的某家奶厂

public static class RealSubject implements Subject{
    //本奶厂下订奶的人集合
    private List<Observer> observerList = new ArrayList<Observer>();
    //添加订阅者
    @Override
    public void attach(Observer observer) {
        observerList.add(observer);
    }
    //删除订阅者
    @Override
    public void detach(Observer observer) {
        observerList.remove(observer);
    }
    //消息通知订阅者
    @Override
    public 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();// 奶厂发布消息 订阅者接收
}

原文地址:https://blog.csdn.net/m0_66584716/article/details/142764606

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!