设计模式|观察者模式

什么是观察者模式

        观察者模式(Observer Pattern)是一种行为型(Behavioral Pattern)设计模式,它定义了对象之间的一种一对多的依赖关系,使得当一个对象改变状态时,所有依赖于它的对象都会得到通知并自动更新。这种模式通常用于实现分布式事件处理系统、消息发布/订阅(pub-sub)机制等。在观察者模式中,主要涉及到两个角色:

  1. 主题(Subject):即“被观察者”,它维护一组观察者,提供用于注册和删除观察者的接口,并且在其内部状态发生变化时负责通知所有注册的观察者。

  2. 观察者(Observer):提供一个更新接口,用于在主题的状态发生变化时得到通知。观察者通过这个接口得知主题的最新状态并进行相应的处理。

        观察者模式的关键点在于主题和观察者之间的解耦。观察者不需要知道主题的具体实现细节,主题也不需要知道观察者的具体实现细节。这种解耦使得系统中的各个组件可以独立地改变和复用,同时保持了它们之间的一致性。

        在实现观察者模式时,通常会有一个抽象的主题接口和一个抽象的观察者接口。具体的主题和观察者类则分别实现这些接口。当主题的状态发生变化时,它会调用观察者接口的更新方法,以通知所有注册的观察者。由此,一个简单的观察者模式的实现可能包含以下几个部分:

    1. 抽象主题(Subject Interface):Subject接口,定义注册、删除和通知观察者的方法。

    2. 具体主题(Concrete Subject):实现Subject接口,实现状态变化时通知观察者的逻辑。

    3. 抽象观察者(Observer Interface):Observer接口,定义更新接口,以便在得到主题的更改通知时更新自己。

    4. 具体观察者(Concrete Observer):实现Observer接口,根据主题的通知来更新自己的状态。

观察者模式的优缺点:

优点

  1. 松耦合:观察者模式使得主题和观察者之间的关系是松散的,它们彼此之间并不直接依赖,这样就可以更容易地对它们进行修改和扩展。\
  2. 支持广播通知:主题可以通知多个观察者,从而实现一对多的通知机制。\
  3. 可以轻松添加新的观察者:由于观察者模式使用了抽象的观察者接口,因此可以很容易地添加新的观察者,而无需修改主题的代码。
  4. 支持开放-封闭原则:可以在不修改主题或观察者的情况下,增加新的主题或观察者。

缺点

  1. 如果观察者之间有循环依赖,可能会导致系统的复杂性增加。
  2. 观察者模式可能会导致性能问题,特别是当观察者数量庞大时,每次通知都需要遍历所有观察者。
  3. 当一个观察者与主题的交互比较复杂时,可能会导致观察者之间的调用顺序难以理解。

如何使用观察者模式

        现在我们需要观测天气变化,如果下雨了,就要马上收衣服,如果不考虑任何设计模式,我们的代码大概是这样的: 

static class Weather{
    //是否下雨
    public static boolean isRaining = false;
}
static class Person{
    public void monitor(){
        while (!Weather.isRaining){
            //持续监控天气
        }
        //天气状态变了,下雨收衣服
    }
}

        在这段代码中,人需要持续地主动监控天气状态,有一个人需要在下雨时收衣服,就要有一个持续观测的while循环,有一百个人需要在下雨时收衣服,就要有一百个持续观测的while循环,显然,这会耗费大量的cpu资源,现在我们了解的观察者模式,就像是天气通过下雨的方式主动通知人们“下雨了”这一状态变化,而不再需要每个人持续不断地盯着天气是否下雨;代码即可优化为:

interface Observable{
    List<Observer> observers = new ArrayList<>();

    default void notifyObservers(){
        observers.forEach(Observer::update);
    }
    default Observable addObserver(Observer observer){
        this.observers.add(observer);
        return this;
    }
}

interface Observer{
    void update();
}

static class Weather implements Observable{
    //是否下雨
    public boolean isRaining = false;

    public void changeRainState(){
        isRaining = !isRaining;
        this.notifyObservers();
    }
}

