【设计模式】二、观察者模式

 一、概述

      观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

     观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

     一个软件系统常常要求在某一个对象的状态发生变化的时候,某些其它的对象做出相应的改变。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合度的设计方案。减少对象之间的耦合有利于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作(Collaboration)。观察者模式是满足这一要求的各种设计方案中最重要的一种。

类比:它的关系类似于报纸的订阅。出版者(subject)+订阅者(observer)=观察者模式。

观察者模式提供一种对象设计,让主题和观察者之间松耦合。当两个对象之间松耦合,他们依然可以相互江湖,但是不太清楚彼此的细节。

改变主题或者观察者的一方,并不会影响另一方,因为两者是松耦合的,所以只要他们之间的接口仍被遵守,我们就可以自由的改变他们。

设计原则:为了交互对象之间的松耦合而努力。

二、在OC中的实现

以下内容参考:李久寧的博客  狂放不羁

1、KVO

Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。每次指定的被观察的对象的属性被修改后,KVO自动通知相应的观察者。

model中定义:

@interface StockData : NSObject {
    NSString * stockName;
    float price;
}
@end
@implementation StockData
@end

controller中使用,记得上一篇怎么说的吗?这里相当于跟模型说,我要收听你的更新广播

- (void)viewDidLoad
{
    [super viewDidLoad];

    stockForKVO = [[StockData alloc] init];
    [stockForKVO setValue:@"searph" forKey:@"stockName"];
    [stockForKVO setValue:@"10.0" forKey:@"price"];    
    [stockForKVO addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];

    myLabel = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 30 )];
    myLabel.textColor = [UIColor redColor];
    myLabel.text = [stockForKVO valueForKey:@"price"];
    [self.view addSubview:myLabel];
   
    UIButton * b = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    b.frame = CGRectMake(0, 0, 100, 30);
    [b addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:b];

}

用户单击View中的button调用控制器中的action去更改模型中的数据

-(void) buttonAction
{
    [stockForKVO setValue:@"20.0" forKey:@"price"];
}

控制器需要实现的回调,相当于收到广播后我应该做啥事

复制代码
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if([keyPath isEqualToString:@"price"])
    {
        myLabel.text = [stockForKVO valueForKey:@"price"];
    }
}
复制代码

视图dealloc需要取消观察

- (void)dealloc
{
    [super dealloc];
    [stockForKVO removeObserver:self forKeyPath:@"price"];
    [stockForKVO release];
}

 

2、Notification

      不要和远程推送以及本地通知所混淆,通知是一种基于订阅-发布模式的模型,它让发布者可以给订阅者发送消息,并且发布者不需要对订阅者有任何的了解。
      通知在苹果官方被大量的使用。举例来说,当键盘弹出或者隐藏的时候,系统会独立发送UIKeyboardWillShowNotification/UIKeyboardWillHideNotification通知。当你的应用进入后台运行的时候,系统会发送一个UIApplicationDidEnterBackgroundNotification通知。
 
注意:打开UIApplication.h,在文件的末尾,你将看到一个由系统发出的超过20个通知组成的列表。

通知使用起来非常的简单:

首先定义回调,即发生通知了我应该做啥事。

- (void)callBack{
    NSLog(@"我收到通知了!");
}

其次,注册通知,即告诉通知中心,我对啥通知感兴趣

[[NSNotificationCenter defaultCenter] addObserver: self
    selector: @selector(callBack)
    name: @"A类通知"
    object: nil];

第三,在程序任何一个地方都可以发送通知

- (void)getNotofocation{
    NSLog(@"get it.");
    //发出通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"A类通知" object:self];
}

当然,也可以在需要的时候取消注册通知。

 

注意:你要总是记得去移除已经销毁的观察者,否则当给不存在的观察者发送消息的时候,你的应用可能会崩溃。
      如果你玩一回你的应用后终止它,你会发现你的应用状态没有被保存,你上次查看的专辑不是下次启动时候的缺省专辑。
       为了修正这个问题,你可以使用列表中的下个模式:备忘录(Memento)模式.
 
通知方法的优点是分离了生产者和消费者之间的联系,但正是这样,往往事情太过于绝对也不好,就像这,一点联系都没的话就容易导致代码的可读性差,通知多得时候,阅读起来可能很复杂,没有思路。

 

posted @ 2013-12-23 19:04  ymonke  阅读(790)  评论(0编辑  收藏  举报