一、对象创建
1、原型(Prototype)✅
使用原型实例制定创建对象的种类,并通过复制这个原型创建新的对象。
例:
Cocoa Touch 框架中的 -(instancetype)copyWithZone:(NSZone *)zone;
- (instancetype)copyWithZone:(NSZone *)zone {
WJPersonal *itemInfo = [[[self class] allocWithZone:zone] init];
itemInfo.userID = self.userID;
itemInfo.userName = self.userName;
itemInfo.userUrl = self.userUrl;
itemInfo.userCompany = self.userCompany;
itemInfo.positionList = [self.positionList wj_config_deepCopy]; // wj_config_deepCopy在分类中实现,具体代码在下面
return itemInfo;
}
容器拷贝相关问题
我处理的方式是:创建 NSObject、NSArray、NSMutableArray、NSDictionary、NSMutableDictionary 分类,具体代码如下
@implementation NSObject (WJDeepCopyExtend)
- (id)wj_config_deepCopy {
if ([self conformsToProtocol:@protocol(NSCopying)]) {
return [self copy];
}
return self;
}
@end
@implementation NSArray (WJDeepCopyExtend)
- (NSArray *)wj_config_deepCopy {
NSMutableArray *result = [NSMutableArray array];
for (id item in self) {
[result addObject:[item wj_config_deepCopy]];
}
return [result copy];
}
@end
@implementation NSMutableArray (WJDeepCopyExtend)
- (NSMutableArray *)wj_config_deepCopy {
NSMutableArray *result = [NSMutableArray array];
for (id item in self) {
[result addObject:[item wj_config_deepCopy]];
}
return result;
}
@end
2、工厂方法✅
定义创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到其子类。
- 也称为虚构造器,⚠️与抽象工厂的区别
优点:
- 从代码中消除了对应用程序特有类的耦合
何时使用工厂方法:
- 编译时无法准备预期要创建的对象的类
- 类想让其子类决定再运行时创建什么
- 类有若干辅助类为其子类,而你想将返回哪个子类型这一信息局部化
Cocoa Touch框架中应用的工厂方法:
+ (NSNumber *)numberWithChar:(char)value;
+ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
+ (NSNumber *)numberWithShort:(short)value;
+ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
+ (NSNumber *)numberWithInt:(int)value;
...
3、抽象工厂✅
提供一个创建一系列相关或相互依赖对象的接口,而无需制定他们具体的类
- 抽象工厂提供了一个固定的接口,用于创建一系列有关联或相互依存的对象,而不必制定其具体类或其创建的细节
- 从工厂得到的具体对象之间没有耦合
软件设计的黄金法则:变动需要抽象
Cocoa Touch框架中应用抽象工厂:
NSNumber *boolNumber = [NSNumber numberWithBool:YES];
NSNumber *charNumber = [NSNumber numberWithChar:'A'];
NSNumber *intNumber = [NSNumber numberWithInt:1];
NSNumber *floatNumber = [NSNumber numberWithFloat:1.0];
NSLog(@"%@", [boolNumber class]);
NSLog(@"%@", [charNumber class]);
NSLog(@"%@", [intNumber class]);
NSLog(@"%@", [floatNumber class]);
2021-03-10 19:16:37.235427+0800 ZF_Beta[21994:439610] __NSCFBoolean
2021-03-10 19:16:37.235913+0800 ZF_Beta[21994:439610] __NSCFNumber
2021-03-10 19:16:37.236037+0800 ZF_Beta[21994:439610] __NSCFNumber
2021-03-10 19:16:37.236196+0800 ZF_Beta[21994:439610] __NSCFNumber
抽象工厂 | 工厂方法 |
---|---|
通过对象组合创建抽象产品 | 通过类继承创建抽象产品 |
创建多系列产品 | 创建一种产品 |
必须修改父类接口才能支持新的产品 | 子类化创建者并重载工厂方法以创建新产品 |
✨简单工厂
- IOS设计模式浅析之简单工厂模式(SimpleFactory)
专门定义一个类来负责创建其他类的实例,被创建的实例常常具有共同的父类
- 实际上就是由一个工厂类,根据传入的参数,动态的决定创建出哪一个产品类的实例
设计原则体现:
- 开闭原则:对修改关闭、对扩展开(放在子类中新增私有方法)✅
- 里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响✅
优缺点:
- 客户端可以直接消费产品,而不必关心具体产品的实现
- 消除了客户端直接创建产品对象的责任,实现了对责任的分割。
- 工厂类集中了所有产品的创建逻辑,一旦不能正常工作,整个系统都会受到影响
- 当产品类别多结构复杂的时候,把所有创建工作放进一个工厂来,会使后期程序的扩展较为困难
使用场景
- 工厂类负责创建的对象比较少时
- 客户端只知道传入工厂类的参数,对于如何创建对象的逻辑不必关心时
4、生成器(建造者模式)✅
- ios设计模式——生成器模式
- IOS设计模式浅析之建造者模式(Builder)
将一个复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的表现
两个角色:
- 指导者(Director)
- 生成器(Builder)
- 创建一个电脑,分为主机,屏幕,键盘,鼠标几个部分
- 产品:电脑
@interface WJComputer ()
@property (nonatomic, strong) NSString *screen; //屏幕
@property (nonatomic, strong) NSString *host; //主机
@property (nonatomic, strong) NSString *keyboard; //键盘
@property (nonatomic, strong) NSString *mouse; //鼠标
@end
@implementation WJComputer
- (instancetype)init {
if(self = [super init]) {
self.screen = @"屏幕";
self.host = @"主机";
self.keyboard = @"键盘";
self.mouse = @"鼠标";
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"主机为%@, 屏幕为%@,键盘为%@,鼠标为%@", self.host, self.screen, self.keyboard, self.mouse];
}
@en
- 构建者
#import
#import "WJComputer.h"
NS_ASSUME_NONNULL_BEGIN
@interface WJComputerBuilder : NSObject
@property (nonatomic, strong) WJComputer *computer;
- (WJComputerBuilder *)buildNewComputer;
- (WJComputerBuilder *)buildScreen:(NSString *)screen;
- (WJComputerBuilder *)buildHost:(NSString *)host;
- (WJComputerBuilder *)buildKeyboard:(NSString *)keyboary;
- (WJComputerBuilder *)buildMouse:(NSString *)mouse;
@end
@implementation WJComputerBuilder
- (WJComputerBuilder *)buildNewComputer {
self.computer = [[WJComputer alloc] init];
return self;
}
- (WJComputerBuilder *)buildScreen:(NSString *)screen {
self.computer.screen = screen;
return self;
}
- (WJComputerBuilder *)buildHost:(NSString *)host {
self.computer.host = host;
return self;
}
- (WJComputerBuilder *)buildKeyboard:(NSString *)keyboary {
self.computer.keyboard = keyboary;
return self;
}
- (WJComputerBuilder *)buildMouse:(NSString *)mouse {
self.computer.mouse = mouse;
return self;
}
@end
- 指导者
#import
#import "WJComputer.h"
#import "WJComputerBuilder.h"
NS_ASSUME_NONNULL_BEGIN
@interface WJComputerDirector : NSObject
- (WJComputer *)buildAcer:(WJComputerBuilder *)builder;
- (WJComputer *)buildLenovo:(WJComputerBuilder *)builder;
@end
@implementation WJComputerDirector
- (WJComputer *)buildAcer:(WJComputerBuilder *)builder {
[builder buildNewComputer];
[builder buildHost:@"AcerHost"];
[builder buildScreen:@"AcerScreen"];
[builder buildKeyboard:@"AcerKeyboard"];
[builder buildMouse:@"AcerMouse"];
return [builder computer];
}
- (WJComputer *)buildLenovo:(WJComputerBuilder *)builder {
[builder buildNewComputer];
[builder buildHost:@"lenovoHost"];
[builder buildScreen:@"lenovoScreen"];
[builder buildKeyboard:@"lenovoKeyboard"];
[builder buildMouse:@"lenovoMouse"];
return [builder computer];
}
@end
- 调用及打印
WJComputerBuilder *creater = [[WJComputerBuilder alloc] init];
WJComputerDirector *director = [[WJComputerDirector alloc] init];
WJComputer *acre = [director buildAcer:creater];
NSLog(@"%@", acre);
WJComputer * lenovo = [director buildLenovo:creater];
NSLog(@"%@", lenovo);
2021-03-15 19:34:48.632250+0800 property[11469:275860] 主机为AcerHost, 屏幕为AcerScreen,键盘为AcerKeyboard,鼠标为AcerMouse
2021-03-15 19:34:48.632436+0800 property[11469:275860] 主机为lenovoHost, 屏幕为lenovoScreen,键盘为lenovoKeyboard,鼠标为lenovoMouse
- 生成器与抽象工厂对比
生成器 | 抽象工厂 |
---|---|
构建复杂对象 | 构建简单或复杂对象 |
以多个步骤构建对象 | 以单一步骤构建对象 |
以多种方式构建对象 | 以单一方式构建对象 |
在构建过程的最后一步返回产品 | 立刻返回产品 |
专注一个特定的产品 | 强调一套产品 |
5、单例✅
保证一个类仅有一个实例,并提供一个访问它全局访问点
系统中只能共享不能复制的资源
- 定位。CLLocationManager定义了GPS设备所提供服务的单一访问点
Cocoa Touch框架使用单例
- UIApplication:每个应用程序有且仅有一个UIApplication实例,由UIApplicationMain函数在应用启动时创建为单例对象,之后通过shareApplication类方法访问
- NSFileManager等
代码中的使用
- 之所以实现NSCopying协议,与内存管理相关
- allocWithZone会分配实例的内存,引用计数会+1
+ (WJLocationManager *)sharedInstance {
static WJLocationManager *s_instance = nil;
static dispatch_once_t once_token;
dispatch_once(&once_token, ^{
s_instance = [[super allocWithZone:NULL] init];
});
return s_instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [WJLocationManager sharedInstance];
}
- (instancetype)copyWithZone:(struct _NSZone *)zone {
return [WJLocationManager sharedInstance];
}
- (instancetype)mutableCopyWithZone:(nullable NSZone *)zone {
return [WJLocationManager sharedInstance];
}
优点:
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
- 避免对资源的多重占用
缺点:
- 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
二、接口适配
6、适配器(Adapter 极为常用)
- iOS进阶|适配器模式(解决设置页面)
- iOS设计模式(3)适配器模式
将一个类的接口转换为客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
使用情形
- 已由类的接口与需求不匹配
- 想要拥有一个可复用类,该类能够同可能带有不兼容接口的其他类协作
- 想要适配一个类的不同子类,可是让每一个子类去子类化一个类适配器又不现实,可以使用对象适配器来适配其父类的接口
Cocoa Touch框架使用单例
- 委托(delegate)模式主要是适配器模式
- 类适配器
- 对象适配器
- 类适配器 与 对象适配器 特征对比
类适配器 | 对象适配器 |
---|---|
只针对单一的具体的Adaptee类,把Adaptee适配到Target | 可以适配多个Adaptee及其子类 |
易于重载Adaptee的行为 | 难以重载Adaptee的行为,需要借助于子类的对象而不是Adaptee本身 |
只有一个Adaptee对象,无需额外的指针间接访问Adaptee | 需要额外的指针以间接访问Adaptee并适配其行为 |
7、桥接
- IOS设计模式浅析之桥接模式(Bridge)
将抽象部分与它的实现部分分离,使他它们都可以独立的变化
何时使用桥接模式
- 不想在抽象与其实现之间行程固定的绑定关系
- 抽象及其实现都可以通过子类化独立进行扩展
- 对抽象的实现进行修改不影响客户端代码
- 如果每个实现需要额外的子类以细化抽象,则说明有必要把她们分成两个部分
- 想在带有不同抽象接口的多个对象之间共享一个实现
8、外观(⚠️与组合模式的区别)✅
- iOS设计模式(8)外观模式
- iOS设计模式之(七)外观模式
为系统中的一组接口提供一个统一的接口。外观定义了一个高层接口,让子系统更易于使用
使用场景
- 子系统正逐渐变得复杂。应用模式的过程中演化出许多类。可以使用外观为这些子系统提供一个较简单的接口
- 可以使用外观对子系统进行分层。每个子系统级别有一个外观座位入口点
- 例:开机自动启动XCode、微信、SourceTree...
#import "WJComputer.h"
#import "WJXcode.h"
#import "WJSourceTree.h"
#import "WJWeChat.h"
@interface WJComputer ()
@property (nonatomic, strong) WJXcode *Xcode;
@property (nonatomic, strong) WJSourceTree *sourceTree;
@property (nonatomic, strong) WJWeChat *weChat;
@end
@implementation WJComputer
- (instancetype)init {
self = [super init];
if (self) {
self.xCode = [[WJXcode alloc] init];
self.sourceTree = [[WJSourceTree alloc]init];
self.weChat = [[WJWeChat alloc]init];
}
return self;
}
- (void)close {
[self.xCode close];
[self.sourceTree close];
[self.weChat close];
}
- (void)open {
[self.xCode open];
[self.sourceTree open];
[self.weChat open];
}
@end
@interface WJXcode : NSObject
- (void)close;
- (void)open;
@end
@implementation WJXcode
- (void)close {
NSLog(@"%s", __func__);
}
- (void)open {
NSLog(@"%s", __func__);
}
@end
@interface WJSourceTree : NSObject
- (void)close;
- (void)open;
@end
@implementation WJSourceTree
- (void)close {
NSLog(@"%s", __func__);
}
- (void)open {
NSLog(@"%s", __func__);
}
@end
@interface WJWeChat : NSObject
- (void)close;
- (void)open;
@end
@implementation WJWeChat
- (void)close {
NSLog(@"%s", __func__);
}
- (void)open {
NSLog(@"%s", __func__);
}
@end
三、对象去耦
9、中介者
用一个对象来封装一系列对象的交互方式。中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
说明
- 中介者模式以中介者内部的复杂性代替交互的复杂性
使用场景
- 对象间的交互虽定义明确,然而非常复杂,导致一组对象比例相互依赖而且难以理解
- 因为对象引用了许多其他对象并与其通讯,导致对象难以复用
- 想要定制一个分布在多个类中的逻辑或行为,又不想生成太多子类
例:推送路由处理
- 将跳转方法封装到一个类中,它减少了视图控制器之间的依存关系
- (void)pushViewController:(NSInteger)tag {
switch (tag) {
case 0: {
GuideViewController *guideViewController = [[GuideViewController alloc] init];
[self.appRootController pushViewController:guideViewController animated:YES];
}
break;
case 1: {
WebViewController *webViewController = [[WebViewController alloc] init];
[self.appRootController pushViewController:webViewController animated:YES];
}
break;
......
default:
break;
}
}
**** 拓展:使用策略模式替换switch
10、观察者✅
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
何时使用观察者模式
- 有两种抽象类型互相依赖。将他们封装在各自的对象中,就可以对它们单独进行改变和复用
- 对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变
- 一个对象必须通知其他对象,而它又不需知道其他对象时什么
Cocoa Touch框架中用到的观察者模式
- 通知
- 键-值观察者(KVO)
两者的主要区别
通知 | 键-值观察者 |
---|---|
一个中心对象为所有观察者提供变更通知 | 被观察的对象直接面向观察者发送通知 |
主要从广义上关注程序事件 | 绑定于特定对象属性的值 |
四、抽象集合
11、组合
- OC 组合实现多继承
- iOS设计模式——组合模式
将对象组合成树形结构以表示“部分-整体”的结构层次。组合使用户对单个对象和组合对象的使用具有一致性
- 通过组合模式实现多继承
例:ClassC需要继承ClassA中methodA、ClassB中methodB
@interface ClassA : NSObject
-(void)methodA;
@end
@interface ClassB : NSObject
-(void)methodB;
@end
@interface ClassC : NSObject {
ClassA *a;
ClassB *b;
}
- (id)initWithA:(ClassA *)A b:(ClassB *)B;
- (void)methodA;
- (void)methodB;
@end
@implementation ClassC
- (id)initWithA:(ClassA *)A b:(ClassB *)B {
a = [[ClassA alloc] initWithClassA: A];
b = [[ClassB alloc] initWithClassB: B];
}
- (void)methodA {
[a methodA];
}
- (void)methodB {
[b methodB];
}
12、迭代器✅
提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内部表示
外部迭代器
- 客户端需要知道外部迭代器才能使用,但是它为客户端提供了更多的控制
- 客户端创建并维护外部迭代器
- 客户端可以使用不同外部迭代器实现多种类型的遍历
内部迭代器
- 客户端不需要知道任何外部迭代器,而是可以通过集合对象的特殊接口,或者一次访问一个元素,或者向集合中的每个元素发送消息
- 集合对象本身创建并维护它的外部迭代器
- 集合对象可以在不修改客户端代码的情况下,选择不同的外部迭代器
何时使用迭代器模式
- 需要访问组合对象的内容,而又不暴露其内部表示
- 需要通过多种方式便利组合对象
- 需要提供一个统一的接口,用来遍历各种类的组合对象
Coco Touch框架中使用迭代器模式
- NSEnumerator
- 基于block的枚举
- 快速枚举
- 内部枚举
NSArray *array = @[@1,@2,@3];
NSEnumerator *enumerator = [array objectEnumerator];
NSNumber *number;
while (number = [enumerator nextObject]) {
/**
* do something
*/
}
for ( ... in ... )
NSArray *array = @[@"123",@"456",@"789"];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj localizedCaseInsensitiveCompare:@"789"]) {
*stop = YES;
}
}];
五、行为扩展
13、访问者
- iOS设计模式---访问者模式
表示一个作用于某对象结构中的各元素的操作。它让我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作
使用场景:
- 一个复杂的对象结构包含很多其他对象,它们有不同的接口,但是想对这些对象实施一些依赖于其具体类型的操作
- 需要对一个组合结构中的对象进行很多不相关的操作,但是不想让这些操作“污染”这些类的对象
- 可以将相关的操作集中起来,定义在一个访问者类中,并在需要在访问者中定义的操作时使用它
- 定义复杂结构的类很少作修改,但经常需要向其添加新的操作
关键角色
- 访问者
- 它访问的元素
优点:
- 添加操作,只需实现具体的访问者,不会对类的结构造成破坏
缺点:
- 访问者与目标类耦合在一起,因此,如果访问者需要支持新类,访问者的父类和子类都需要修改,才能反应新的功能
14、装饰✅
- iOS设计模式之装饰者模式
动态地给一个对象添加一些额外的职责。就是扩展功能来说,装饰模式相比生成子类更为灵活
使用场景:
- 想要在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
- 想要扩展一个类的行为,却做不到。类定义可能被隐藏,无法进行子类化;或者进行子类化;或者,对类的每个行为的扩展,为支持每种功能的组合,将产生大量的子类
- 对类的职责的扩展是可选的
总结:
- 装饰者包含被装饰者的所有接口和引用,方法实现完全是引用调用自己的方法,在装饰者子类添加新功能。
- 分类主要用于对被装饰者类的方法拓展,与本设计模式稍有区别
使用:
- 为减少第三方的入侵性,通常创建一个类来管理它
- IQKeyboardManager
@interface IQKeyboardManager : NSObject
@property(nonatomic, assign, getter = isEnabled) BOOL enable;
@property(nonatomic, assign) CGFloat keyboardToNavigationDistance;
...
@end
@interface WJKeyboardManager : NSObject
@property(nonatomic, assign, getter = isEnabled) BOOL enable;
@property(nonatomic, assign) CGFloat keyboardToNavigationDistance;
@property (nonatomic, strong) IQKeyboardManager *keyboardMgr;
@end
@implementation WJKeyboardManager
- (instancetype)init {
self = [super init];
if (self) {
_keyboardMgr = [IQKeyboardManager sharedManager];
}
return self;
}
- (void)setEnable:(BOOL)enable {
[_keyboardMgr setEnable:enable];
}
- (BOOL)isEnabled {
return _keyboardMgr.isEnabled;
}
- (BOOL)keyboardIsShowing {
return _keyboardMgr.keyboardShowing;
}
- (void)setKeyboardToNavigationDistance:(CGFloat)keyboardToNavigationDistance {
[_keyboardMgr setKeyboardToNavigationDistance:keyboardToNavigationDistance];
}
- 装饰和策略之间的差异
“外表”变更(装饰) | “内容”变更(策略) |
---|---|
从外部变更 | 从内部变更 |
每个节点不知道变更 | 每个节点知道一组预定义的变更方式 |
15、责任链✅
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间发生耦合。此模式将这些对象连成一条链,并沿着这条链传递请求,知道有一个对象处理它为止。
何时使用责任链模式
- 有多个对象可以处理请求,而处理程序只有在运行时才能确定
- 向一组对象发送请求,而不想显式指定处理请求的特定处理程序
例:游戏中人物的链式防御层
- 盔甲1不知道如何应对攻击,所以把它传给下一个盔甲
- 盔甲2刚好知道如何应对这次攻击,化解了人物可能遭受的损伤
优点:
- 任何攻击处理程序都能在任何时间被添加/删除,而不会影响人物的其他行为
开发中的应用:
- 用户的信息录入校验(层层校验)
六、算法封装
16、模版方法✅
定义一个操作中算法的骨架,而将一些步骤延迟到子类中。模版方法使子类可以重定义算法的某些特定步骤而不改变算法的结构
何时使用模版方法
- 需要一次性实现算法的不变部分,并将可变的行为留给子类来实现
- 子类的共同行为应该被提取出来放到公共类中,以避免代码重复
- 需要控制子类的扩展
Cocoa Touch框架中用到的模版方法
- UIView类中的定制绘图(- (void)drawRect:(CGRect)rect)
简单总结:
- 将共有实现抽到父类方法
- 重写父类方法,实现自己特有,不需要执行[super text];
模版 | 委托(适配器) |
---|---|
父类定义一个一般算法,但缺少某些特定/可选的信息或算法,它通过这些缺少的信息或算法起到一个算法“食谱”的作用 | 委托(适配器)与预定义好的委托接口一起定义一个特定算法 |
缺少的信息由子类通过继承来提供 | 特定算法由任何对象通过对象组合来提供 |
17、策略✅
- iOS架构师_策略模式
定义一系列算法,把他们一个个封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化
- 把每个算法封装成一个对象,消除根据数据类型决定使用什么算法的一堆if-else或switch-case
- 如果代码中有很多条件语句,就可能意味着需要把它们重构成各种策略对象
- 优点是:解决代码的耦合度,但是为了解决代码耦合度,需要创建更多的类。
何时使用策略模式
- 一个类在其操作中使用多个条件语句来定义许多行为。我们可以把相关的条件分支移到它们自己的策略类中
- 需要算法的各种变体
- 需要避免把复杂的、与算法相关的数据结构暴露给客户端
应用
- collectionview布局
- UITextField验证
// 使用策略模式前
- (void)textFieldDidEndEditing:(UITextField *)textField {
if (textField == self.letterInput) { // 验证输入是否为字母
NSString *outputLatter = [self validateLatterInput:textField];
if (outputLatter) {
...
} else {
...
}
} else if (textField == self.numberInput) { // 验证输入的是否为数字
NSString *outputNumber = [self validateNumberInput:textField];
if (outputNumber) {
...
} else {
...
}
}
....
}
// 使用策略模式后:将所有逻辑判断都封装到类中
- (void)textFieldDidEndEditing:(UITextField *)textField {
if ([textField isKindOfClass:[CustomTextField class]]) {
[(CustomTextField *)textField validate];
}
}
18、命令
将请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作
何时使用命令模式
- 想让应用程序支持撤销与恢复
- 想让对象参数化一个动作以执行操作,并用不同命令对象来代替回调函数
- 想要在不同时刻对请求进行指定、排列和执行
- 想记录修改日志,这样在系统故障时,这些修改可在后来重做一遍
- 想让系统支持事物,事物封装了对数据的一系列修改。事物可以建模为命令对象
Cocoa Touch框架中使用命令模式
- NSUnvotaion
- NSUndoManager
命令模式还能做什么?
- 推迟调用器的执行
- 例,点击控制器的按钮,可以执行一个命令对戏那个,对另一个试图控制器进行某些操作。命令对象隐藏了与这些操作有关的所有细节
七、性能与对象访问
19、享元✅
运用共享技术有效地支持大量细粒度的对象
总结:相同的数据就拿来共享
何时使用享元模式
- 应用程序使用很多对象
- 在内存中保存对象回影响内存性能
- 对象的多数特有状态可以放到外部而轻量化
- 移除了外在对象之后,可以用较少的共享对象代替原来的那组对象
- 应用程序不依赖于对象标识,因为共享对象不能提供唯一的标识
关键组件
- 享元对象
- 保存享元对象的池
优点:节省内存(减少同一类对象的大量创建✨✨✨✨,节省内存意味着提升整体性能)
节省空间主要取决于这几个因素:
- 通过共享减少的对象总数
- 每个对象中内在状态的数量
- 外在状态是计算出来的还是保存的
应用:
- UITableViewCell、UICollectionViewCell
- 多线程的线程池
- 书中提到的“百花池”程序
20、代理
为其他对象提供一种代理以控制对这个对象的访问
- 我们平时使用的@protocol,遵循协议,成为代理,实现方法
@protocol WJIMObject
- (NSString *)imUUID;
- (NSString *)imAuthToken;
- (NSString *)imHost;
@end
Cocoa Touch框架中使用代理模式
- NSProxy
八、对象状态
21、备忘录
在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
何时使用备忘录模式
- 需要保存一个对象在某个时刻的状态,这样以后就可以恢复到先前的状态
- 用于获取状态的接口会暴露实现的细节,需要将其隐藏起来
Cocoa Touch框架中的备忘录模式
- 归档()
- 属性列表序列化
- 核心数据
九、复合设计模式
- MVC
- MVVM
- MVP
结尾:
- 参考:《Objective-C编程之道》、《大话数据结构》
- 关于MVC
- MVC本身并不是最基本的设计模式
- 它包含了若干更加基本的设计模式
- 在MVC中,基本设计模式互相配合,确定了各功能之间的协作
Cocoa的MVC用到的模式有:**组合、命令、中介者、策略、观察者**
- 其余参考文章:
菜鸟教程 - 设计模式
学习并理解 23 种设计模式