1

看图识模式

一天公司里面,boss出差去了,终于没人管了,大家都觉得很嗨皮。于是乎,同学A呼呼大睡、同学B看看电影,感叹生活美好呀~

clipboard.png

可是突然boss回公司了,发现这个场景,现场是血淋淋的....

clipboard.png

他们都不知道boss的状态,如果提前知道了,不就没事了吗。
那就在老板回来的时候找人通知我们一下。

解决方案

大家和前台的MM关系不错,平时买吃的都给她留一份,由于她在公司前台,可以及时发现boss回来,并且通知大家。嗯,这样不错~

clipboard.png

其实这就是一个观察者模式的应用。

  • 第一种,Boss就是通知者,显然大家并不希望被boss通知回来了

  • 第二种,前台MM变成了通知者,这样大家就有时间及时应对,这是最好的

模式动机

  • 建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应

  • 发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者。它们之间并不需要知道其他对象的具体是干什么的(对象之间低耦合)。

  • 可以根据需要增加和删除观察者,使得系统更易于扩展。

模式定义

根据以上特征,我们可以归纳出:

观察者模式: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并被自动更新。

结构图

clipboard.png

观察者模式包含如下角色:

  • Subject: 抽象观察目标

  • ConcreteSubject: 具体观察目标

  • Observer: 抽象观察者

  • ConcreteObserver: 具体观察者

应用示例

以上面的例子场景做代码设计:

抽象Subject

这里使用接口设计

#import <Foundation/Foundation.h>
@class Observer;

@protocol Subject <NSObject>

@required
@property (nonatomic, copy) NSString *status;

- (void)attach:(Observer *)ob;
- (void)detach:(Observer *)ob;
- (void)notify;

@end
具体观察目标Secretary
#import <Foundation/Foundation.h>
#import "Subject.h"

@interface Secretary : NSObject <Subject>

// 同事列表
@property (nonatomic, strong) NSMutableArray *observers;
@property (nonatomic, copy) NSString *status;

@end

#import "Secretary.h"
#import "Observer.h"

@implementation Secretary

- (void)attach:(Observer *)ob
{
    if (ob) {
        [self.observers addObject:ob];
    }
}

- (void)detach:(Observer *)ob
{
    if (ob) {
        [self.observers removeObject:ob];
    }
}

- (void)notify
{
    for (Observer *ob in self.observers) {
        [ob update];
    }
}

- (NSMutableArray *)observers
{
    if (!_observers) {
        _observers = [NSMutableArray array];
    }
    return _observers;
}

@end
抽象观察者Observer
#import <Foundation/Foundation.h>
#import "Subject.h"

@interface Observer : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, weak) id <Subject> subject; // strong在这里会引起循环引用

- (instancetype)initWithName:(NSString *)name subject:(id<Subject>)sb;

- (void)update;

@end

#import "Observer.h"

@implementation Observer

// OC中没有绝对的抽象类,我们可以把基类的方法进行实现
- (instancetype)initWithName:(NSString *)name subject:(id<Subject>)sb
{
    if(self = [super init])
    {
        self.name = name;
        self.subject = sb;
    }
    return self;
}

- (void)update
{
    // 抽象类默认空实现
}

@end
具体观察者

股票交易同事

#import "Observer.h"

@interface StockObserver : Observer

@end

#import "StockObserver.h"

@implementation StockObserver

- (void)update
{
    NSLog(@"%@ ,%@ 关闭股票行情", self.subject.status, self.name);
}

@end

睡觉同事

#import "Observer.h"

@interface SleepObserver : Observer

@end

#import "SleepObserver.h"

@implementation SleepObserver

- (void)update
{
    NSLog(@"%@ ,%@ 从睡梦中醒来", self.subject.status, self.name);
}

@end
客户端调用
        Secretary *secretary = [[Secretary alloc] init];
        
        SleepObserver *sleeper = [[SleepObserver alloc] initWithName:@"小王" subject:secretary];
        StockObserver *stocker = [[StockObserver alloc] initWithName:@"小李" subject:secretary];
        
        [secretary attach:sleeper];
        [secretary attach:stocker];
        
        secretary.status = @"Boss回来了!";
        [secretary notify];

运行结果:

clipboard.png

应用

MVC模式

MVC模式是一种架构模式,它包含三个角色:模型(Model),视图(View)和控制器(Controller)。观察者模式可以用来实现MVC模式,观察者模式中的观察目标就是MVC模式中的模型(Model),而观察者就是MVC中的视图(View),控制器(Controller)充当两者之间的中介者(Mediator)。当模型层的数据发生改变时,视图层将自动改变其显示内容。

拓展

将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性,我们不希望为了维护一致性而使各类紧密耦合,这样会给维护、拓展和重用都带来不便。
所以,观察者模式所做的工作就是解耦。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不影响另一边的变化。
这也是依赖倒转原则最佳体现。

Cocoa Touch框架中使用观察者模式

作为一名iOS工程师,你完全不用自己去写一套观察者的代码。因为Cocoa Touch已经给你提供了2种技术方案供你选择-通知(NSNotification)和KVO(Key-Value Observing)。
当然可能有些人觉得和标准的观察者模式看起来不太一样呀,哈哈~ Apple其实是改写了这种模式的设计,但核心原理还是一样的。可以细细领会。

优缺点

优点
  • 观察者模式在观察目标和观察者之间建立一个抽象的耦合。

  • 观察者模式支持广播通信。

  • 观察者模式符合“开闭原则”的要求。(观察者与被观察者之间是属于轻度的关联关系,并且是抽象耦合的,这样,对于两者来说都比较容易进行扩展。)

缺点
  • 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。

  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。(在上面我已经避免过这个情况,但是需要注意观察目标的生命周期)

  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。


danielmea
28 声望8 粉丝

程序猿的逗比日常-------