一,什么是观察者模式
- 定义:
定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
- 需求场景:
当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变;或者一个对象必须通知其它对象,而它又不能假定其它对象是谁,换言之,我们不希望这些对象是紧密耦合的。这时我们就可以利用到观察者模式。
- 示例
在IOS中典型的推模型实现方式为NSNotificationCenter和KVO。
二,观察者模式的结构图
- 实现步骤:
1. 定义观察者中心类,用于订阅号的注册,及管理消息的发送,观察者对象的释放。
2. 定义接口协议,用于观察者的代理回调
3. 实现添加订阅号,添加订阅号观察者,订阅号消息发送,移除订阅号及观察者对象 - 注意点
1.对象容器的结构:{key:(NSHashTable),key:(NSHashTable)...}
2.订阅号下消息代理回调:NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber]; if (hashTable) { NSEnumerator *enumerator = [hashTable objectEnumerator]; id <SubscriptionServiceCenterProtocol> customer = nil; while (customer = [enumerator nextObject]) { if ([customer respondsToSelector:@selector(subscriptionMessage:subscriptionNumber:)]) { [customer subscriptionMessage:message subscriptionNumber:subscriptionNumber]; } } }
- 结构图
三,代码示例
- SubscriptionServiceCenter
- SubscriptionServiceCenter.h
#import <Foundation/Foundation.h> #import "SubscriptionServiceCenterProtocol.h" /** * 订阅服务中心(实现了系统的通知中心业务逻辑) * * = 注意 = 没有考虑发送通知的时候,同步与异步的问题 * */ @interface SubscriptionServiceCenter : NSObject /** * 创建订阅号 * * @param subscriptionNumber 订阅号码 */ + (void)createSubscriptionNumber:(NSString *)subscriptionNumber; /** * 移除订阅号(参与到该订阅号码的所有客户不会再收到订阅信息) * * @param subscriptionNumber 订阅号码 */ + (void)removeSubscriptionNumber:(NSString *)subscriptionNumber; /** * 客户订阅指定的订阅号 * * @param customer 客户对象 * @param subscriptionNumber 订阅号码 */ + (void)addCustomer:(id <SubscriptionServiceCenterProtocol>)customer withSubscriptionNumber:(NSString *)subscriptionNumber; /** * 将指定客户从指定订阅号上移除掉 * * @param customer 客户对象 * @param subscriptionNumber 订阅号码 */ + (void)removeCustomer:(id <SubscriptionServiceCenterProtocol>)customer fromSubscriptionNumber:(NSString *)subscriptionNumber; /** * 通知签订了订阅号码的对象 * * @param message 信息对象 * @param subscriptionNumber 订阅号码 */ + (void)sendMessage:(id)message toSubscriptionNumber:(NSString *)subscriptionNumber; @end
- SubscriptionServiceCenter.m
#import "SubscriptionServiceCenter.h" static NSMutableDictionary *_subscriptionNumberDictionary = nil; @implementation SubscriptionServiceCenter #pragma mark - 初始化 + (void)initialize { if (self == [SubscriptionServiceCenter class]) { _subscriptionNumberDictionary = [NSMutableDictionary dictionary]; } } + (void)createSubscriptionNumber:(NSString *)subscriptionNumber { NSParameterAssert(subscriptionNumber); NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber]; if (hashTable == nil) { hashTable = [NSHashTable weakObjectsHashTable]; [_subscriptionNumberDictionary setObject:hashTable forKey:subscriptionNumber]; } } + (void)removeSubscriptionNumber:(NSString *)subscriptionNumber { NSParameterAssert(subscriptionNumber); if ([self existSubscriptionNumber:subscriptionNumber]) { [_subscriptionNumberDictionary removeObjectForKey:subscriptionNumber]; } } + (void)removeCustomer:(id <SubscriptionServiceCenterProtocol>)customer fromSubscriptionNumber:(NSString *)subscriptionNumber { NSParameterAssert(subscriptionNumber); NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber]; if (hashTable && customer) { [hashTable removeObject:customer]; } } + (void)sendMessage:(id)message toSubscriptionNumber:(NSString *)subscriptionNumber { NSParameterAssert(subscriptionNumber); NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber]; if (hashTable) { NSEnumerator *enumerator = [hashTable objectEnumerator]; id <SubscriptionServiceCenterProtocol> customer = nil; while (customer = [enumerator nextObject]) { if ([customer respondsToSelector:@selector(subscriptionMessage:subscriptionNumber:)]) { [customer subscriptionMessage:message subscriptionNumber:subscriptionNumber]; } } } } + (void)addCustomer:(id)customer withSubscriptionNumber:(NSString *)subscriptionNumber { NSParameterAssert(customer); NSParameterAssert(subscriptionNumber); NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber]; [hashTable addObject:customer]; } #pragma mark - 私有方法 + (NSHashTable *)existSubscriptionNumber:(NSString *)subscriptionNumber { return [_subscriptionNumberDictionary objectForKey:subscriptionNumber]; } @end
- SubscriptionServiceCenterProtocol.h
#import <Foundation/Foundation.h>
@protocol SubscriptionServiceCenterProtocol <NSObject> /** * 接收到的订阅信息 * * @param message 订阅信息 * @param subscriptionNumber 订阅编号 */ - (void)subscriptionMessage:(id)message subscriptionNumber:(NSString *)subscriptionNumber; @end
- SubscriptionServiceCenter.h
- ViewController
#import "ViewController.h" #import "SubscriptionServiceCenter.h" #define SCIENCE @"SCIENCE" #define NEWTON @"NEWTON" @interface ViewController ()<SubscriptionServiceCenterProtocol> @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 创建订阅号 - SCIENCE NEWTON [SubscriptionServiceCenter createSubscriptionNumber:SCIENCE]; [SubscriptionServiceCenter createSubscriptionNumber:NEWTON]; // 客户添加了订阅号 - SCIENCE NEWTON [SubscriptionServiceCenter addCustomer:self withSubscriptionNumber:SCIENCE]; [SubscriptionServiceCenter addCustomer:self withSubscriptionNumber:NEWTON]; // 订阅中心给订阅号 - SCIENCE NEWTON 发送订阅信息 [SubscriptionServiceCenter sendMessage:@"爱因斯坦" toSubscriptionNumber:SCIENCE]; [SubscriptionServiceCenter sendMessage:@"小苹果" toSubscriptionNumber:NEWTON]; } - (void)subscriptionMessage:(id)message subscriptionNumber:(NSString *)subscriptionNumber { if ([subscriptionNumber isEqualToString:NEWTON]) { NSLog(@"来自于牛顿杂志的信息 - %@", message); } else if ([subscriptionNumber isEqualToString:SCIENCE]) { NSLog(@"来自于科学美国人杂志的信息 - %@", message); } } @end
- 打印结果:
2019-09-07 23:21:44.685105+0800 SystemNotificationCenter[24007:6871723] 来自于科学美国人杂志的信息 - 爱因斯坦 2019-09-07 23:21:44.685360+0800 SystemNotificationCenter[24007:6871723] 来自于牛顿杂志的信息 - 小苹果
四,demo
观察者模式