美文网首页
DP之观察者模式

DP之观察者模式

作者: 今晚写bug | 来源:发表于2016-08-13 00:23 被阅读24次

首先我想以一份简单代码开始这篇blog。

#import <Foundation/Foundation.h>

@interface B : NSObject
{
    int _a;
}
- (void)update:(int)a;
@end
@implementation B

- (void)update:(int )a
{
    _a = a;
    _a += 10;
    NSLog(@"B:%d",_a);
}
@end

@interface C : NSObject
{
    int _a;
}

- (void)update:(int) a;

@end
@implementation C

- (void)update:(int) a
{
    _a = a;
    _a += 20;
    NSLog(@"C:%d",_a);
}

@end

@interface A : NSObject
{
    int _a;
}
- (void)update:(B *)pb : (C *)pc :(int) a;
@end
@implementation A
- (void)update:(B *)pb : (C *)pc :(int) a
{
    _a = a;

    NSLog(@"A:%d",_a);
    [pb update:_a];
    [pc update:_a];
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        B *pb = [B new];
        C *pc = [C new];
        
        A *a = [A new];
        [a update:pb :pc :18];
    }
    return 0;
}

上述小案例,主要是想表达,当A 类对象的 _a 成员发生变化时,同时让 B 类 和 C类对象的_a 成员作出各自相应的变化。事实上,我们确实实现了这个功能。但是,你发现没有,耦合性太高了。A 类提供的API - (void)update:(B *)pb : (C *)pc :(int) a; 就可以看出,耦合性太高了。遥想想当年,设计模式产生之前,肯定大师们也发现了这样的问题,他们是怎么解决的呢,下面引出我们的使用很广泛的设计模式 --- 观察者模式。

观察者模式 observer

Observer模式是行为模式之一,它的作用是当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对象状态。
Observer模式提供给关联对象一种同步通信的手段,使某个对象与依赖它的其他对象之间保持状态同步。
定义:

定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的 对象都得到通知并被自动更新。观察者处于被动地位,被观察者处于主动地位。
这种交互也称为发布-订阅(publish-subscribe)。

角色和职责
Subject(被观察者):
被观察的对象。当需要被观察的状态发生变化时,需要通知队列中所有观察者对象。 
被观察者知道他的观察者,可以有任意多个观察者观察同一个目标。
提供注册和删除观察者对象的接口。
    
ConcreteSubject(具体被观察者):
        将有关状态存入各ConcreteObserver对象。
        当他的状态发生改变时,向他的各个观察者发出通知。
        被观察者的具体实现。包含一些基本的属性状态及其他操作。
Observer(观察者):
        为那些在被观察者发生改变时需获得通知的对象定义一个更新接口。
        接口或抽象类。当Subject的状态发生变化时,Observer对象将通过一个callback函数得到通知。
ConcreteObserver:
        观察者的具体实现。得到通知后将完成一些具体的业务逻辑处理。
        维护一个指向ConcreteSubject对象的引用。
        存储有关状态,这些状态应与目标的状态保持一致。
        实现Observer的更新接口以使自身状态与目标的状态保持一致。

典型应用

  • 侦听事件驱动程序设计中的外部事件
  • 侦听/监视某个对象的状态变化
  • 发布者/订阅者(publisher/subscriber)模型中,当一个外部事件(新的产品,消息的出现等等)被触发时,通知邮件列表中的订阅者

适用于:
定义对象间一种一对多的依赖关系,使得每一个对象改变状态,则所有依赖于他们的对象都会得到通知。

使用场景:

比如有一个世界时钟程序,有多个图形时钟去显示比如北京时区,巴黎时区,等等。 如果设置一个北京时间,那么其他时钟图形都需要更新(加上或者减去时差值)。典型 的图形界面设计随处可见,一个温度程序,在温度湿度等条件改变时,要更新多种显示 图形来呈现。

show me the code

#import <Foundation/Foundation.h>

@interface Observer : NSObject

- (void)updateHour:(int)h minute:(int )m second:(int )s;

@end

@implementation Observer

- (void)updateHour:(int)h minute:(int )m second:(int )s{}

@end


@interface Subject : NSObject

@property (nonatomic, strong) NSMutableArray *observerList;

- (void) registerObserver:(Observer *)observer;
- (void) removeObserver:(Observer *)observer;

@end

@implementation Subject

- (NSMutableArray *)observerList
{
    if (_observerList == NULL) {
        _observerList = [NSMutableArray array];
    }
    return _observerList;
}


