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

文章目录

设计模式

设计模式(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、提供全局访问点

懒汉模式

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

java 复制代码
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)模式。

java 复制代码
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 实例。

饿汉模式

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

javascript 复制代码
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中时就立即被创建。

java 复制代码
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种设计模式之一,但它是一种常用的设计思想。它提供了一个创建对象的接口,但由一个单独的类来决定实例化哪一个类。

  • 通常包含一个静态工厂方法。
  • 客户端不需要知道具体的产品类,只需要传递一个参数给工厂方法即可获得所需的产品实例。
  • 封装了创建对象的过程,降低了系统的耦合度。
  • 当产品种类增加时,需要修改工厂逻辑,违反开闭原则。
java 复制代码
// 面条接口
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设计模式之一,它定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

  • 包含一个抽象工厂角色和多个具体工厂角色。
  • 抽象工厂声明了工厂方法,该方法返回一个产品类型的对象。
  • 具体工厂实现了抽象工厂中的工厂方法,负责创建特定类型的产品。
  • 每次添加新产品时都需要添加新的具体工厂类,可能导致系统中类的数量增多。
java 复制代码
// 面条接口
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设计模式之一,它提供了一种方式,能够封装一组具有相关性的具体工厂类。每个具体工厂类都能创建一系列相关的产品。

  • 定义了一个创建产品族的接口,而每个具体工厂则负责创建一个完整的产品系列。
  • 抽象工厂接口声明了一组用于创建不同产品的工厂方法。
  • 具体工厂实现了抽象工厂接口,创建的是同一主题下的多种产品。
  • 增加新产品系列比较复杂,因为需要改变抽象工厂以及所有具体的工厂类;当产品族中产品变化较多时,可能会导致大量的具体工厂类。
java 复制代码
// 面条接口
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(具体装饰器)

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

代码示例

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

java 复制代码
// 抽象组件:面条
public interface Noodles {
    void desc();
}

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

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

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

java 复制代码
// 装饰器
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),而不需要修改现有的装饰器代码。

java 复制代码
// 具体装饰器:加牛肉
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()方法来表示加牛肉和加油泼辣子的动作。

java 复制代码
// 测试
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();
    }
}
tex 复制代码
输出: 这是兰州拉面 加牛肉 加油泼辣子

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

查看类图

代理模式(Proxy Pattern)

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

组成部分

1、Subject(主题接口)

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

2、RealSubject(真实主题)

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

3、Proxy(代理)

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

类型

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

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

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

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

代码示例

java 复制代码
// Subject 接口
public interface Noodles {
    void prepare();
}

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

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

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

java 复制代码
// 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()方法时会先检查库存。如果库存足够,它会调用真实对象的方法;如果库存不足,则不会调用并且给出相应的提示。

java 复制代码
// 测试
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();
        }
    }
}

查看类图

再举一例

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

java 复制代码
// 定义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();
    }
}
tex 复制代码
检查用户是否合法!合法
用户已通过身份验证,允许上网。
上网浏览信息!
记录上网活动: 用户 user 上网浏览信息。
检查用户是否合法!非法
用户未通过身份验证,禁止上网。

观察者模式(Observer Pattern)

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

组成部分

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

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

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

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

3、Observer(观察者)

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

4、ConcreteObserver(具体观察者)

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

代码示例

java 复制代码
// 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("重庆小面"); // 再次添加新面条
    }
}
tex 复制代码
新面条上架: 兰州拉面
张三 收到通知: 新面条上架 - 兰州拉面
李四 收到通知: 新面条上架 - 兰州拉面
新面条上架: 重庆小面
李四 收到通知: 新面条上架 - 重庆小面

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

再举一例

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

观察者主题对象

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

观察者订阅人对象

java 复制代码
public interface Observer {
    // 接收变动通知
    void update();
}

具体订阅人

java 复制代码
public static class RealObject implements Observer {
    @Override
    public void update() {
        System.out.println("接收到了通知");
    }
}

具体的某家奶厂

java 复制代码
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();
        }
    }
}

测试

java 复制代码
public static void main(String[] args) {
    Subject subject = new RealSubject(); // 创建奶厂
    Observer observer = new RealObject();// 创建订阅人
    subject.attach(observer);// 订阅subject奶厂
    subject.notifyChanged();// 奶厂发布消息 订阅者接收
}
相关推荐
代码代码快快显灵1 小时前
java之异常处理
java·开发语言
茶馆大橘1 小时前
Spring Validation —— 参数校验框架
java·后端·学习·spring
阿望要努力上研究生3 小时前
若依项目搭建(黑马经验)
java·redis·node.js·maven·管理系统
一只脑洞君3 小时前
Kubernetes(K8s)的简介
java·容器·kubernetes
zygswo3 小时前
程序猿成长之路之设计模式篇——设计模式简介
java·设计模式
Byron07073 小时前
JavaScript的设计模式
设计模式
除了代码啥也不会4 小时前
springboot项目发送邮件
java·spring boot·spring
无敌の星仔5 小时前
一个月学会Java 第7天 字符串与键盘输入
java·开发语言·python
GGBondlctrl5 小时前
【JavaEE初阶】多线程案列之定时器的使用和内部原码模拟
java·开发语言·定时器·timer的使用·定时器代码模拟