面向对象编程内功心法系列十三(聊一聊观察者模式)

30 阅读6分钟

1.引子

不知不觉,这是我们面向对象编程内功心法系列的第十三篇文章了。这一篇我将给你分享观察者设计模式,它是行为型设计模式中的一种

前面通过六篇文章,我分别给你分享了创建型设计模式中的单例、工厂、建造者设计模式,与结构型设计模式中的代理、装饰器、适配器设计模式。今天我们开始行为型设计模式的分享。

在这里,不知道你是否思考过这么一个问题,设计模式分类为创建型、结构型、行为型,都是基于什么考虑呢

让我们一起来理一理吧。

我们说在实际项目中,应用设计模式最大的收益是实现解耦,从而获得项目更好的扩展性与灵活性。那么创建型、结构型、行为型是如何去分类的呢?

  • 创建型,解决对象的创建问题,将对象的创建与使用解耦
  • 结构型,解决了对象的组装问题,将对象的功能结构解耦
  • 行为型,解决了对象的交互问题,将对象的行为解耦

你可以仔细琢磨一下设计模式不同分类的关注点。在创建型设计模式中,主要关注对象的创建,比如说单例、工厂、建造者设计模式;在结构型设计模式中,主要关注对象之间的组装,比如说代理、装饰器、适配器设计模式;在行为型设计模式中,主要关注对象之间的交互,比如说观察者、模板、策略、责任链设计模式。

那么接下来,让我们一起来看行为型设计模式中的观察者设计模式。观察者设计模式在实际项目中,应用非常普遍。小到代码层面,大到架构层面,甚至产品设计都能有它的用武之地。举几个例子

  • 电商降价通知机制实现
  • 用户注册送积分,发放优惠券功能需求实现
  • 发布-订阅机制实现
  • 生产者-消费者编程模型实现

本篇文章,我将通过模拟电商降价通知机制实现案例,给你分享如何完整的通过自定义的方式实现观察者模式,以及借助jdk提供的接口来实现观察者模式。让我们开始吧

2.案例

当今我们的购物习惯,早已从传统线下购物,转变到了线上购物。你是不是经常会到各大电商网站平台看一看喜欢的商品,关注它什么时候降价,并且留下手机号码点击降价通知。

今天我们就通过观察者模式来实现这个案例。观察者设计模式代码实现非常简单,容易理解,你主要需要关注的点有

  • 有哪些角色?观察者、被观察者
  • 观察者中,需要有处理更新通知的能力
  • 被观察者中,需要有注册观察者、移除观察者、通知观察者的能力

关于这几个关注点,如果你暂时理解起来有一些困难,那不要紧,等案例实现以后,你再对照着代码一起来领会,相信你一定会有所收获。

2.1.自定义实现观察者模式

2.1.1.观察者接口

/**
 * 自定义实现观察者模式案例:观察者接口
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/3/21 8:02
 */
public interface Observer {
​
    /**
     * 处理更新通知
     * @param o
     * @param msg
     */
    void handle(Observable o, Object msg);
}

2.1.2.被观察者接口

/**
 * 自定义实现观察者模式案例:被观察者接口
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/3/21 8:00
 */
public interface Observable {
​
    /**
     * 注册观察者
     * @param observer
     */
    void registerObserver(Observer observer);
​
    /**
     * 移除观察者
     * @param observer
     */
    void removeObserver(Observer observer);
​
    /**
     * 通知观察者
     * @param msg
     */
    void notifyObservers(Object msg);
}

2.1.3.观察者具体实现

/**
 * 观察者设计模式案例:观察者实现
 *业务场景描述:电商降价通知
 * @author ThinkPad
 * @version 1.0
 * @date 2021/3/21 8:12
 */
public class ProductPriceObserverBySelf implements Observer{
​
    /**
     * 处理更新通知
     * @param o
     * @param msg
     */
    @Override
    public void handle(Observable o, Object msg) {
        // 商品对象
        ProductBySelf product = (ProductBySelf)o;
​
        // 输出消息
        System.out.println(msg);
        // 输出商品信息
        // 输出价格变化
        System.out.println("ProductPriceObserver观察到商品【"
                + product.getName() + "】价格更新为【" + product.getPrice() + "】.");
​
    }
}