static class Person implements Observer{
    @Override
    public void update() {
        //天气状态变了,下雨收衣服
    }
}

        在上述代码中,定义了两个接口以及两个实现类,分别对应着抽象主题Observable,具体主题Weather,抽象观察者Observer以及具体观察者Person,并使得天气在每次发生变化时主动通知观察者(notifyObservers),观察者只需要在触发了update时去收衣服即可,这样便实现了一个简单的观察者模式。

如何继续优化

        如何根据不同情况做出不同反应:

        在上面的示例代码中,天气变化了,就通知观察者收衣服,那天气由下雨变化为雨停了,此时观察者再去收衣服显然是不正确了,也就是说,此时观察者只知道天气变化了,而不知道是下雨了还是雨停了,也不知道雨下得有多大,无法根据不同的情况做出不同的反应,为了解决这个问题,我们可以将天气状态的所有变化情况封装到一个对象中,在通知观察者时,将对象作为参数一并传给观察者:

static class Event{
    //下雨状态
    boolean isRaining;
    //降雨量
    int rainfall;

    public Event(boolean isRaining, int rainfall) {
        this.isRaining = isRaining;
        this.rainfall = rainfall;
    }
}

interface Observable{
    List<Observer> observers = new ArrayList<>();

    default void notifyObservers(Event event){
        observers.forEach(observer -> observer.update(event));
    }
    default Observable addObserver(Observer observer){
        this.observers.add(observer);
        return this;
    }
}

interface Observer{
    void update(Event event);
}

static class Weather implements Observable{
    //是否下雨
    public boolean isRaining = false;

    public void changeRainState(){
        isRaining = !isRaining;
        this.notifyObservers(new Event(isRaining, 5));
    }
}
static class Person implements Observer{
    @Override
    public void update(Event event) {
        if(event.isRaining){
            if(event.rainfall<3){
                //雨下的很小,不用管
            }else{
                //雨下大了,收衣服
            }
        }else{
            //雨停了,晾衣服
        }
    }
}

        如何同时观察多个对象

        现在,观察者Person可以根据天气的不同变化做出不同的反应了,但是还是存在一些问题的,Person作为一个观察者,要观察的事情可不仅仅是天气的变化这么简单的,比如Person在观察天气变化的同时,还要观察电饭煲里的米饭有没有熟,那么update方法仅接受一个天气变化情况的Event对象显然是不足以满足Person要观察多个对象的需求的,此时我们可以直接将被观察者作为参数传给观察者,观察者即可根据事件来源和其中的变量做出不同的反应:       

interface Observable{
    List<Observer> observers = new ArrayList<>();

    default void notifyObservers(Observable source){
        observers.forEach(observer -> observer.update(source));
    }
    default Observable addObserver(Observer observer){
        this.observers.add(observer);
        return this;
    }
}

interface Observer{
    void update(Observable source);
}

/**
 * 被观察者--天气
 */
static class Weather implements Observable{
    //是否下雨
    public boolean isRaining = false;

    public void changeRainState(){
        isRaining = !isRaining;
        this.notifyObservers(this);
    }
}

/**
 * 被观察者--电饭煲
 */
static class RiceCooker implements Observable{
    public boolean isCooked = false;

    public void changeCookedState(){
        isCooked = true;
        this.notifyObservers(this);
    }
}

static class Person implements Observer{
    @Override
    public void update(Observable source) {
        if(source instanceof Weather){
            //天气变了
        }else if(source instanceof RiceCooker){
            //米饭熟了
        }
    }
}

JAVA自带的观察者接口

        其实Java本身是已经定义好了观察者接口和被观察者接口的,分别是java.util.Observer和java.util.Observable,以下是它们的源码,在前面了解了观察者模式的原理和简单的实现之后,下面的源码就很好理解了: 

总结

        观察者模式是一种非常有用的设计模式,特别适用于需要实现发布-订阅机制的场景,它能够帮助我们实现对象之间的松耦合,提高系统的灵活性和可维护性。同时,在使用观察者模式时,需要注意避免潜在的循环依赖和性能问题。

  • 23
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值