设计模式即编码的工程化,是代码设计经验的总结。使用设计模式可提高代码的复用性、可读性和可靠性。设计模式在各类面向对象的语言中基本相通,仅在具体的实现语法上略有差异,下面我们以OC为例来理解和研究一些常用的设计模式。
单例模式
保证程序运行中,一个类全局只有一个实例。它提供了对类对象的全局访问点,在整个过程中共享着一份资源。
应用:登录控制体系、网络请求、音乐播放、分享体系等。
+ (instancetype)sharedInstance {
static Singleton *singleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[Singleton alloc] init];
});
return singleton;
}
//严谨情况下应把copyWithZone和mutableCopyWithZone也重写
注:可将allocWithZone重写为dispatch_once实现单例,sharedInstance中调用默认的alloc init(alloc会默认调用allocWithZone),此时,单例可被继承,重写init方法实现子单例的扩展。如下:
static Singleton *_instance;
+ (id)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
)};
return _instance;
}
+ (instancetype)sharedInstance {
if(!_instance) {
_instance = [[Singleton alloc] init];
}
return _instance;
}
使用带参数的宏定义可以把单例模式的声明优化到只写一次,在各单例类中的.h、.m中只需调用宏定义即可。
最后补充一下iOS系统的单例类:UIApplication,UIScreen,NSNotificationCenter,NSFileManager,NSUserDefaults,NSURLCache,NSHTTPCookieStorage等。
工厂模式
简单工厂模式
一个工厂类,根据传入参数不同,决定初始化某个具体产品实例。
应用理解:一个简单四则运算计算器。如果不使用工厂模式,要实现加减乘除四个方法就需要new4个运算方法类,这显然不是一个完美的解决方案。此时,若有个产品基类BaseCalculate,它包括加减乘除四个产品类(Add,Minus,Multiply,Devide),在需要实例化运算对象的地方,使用运算工厂类CalcuteFactory,根据类型返回产品类的方法-(BaseCaculate *)createWithType:(id)type;生成对应运算的产品类对象。
缺点:增加或者修改产品类时,需在代码层次修改工厂类(此时需重新测试工厂类),不够灵活,不便扩展。
为解决上述缺点,出现了第二种工厂模式:
工厂方法模式
一个工厂类,一个产品类对应一个工厂子类(产品与工厂配套),即,扩展产品类别时需同时扩展工厂子类,与简单工厂模式相比,抽象了工厂类。工厂子类中重写抽象工厂类中的方法生产对应的产品子类。
缺点显而易见:大量的产品+工厂类,且产品和工厂间隔断严重无法复用相同代码。
于是抽象工厂模式应运而生:
抽象工厂模式
抽象工厂模式的最大特点就是有多个抽象产品类。找出某类产品的共性,设计出此类产品的抽象类(派生出多个具体产品类)。而其具有一个抽象工厂类和多个具体工厂类,每个具体工厂类都可创建多个具体产品类。
观察者模式
发布(publish)- 订阅(Subscribe)模式,用于一对多依赖关系中的解耦。对象可以通过注册,成为观察者(Observer),去订阅中心对象(Subject)的变化。Subject(被观察者)则需要实现观察者注册、数据更新通知、观察者移除三个基础方法。
NSNotification
注册观察者
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notice:) name:@"PostName" object:nil];
- (void)notice:(id)sender { NSLog(@"%@",sender); }
被观察者发出通知
[[NSNotificationCenterdefaultCenter] postNotificationName:@"PostName" object:nil];
移除通知
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"PostName" object:nil];
}
KVO
KVC、KVO小结和应用
代理模式
OC和Swift中的protocol
非正式协议
即Category,如NSString (StringFrame)。
代理与Block
1、block是让代码块以闭包(一个函数+其执行的外部上下文变量)的形式传递内容,实在是太轻量级了,适用于大多数异步和简单的回调。
2、当有多个方法回调时应当选用delegate会更清晰,如UITableView的delegate代理方法。
3、block会涉及到栈区到堆区的拷贝等操作,delegate只是定义了一个方法列表,在遵守了协议的对象的objc_protocol_list中添加了一个节点,运行时向对象发送消息即可。所以block在时间空间消耗都大于delegate,性能消耗较大。
4、代理更加面向过程,block更加面向结果。
策略模式
#结合现金支付计算策略实例详细说明#
1、定义一个抽象的、通用的算法(抽象策略类BaseStrategy)协议,此角色给出所有具体策略类所需的接口;
@protocol CashBase
-(CGFloat)acceptCash:(CGFloat)cash;
@end
2、让每个具体算法(继承Base的具体策略类NormalStrategy,SpecialStrategy)都遵循他的守则,提供了具体的算法实现了抽象策略类定义的接口;
@interface CashNormal : NSObject <CashBase>
@end
@implementation CashNormal
-(CGFloat)acceptCash:(CGFloat)cash { return cash; }
@end
@interface CaseReturn : NSObject <CashBase>
-(instancetype)initWithMoneyReturn:(CGFloat)moneyReturn;
@end
@implementation CaseReturn
-(instancetype)initWithMoneyReturn:(CGFloat)moneyReturn {
if (self) {
_moneyReturn = moneyReturn;
}
return self;
}
-(CGFloat)acceptCash:(CGFloat)cash {
return cash - self.moneyReturn;
}
@end
3、然后需要定义一个环境角色(Context类)用来持有一个Strategy的引用,配合简单工厂模式,根据入参决定调用哪个具体策略类算法;
@interface CashContext : NSObject
-(instancetype)initWithCashType:(CashType)type;
-(CGFloat)getResult:(CGFloat)money;
@end
@implementation CashContext
-(instancetype)initWithCashType:(CashType)type{
//根据type初始化算法实例
}
-(CGFloat)getResult:(CGFloat)money{
//算法实例调用策略接口
return [self.cashSuper acceptCash:money];
}
@end
4、最后在需要的地方调用Context对象。
CashContext * context = [[CashContext alloc] initWithCashType:CashTypeNormal];
NSLog(@"结果是%f",[context getResult:100]);
装饰模式
不修改原类代码的情况下,动态、透明的给一个对象增加新的行为和职责,Decorator比生成子类更加灵活,其目的是把功能分散,运行期间再动态组合。
装饰器的构成:1、Component,抽象的组件父类,声明了一些方法由子类进行重载;ConcreteComponent,具体的组件类,实现了组件接口,通常是被装饰的原始对象。
2、Decorator,装饰器父类(Component细化后的抽象类),用来持有原对象(被装饰对象)。ConcreteDecorator具体的装饰器类,具体实现要添加的功能,并内嵌Component操作,以装饰具体的组件对象。
在iOS中,分类(category)简单便捷的实现了装饰器设计模式所能达到的功能,但严格意义上讲并不符合装饰器模式的定义。
注:1、有些教程中说委托也是iOS中的一种装饰器模式,但我认为委托没有做到装饰模式定义中“不修改原类代码”的要求;2、分类中定义的方法不要和原有类的方法重名。
软件开发中还用很多常用的设计模式,如模板方法模式(提取算法可复用结构,延迟某些特定步骤到子类:如问答题的不变部分(题目)和变化部分(答案))、外观模式(为一套需要同时调用的子系统定义一个高层的综合接口,便于多个接口同时调用)、建造者模式、状态模式、适配器模式、备忘录模式、组合模式、迭代器模式、单列模式、桥接模式、命令模式、职责链模式、中介者模式、享元模式、解释器模式、访问者模式、原型模式(已有对象深拷贝申请新内存,生成新对象,需满足NSCopying协议)等。
最后提供一下容易乱入的MVC,MVVM等设计模式等链接,严格来说,它们应称为架构设计模式,而非广义上的编码设计模式。
架构设计模式之MVC和MVVM
写在最后
没有最后 <( ̄︶ ̄)>