2.1.4.被观察者具体实现

/**
 * 自定义实现观察者模式案例:被观察者接口实现
 *业务场景描述:电商降价通知
 * @author ThinkPad
 * @version 1.0
 * @date 2021/3/21 8:07
 */
public class ProductBySelf implements Observable {
​
    /**
     * 观察者集合
     */
    private Vector<Observer> vec = new Vector<>();
​
    // 商品名称
    private String name;
    // 商品价格
    private float price;
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public float getPrice() {
        return price;
    }
​
    /**
     * 重点关注:当设置价格,即价格发生变化后,发出价格变更通知
     * @param price
     */
    public void setPrice(float price) {
        this.price = price;
        // 发出价格更新通知
        notifyObservers("价格发生了变化!");
    }
​
    /**
     * 注册观察者
     * @param observer
     */
    @Override
    public void registerObserver(Observer observer) {
        vec.add(observer);
    }
​
    /**
     * 移除观察者
     * @param observer
     */
    @Override
    public void removeObserver(Observer observer) {
      vec.remove(observer);
    }
​
    /**
     * 通知观察者
     * @param msg
     */
    @Override
    public void notifyObservers(Object msg) {
         for(Observer observer : vec){
             observer.handle(this, msg);
         }
    }
}

2.1.5.使用案例

 public static void main(String[] args) {
        // 创建观察者对象
        Observer observer = new ProductPriceObserverBySelf();
​
        // 创建被观察者对象(商品)
        ProductBySelf product = new ProductBySelf();
        product.setName("苹果手机");
​
        // 注册观察者
        product.registerObserver(observer);
​
        // 更新商品价格
        product.setPrice(99.0f);
}
​
#执行结果
价格发生了变化!
ProductPriceObserver观察到商品【苹果手机】价格更新为【99.0】.
​
Process finished with exit code 0    

2.2.基于jdk提供的接口实现观察者模式

在定义观察者模式实现案例中,我们需要定义观察者,与被观察者接口。事实上jdk给我们提供了观察者接口java.util.Observer,被观察者接口java.util.Observable。让我们在实际项目中,更加容易实现观察者模式。

2.2.1.观察者具体实现

/**
 * 观察者设计模式案例:基于jdk提供的观察者实现
 *业务场景描述:电商降价通知
 * @author ThinkPad
 * @version 1.0
 * @date 2021/3/21 7:49
 */
public class ProductPriceObserverByJdk implements Observer{
​
    @Override
    public void update(Observable o, Object arg) {
        // 商品,与商品价格
        ProductByJdk product = (ProductByJdk)o;
        Float price = ((Float) arg).floatValue();
​
        // 输出价格变化
        System.out.println("ProductPriceObserver观察到商品【"
                + product.getName() + "】价格更新为【" + price + "】.");
    }
}

2.2.2.被观察者具体实现

/**
 * 观察者设计模式案例:基于jdk提供的被观察者实现
 * 业务场景描述:电商降价通知
 * @author ThinkPad
 * @version 1.0
 * @date 2021/3/21 7:45
 */
public class ProductByJdk extends Observable{
​
    // 商品名称
    private String name;
    // 商品价格
    private float price;
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public float getPrice() {
        return price;
    }
​
    /**
     * 重点关注:当设置价格,即价格发生变化后,发出价格变更通知
     * @param price
     */
    public void setPrice(float price) {
        this.price = price;
        // 设置变化点
        setChanged();
        // 通知观察者
        notifyObservers(new Float(price));
    }
}

2.2.3.使用案例

 public static void main(String[] args) {
        // 创建观察者对象
        ProductPriceObserverByJdk observer = new ProductPriceObserverByJdk();
​
        // 创建被观察者(商品对象)
        ProductByJdk product = new ProductByJdk();
        // 添加观察者
        product.addObserver(observer);
        // 设置商品名称
        product.setName("苹果手机");
        // 设置商品价格(触发观察-被观察行为的执行)
        product.setPrice(99.0f);
​
​
 }
​
#执行结果
ProductPriceObserver观察到商品【苹果手机】价格更新为【99.0】.
​
Process finished with exit code 0