- (void) registerObserver:(Observer *)observer
{
    [self.observerList addObject:observer];
}
- (void) removeObserver:(Observer *)observer
{
    [self.observerList removeObject:observer];
}

@end



@interface PekingTimeSubject : Subject
{
    int _hour;
    int _min;
    int _sec;
}

- (void)notify;

- (void)getTime;

- (void)setTime:(int) h minute:(int) m second:(int) s;


@end

@implementation PekingTimeSubject
- (void)notify
{
    for (Observer *ob in self.observerList) {
        [ob updateHour:_hour minute:_min second:_sec];
    }

}

- (void)getTime
{
    NSLog(@"北京:%d:%d:%d\n",_hour,_min,_sec);
}

- (void)setTime:(int) h minute:(int) m second:(int) s
{
    _hour = h;
    _min = m;
    _sec = s;
    [self getTime];
    [self notify];
}

@end


@interface USATimeObserver : Observer
{
    int _hour;
    int _min;
    int _sec;
}

- (void)updateHour:(int)h minute:(int )m second:(int )s;
- (void)dis;

@end

@implementation USATimeObserver

- (void)updateHour:(int)h minute:(int )m second:(int )s{
    _hour = h + 2;
    _min = m + 12;
    _sec = s;

    [self dis];
}

- (void)dis
{
    NSLog(@"美国:%d:%d:%d\n",_hour,_min,_sec);
}

@end


@interface EuropeTimeObserver : Observer
{
    int _hour;
    int _min;
    int _sec;
}

- (void)updateHour:(int)h minute:(int )m second:(int )s;
- (void)dis;

@end

@implementation EuropeTimeObserver

- (void)updateHour:(int)h minute:(int )m second:(int )s{
    _hour = h = 3;
    _min = m +10;
    _sec = s;
    
    [self dis];
}

- (void)dis
{
    NSLog(@"欧洲:%d:%d:%d\n",_hour,_min,_sec);
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        PekingTimeSubject  *pkt =  [PekingTimeSubject new];
        USATimeObserver    *ust =  [USATimeObserver new];
        EuropeTimeObserver *ert =  [EuropeTimeObserver new];
    
        [pkt registerObserver:ust];
        [pkt registerObserver:ert];
        [pkt setTime:12 minute:23 second:45];
        
        
        [pkt removeObserver:ust];
        [pkt setTime:10 minute:10 second:23];
    }
    return 0;
}

仔细阅读上述代码。并在自己的Xcode中自行调试。设计模式本来就是极其抽象的。☺ ↖(ω)↗ ☺

原本是子类间进行耦合,把子类间的耦合提升到父类中去,这样父类中一次耦合关系,使得众子类耦合关系得到了解耦。从uml 也可以看的出来,父类间是紧耦合的,而子类中却是松耦合的。

上述介绍,只是实现了传统意义上的的观察者模式。万变不离其宗,要多加体会。

在Cocoa Touch 框架中 用了两种技术改写了观察者模式 ---- 通知和键值观察(Key-Value Observing).尽管是两种不同过得Cocoa技术,两者都实现了观察者模式。他们有各自的特征以及差别。

1. 通知

Cocoa Touch框架使用 NSNotificationCenter 和 NSNotifiCation 对象实现了一对多的发布-订阅模型。他们允许subject与observer以一种松耦合的方式通信.两者在通信时对另一方无需多少了解。
subject要notify其他对象时,需要创建一个可通过全局的名字来识别的通知对象,然后把它投递到通知中心。通知中心,查明特定的通知的观察者,然后通过消息把通知发送给他们。

2. 键-值观察

Cocoa(包括Cocoa Touch)提供了一种称为键-值观察的机制,对象可以通过它得到其他对特定属相的变更通知。这种机制在模型-视图-控制器模式的场景中尤其重要,因为它让视图对象可以经由控制器层观察模型对象的变更。
这里我们提到了MVC,其实观察者模式对MVC设计模式的运作,是不可或缺的,从某种意义上说构成了Model和Controller进行主动通信的桥梁。稍过些日子可能会有一篇关于MVC的总结。

3. 更多的KVO和通知的介绍和剖析这里暂时不进行叙述了。不过我提供了一篇博文供大家参考及研究。链接如下:

南峰子的技术博客--Foundation: NSKeyValueObserving(KVO)

如果您有更多的优秀资源学习资源要推荐请麻烦您,联系我。共同进步,thanks a lot!

相关文章

网友评论

      本文标题:DP之观察者模式

      本文链接:https://www.haomeiwen.com/subject/oqmesttx.html