观察者模式的定义:在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新!
提到观察者,你想到的很可能是KVO,是
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;```
方法
还有可能顺带想到一对多原则的通知。
观察者模式,并不是OC中所说的KVO与通知。但其实现思路确与二者的底层实现相一致:被观察者通知观察者有数据更新,并将数据告知观察者。其并不关心谁是观察者,有多少观察者,而观察者也无需知道被观察者是如何获取数据更新的。
下面模拟以下场景:
某气象站有这样一套设置:可以实时地监测环境的温度(temperature)、湿度(humidity)和气压(pressure),并有一套与之相连接的SDK(YXWWeatherDataSDK),可以实时返回这三个参数.现需要做一个气象观测站:首先,做一款App,以这三个参数互相组合展示三个页面(当前气象、温差显示及天气预报),再者,该观测站必须公布一组API可让第三方调用。
在做App时,如果三个页面者直接使用气象站的SDK,这样一旦SDK更新时,其参数的类型、数量等发生变化或者直接更换了SDK,那将带来非常繁琐的工作量。
不再多说,直接上代码。我们首先创建subject主题接口,用来声明注册、移除、通知观察者,所有的被观察者必须实现此接口。
import <Foundation/Foundation.h>
@protocol WYXSubject <NSObject>
@requried
- (void)registerObserver:(id)obj;
- (void)removeObserver:(id)obj;
- (void)notifyObervers:(NSDictionary *)data;
@end```
创建Observer观察者接口,所有的观察者要实现此接口
#import <Foundation/Foundation.h>
@protocol WYXObserver <NSObject>
- (void)update:(NSDictionary *)data;
@end
创建DisplayElement接口,观察者应实现此接口,在需要更新页面时调用相应方法
#import <Foundation/Foundation.h>
@protocol WYXDisplayElement <NSObject>
- (void)display;
@end```
下面实现被观察者
新建 WYXWeatherData 类,该类遵守WYXSubject协议
import <Foundation/Foundation.h>
import "WYXSubject.h"
@interface WYXWeatherData : NSObject<WYXSubject>
- (instancetype)sharedWeatherData;
@end
再看下具体实现
在单例方法中,该类成为YXWWeatherDataSDK的代理并实现代理方法
import "WYXWeatherData.h"
import "YXWWeatherData.h"
import "WYXObserver.h"
import "WYXHeaders.h"
@interface WYXWeatherData ()<YXWWeatherDataDelegate>
// 用来存放观察者
@property (nonatomic, strong) NSMutableArray <id<WYXObserver>>*observersArr;
@end
@implementation WYXWeatherData
static WYXWeatherData *weatherData;
- (instancetype)sharedWeatherData{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
weatherData = [[WYXWeatherData alloc] init];
weatherData.observersArr = [NSMutableArray array];
// 设置代理
[YXWWeatherData sharedWeatherDataSDK].delegate = weatherData;
});
return weatherData;
}
}```
其中observersArr用来存放观察者。
然后实现WYXSubject协议方法
pragma mark ****** WYXSubjec ******
// 添加观察者
- (void)registerObserver:(id<WYXObserver>)obj{
[self.observersArr addObject:obj];
}
// 移除观察者
- (void)removeObserver:(id<WYXObserver>)obj{
for (id<WYXObserver>oberver in self.observersArr) {
if ([oberver isEqual:obj]) {
[self.observersArr removeObject:obj];
}
}
}
// 通知观察者有更新
- (void)notifyObervers:(NSDictionary *)data{
for (id<WYXObserver>oberver in self.observersArr) {
[oberver update:data];
}
}```
在代理方法中调用上述- (void)notifyObervers:(NSDictionary *)data方法来为观察者更新数据
pragma mark ****** delegate ******
- (void)updateTemperature:(float)temperature humidity:(float)humidity andPressure:(float)pressure{
NSDictionary *data = @{
TEMP : @(temperature),
HUMI : @(humidity),
PRES : @(pressure)
};
[self notifyObervers:data];
}```
以上代码就可以视为我们所需要提供给第三方的API。
下面我们通过上述API来实现当前气象的的页面。
新建WYXCurrCondiDisplay类
#import <Foundation/Foundation.h>
#import "WYXObserver.h"
#import "WYXDisplayElement.h"
#import "WYXSubject.h"
@interface WYXCurrCondiDisplay : NSObject<WYXObserver,WYXDisplayElement>
// 构造方法需要一个实现Suject协议的被观察者对象
- (instancetype)initWithWeatherData:(id<WYXSubject>)weatherData;
@end```
import "WYXCurrCondiDisplay.h"
import "WYXHeaders.h"
@interface WYXCurrCondiDisplay ()
@property (nonatomic, strong) NSDictionary *currData;
@end
@implementation WYXCurrCondiDisplay
-
(instancetype)initWithWeatherData:(id<WYXSubject>)weatherData{
self = [super init];
if (self) {
// 在构造方法中注册成为weatherData的观察者
[weatherData registerObserver:self];
}
return self;
}
// 接收到有数据更新的通知 -
(void)update:(NSDictionary *)data{
self.currData = data;
[self display];
}
// 显示当前数据 -
(void)display{
float tem = [[self.currData objectForKey:TEMP] floatValue];
float hum = [[self.currData objectForKey:HUMI] floatValue];
float pre = [[self.currData objectForKey:PRES] floatValue];NSLog(@"当前最新气况\n 温度:%f\n 湿度:%f\n 气压:%f",tem,hum,pre);
}
[原码地址](https://git.oschina.net/handanying/ObserverPattern.git)
网友评论