參考:《Objective-C編程之道》html
這邊文章是在學習了《Objective-C編程之道》後的作的一些筆記,在其中參考了其餘相關書籍或者其餘前輩的例子以及一些文章內容,有引用到的地方我已經儘可能添加原文連接或者引用了,若是發現本文侵權可聯繫刪除.node
此篇文章主要是對各個模式對介紹,此處引用對例子是我認爲比較合適比較容易理解對例子,不過也會有一些片面,沒法徹底解決各個模式.這篇文章總的來講適合用來入門設計模式.ios
此篇文章可能會有不少不足之處,歡迎你們指出批評,也歡迎你們一塊兒來學習探討.c++
使用原型實例指定建立對象的種類,並經過複製這個原型建立新的對象.git
數據模型間的複製github
Student *s1 = [[Student alloc] init];
s1.age = 18;
s1.name = @"s1";
s1.address = @"adress";
s1.size = CGSizeMake(100, 100);
s1.teacher = p1;
s1.friends = @[p1, p2];
s1.girlfriends = [[NSMutableArray alloc] initWithArray:s1.friends];
s1.other = @[s1.friends, s1.girlfriends];
Student *s2 = [s1 copy];
複製代碼
這裏經過copy
可以拷貝獲得一個數據一致的Student
.算法
這裏須要特別注意的是淺複製和深複製之間的區別.macos
+ (UITableViewCell *)cellWithText:(NSString *)text {
LabelTableViewCell *cell = [[LabelTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];
cell.textLabel.text = text;
return cell;
}
+ (UITableViewCell *)cellWithImage:(UIImage *)img {
ImageTableViewCell *cell = [[ImageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.imageView.image = img;
return cell;
}
+ (UITableViewCell *)cellWithSelect:(BOOL)isSelect {
SwitchTableViewCell *cell = [[SwitchTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
[cell setSwitchSelect:isSelect];
return cell;
}
複製代碼
在iOS
中能夠參考系統類NSNumber
;編程
- (NSNumber *)initWithChar:(char)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedChar:(unsigned char)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithShort:(short)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedShort:(unsigned short)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithInt:(int)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedInt:(unsigned int)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithLong:(long)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedLong:(unsigned long)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithLongLong:(long long)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedLongLong:(unsigned long long)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithFloat:(float)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithDouble:(double)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithBool:(BOOL)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithInteger:(NSInteger)value API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedInteger:(NSUInteger)value API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
複製代碼
提供一個建立一系列相關或相互依賴對象的接口,而無需指定他們具體的類.canvas
Cell
抽象工廠類AbstractFactory
@interface AbstractFactory : NSObject
+ (UITableViewCell *)cellWithText:(NSString *)text;
+ (UITableViewCell *)cellWithImage:(UIImage *)img;
@end
複製代碼
繼承AbstractFactory
的普通工廠類CellFactory
@implementation CellFactory
+ (UITableViewCell *)cellWithText:(NSString *)text {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];
cell.textLabel.text = text;
return cell;
}
+ (UITableViewCell *)cellWithImage:(UIImage *)img {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.imageView.image = img;
return cell;
}
@end
複製代碼
繼承AbstractFactory
的可點擊Cell
工廠類CellFactory
@implementation ClickFactory
+ (UITableViewCell *)cellWithText:(NSString *)text {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];
cell.textLabel.text = text;
cell.textLabel.userInteractionEnabled = YES;
return cell;
}
+ (UITableViewCell *)cellWithImage:(UIImage *)img {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.imageView.image = img;
cell.imageView.userInteractionEnabled = YES;
return cell;
}
@end
複製代碼
控制器實現
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
switch (indexPath.row % 2) {
case 0:
if (indexPath.row > 2) {
cell = [ClickFactory cellWithText:[NSString stringWithFormat:@"%ld", indexPath.row]];
} else {
cell = [CellFactory cellWithText:[NSString stringWithFormat:@"%ld", indexPath.row]];
}
break;
case 1:
if (indexPath.row > 2) {
cell = [ClickFactory cellWithImage:[UIImage imageNamed:@"12.png"]];
} else {
cell = [CellFactory cellWithImage:[UIImage imageNamed:@"12.png"]];
}
break;
case 2:
break;
default:
break;
}
return cell;
}
複製代碼
抽象工廠與工廠模式在許多方面都很是相似.不少人經常搞不清楚應該在何時用哪個.兩個模式都用於相同的目的:建立對象而不讓客戶端知曉返回了什麼確切的具體對象.
抽象工廠 | 工廠方法 |
---|---|
經過對象組合建立抽象產品 | 經過類繼承建立抽象產品 |
建立多系列產品 | 建立一種產品 |
必須修改父類的接口才能支持新的產品 | 子類化建立者並重載工廠方法以建立新產品 |
抽象工廠模式是一種極爲常見的設計模式.它是最基本的,由於它能夠涉及許多類型的對象建立.一系列相關類的好的模式,應該做爲一種抽象,不爲客戶端所見.抽象工廠能夠順暢地提供這種抽象,而不暴露建立過程當中任何沒必要要的細節或所建立對象的確切類型.
將一個複雜對象的構建與它的表現分離,使得一樣的構建過程能夠建立不一樣的表現.
須要建立涉及各類部件的複雜對象.建立對象的算法應該獨立於部件的裝配方式.常見例子是構建組合對象.
構建過程須要以不一樣的方式構建對象.
定義指導者類SandwichDirector
@interface SandwichDirector : NSObject
// 具體的生成類
@property (nonatomic, strong) id<SandwichBuilder> concreteBuilder;
// 生成動做
- (void)construct;
@end
規定製做三明治流程
@implementation SandwichDirector
- (void)construct {
[_concreteBuilder prepareBread];
[_concreteBuilder addMeat];
}
@end
複製代碼
@protocol SandwichBuilder <NSObject>
- (void)prepareBread;
- (void)addMeat;
@end
複製代碼
基礎三明治類Sandwich
@interface Sandwich : NSObject
@property (nonatomic, copy) NSString *breadType;
@property (nonatomic, copy) NSString *meatType;
@end
複製代碼
製做魯賓三明治類SandwichConcreteBuilder
@interface SandwichConcreteBuilder : NSObject<SandwichBuilder>
// 獲取生成的三明治
- (Sandwich *)getSandwich;
@end
@interface SandwichConcreteBuilder ()
@property (nonatomic, strong) Sandwich *reubenSandwich;
@end
@implementation SandwichConcreteBuilder
- (instancetype)init {
if (self = [super init]) {
_reubenSandwich = [[Sandwich alloc] init];
}
return self;
}
- (void)prepareBread {
_reubenSandwich.breadType = @"黑麥麪包";
}
- (void)addMeat {
_reubenSandwich.meatType = @"醃牛肉";
}
- (id)getSandwich {
return _reubenSandwich;
}
複製代碼
獲取魯賓三明治
SandwichDirector *director = [[SandwichDirector alloc] init];
SandwichConcreteBuilder *builder = [[SandwichConcreteBuilder alloc] init];
[director setConcreteBuilder:builder];
[director construct];
Sandwich *sandwich = [builder getSandwich];
NSLog(@"麪包:%@\n肉:%@", sandwich.breadType, sandwich.meatType);
複製代碼
生成器模式能幫助構建涉及部件與表現的各類組合的對象.沒有這一模式,知道構建對象所需細節的Director
可能最終會變成一個龐大的"神"類,帶有無數用於構建同一個類的各類表現的內嵌算法.
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點.
@interface Singleton : NSObject
/// 統一的接口
+ (instancetype)defaultSingleton;
@end
static Singleton *_singleton;
@implementation Singleton
+ (instancetype)defaultSingleton {
return [[self alloc] init];
}
- (instancetype)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_singleton = [super init];
});
return _singleton;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (!_singleton) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_singleton = [super allocWithZone:zone];
});
}
return _singleton;
}
- (id)copy {
return _singleton;
}
- (id)mutableCopy {
return _singleton;
}
@end
複製代碼
由於單例只會存在一個對象,因此sigleton0
和sigleton1
是等價的.
Singleton *sigleton0 = [[Singleton alloc] init];
NSLog(@"%@", sigleton0);
Singleton *sigleton1 = [Singleton defaultSingleton];
NSLog(@"%@", sigleton1);
NSLog(@"%@", [sigleton0 copy]);
NSLog(@"%@", [sigleton1 mutableCopy]);
複製代碼
幾乎在任何類型的應用程序中,單例模式都極爲常見,並不僅限於iOS應用程序開發.只要應用程序須要用集中式的類來協調其服務,這個類就應生成單一的實例,而不是多個實例.
將一個類的接口轉換成客戶但願的另一個接口.適配器模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做.
類適配器類圖
對象適配器類圖
初始請求目標執行類
@interface Target : NSObject
- (void)request;
@end
@implementation Target
- (void)request {
NSLog(@"請求");
}
@end
複製代碼
新的執行類
@interface Adaptee : NSObject
- (void)specialRequest;
@end
@implementation Adaptee
- (void)specialRequest {
NSLog(@"特殊請求");
}
@end
複製代碼
適配器類實現
@interface Adapter : Target
@property (nonatomic, strong) Adaptee *adaptee;
@end
@implementation Adapter
- (instancetype)init {
if (self = [super init]) {
self.adaptee = [[Adaptee alloc] init];
}
return self;
}
- (void)request {
[self.adaptee specialRequest];
}
@end
複製代碼
控制器調用
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Target *target = [[Adapter alloc] init];
[target request];
}
@end
複製代碼
適配器模式能夠用做爲目標接口的協議來實現,協議定義客戶端要求的,也是適配器能夠保證的一些標準行爲.協議是Object-C
中語言級別的功能,經過它能夠定義用做適配器模式的實例的接口.
將抽象部分與它的實現部分分離,使它們均可以獨立地變化.
接口協議MessageImplement
@protocol MessageImplement <NSObject>
- (void)sendMessage:(NSString *)message;
@end
複製代碼
抽象信息類AbstractMessage
/// 抽象信息類
@interface AbstractMessage : NSObject
@property (nonatomic, strong) id<MessageImplement> messageIm;
- (void)send:(NSMutableString *)message;
- (instancetype)initWithImplement:(id<MessageImplement>)implement;
@end
@implementation AbstractMessage
- (instancetype)initWithImplement:(id<MessageImplement>)implement {
if (self = [super init]) {
self.messageIm = implement;
}
return self;
}
- (void)send:(NSMutableString *)message {
}
@end
複製代碼
普通訊息類CommonMessage
/// 普通訊息類
@interface CommonMessage : AbstractMessage
@end
@implementation CommonMessage
- (void)send:(NSMutableString *)message {
[message insertString:@"【普通消息:" atIndex:0];
[message appendString:@"】"];
[self.messageIm sendMessage:message];
}
@end
複製代碼
QQ信息類QQMessage
/// QQ信息類
@interface QQMessage : AbstractMessage
@end
@implementation QQMessage
- (void)send:(NSMutableString *)message {
[message insertString:@"【QQ消息:" atIndex:0];
[message appendString:@"】"];
[self.messageIm sendMessage:message];
}
@end
複製代碼
系統發送類MessageSMS
/// 系統發送類
@interface MessageSMS : NSObject<MessageImplement>
@end
@implementation MessageSMS
- (void)sendMessage:(NSString *)message {
NSLog(@"使用系統發送消息:%@", message);
}
@end
複製代碼
手機發送類MessageTEL
/// 手機發送類
@interface MessageTEL : NSObject<MessageImplement>
@end
@implementation MessageTEL
- (void)sendMessage:(NSString *)message {
NSLog(@"使用手機發送消息:%@", message);
}
@end
複製代碼
控制器實現
- (void)viewDidLoad {
[super viewDidLoad];
MessageSMS *sms = [[MessageSMS alloc] init];
MessageTEL *tel = [[MessageTEL alloc] init];
QQMessage *messageSMS = [[QQMessage alloc] initWithImplement:sms];
[messageSMS send:[[NSMutableString alloc] initWithString:@"SMS->QQ"]];
NSLog(@"\n");
QQMessage *messageTEL = [[QQMessage alloc] initWithImplement:tel];
[messageTEL send:[[NSMutableString alloc] initWithString:@"TEL->QQ"]];
NSLog(@"\n");
CommonMessage *commonSMS = [[CommonMessage alloc] initWithImplement:sms];
[commonSMS send:[[NSMutableString alloc] initWithString:@"SMS->Common"]];
NSLog(@"\n");
CommonMessage *commonTEL = [[CommonMessage alloc] initWithImplement:tel];
[commonTEL send:[[NSMutableString alloc] initWithString:@"TEL->Common"]];
}
@end
複製代碼
結果展現
2019-04-10 22:25:22.611392+0800 BridgePattern[46027:874792] 使用系統發送消息:【QQ消息:SMS->QQ】
2019-04-10 22:25:22.611661+0800 BridgePattern[46027:874792]
2019-04-10 22:25:25.998905+0800 BridgePattern[46027:874792] 使用手機發送消息:【QQ消息:TEL->QQ】
2019-04-10 22:25:25.999066+0800 BridgePattern[46027:874792]
2019-04-10 22:25:25.999229+0800 BridgePattern[46027:874792] 使用系統發送消息:【普通消息:SMS->Common】
2019-04-10 22:25:25.999343+0800 BridgePattern[46027:874792]
2019-04-10 22:25:25.999468+0800 BridgePattern[46027:874792] 使用手機發送消息:【普通消息:TEL->Common】
複製代碼
橋接模式是把一個接口適配到不一樣接口的一種方式.
爲系統中的一組接口提供一個統一的接口.外觀定義一個高層接口,讓子系統更易於使用.
子系統正逐漸變得複雜.應用模式的過程當中演化出許多類.可使用外觀爲這些子系統提供一個簡單的接口.
可使用外觀對子系統進行分層.每一個子系統級別有一個外觀做爲入口點.讓它們經過其外觀進行通訊,能夠簡化它們的依賴關係.
定義汽車類Car
@interface Car : NSObject
- (void)releaseBreaks;
- (void)changeGears;
- (void)pressAccelerator;
- (void)pressBreaks;
- (void)releaseAccelerator;
@end
@implementation Car
- (void)releaseBreaks {
NSLog(@"鬆開剎車");
}
- (void)changeGears {
NSLog(@"換擋");
}
- (void)pressAccelerator {
NSLog(@"踩油門");
}
- (void)pressBreaks {
NSLog(@"鬆油門");
}
- (void)releaseAccelerator {
NSLog(@"剎車");
}
@end
複製代碼
計價器類Taximeter
實現
@interface Taximeter : NSObject
- (void)start;
- (void)stop;
@end
@implementation Taximeter
- (void)start {
NSLog(@"啓動計價器");
}
- (void)stop {
NSLog(@"停下計價器");
}
複製代碼
外觀類CabDriver
的driveToLocation
接口整合Taximeter
Car
子系統爲外部提供接口
@interface CabDriver : NSObject
- (void)driveToLocation:(CGPoint)x;
@end
@implementation CabDriver
- (void)driveToLocation:(CGPoint)x {
// 啓動計價器
Taximeter *meter = [[Taximeter alloc] init];
[meter start];
// 操做車輛,直到抵達位置
Car *car = [[Car alloc] init];
[car releaseBreaks];
[car changeGears];
[car pressAccelerator];
// 當到達了位置x,就停下車和計價器
[car releaseAccelerator];
[car pressBreaks];
[meter stop];
}
@end
複製代碼
外觀有助於提供一種更爲簡潔的方式來使用子系統中的這些類.處理這些子系統類的默認行爲,可能只是定義在外觀的一個簡便方法,而沒必要直接去使用這些類時即可以使用外觀設計模式了.
用一個對象來封裝一系列對象的交互方式.中介者使對象不須要顯式地相互引用,從而使其耦合鬆散,並且能夠獨立地改變它們之間的交互.
原文: Objective-C設計模式——中介者Mediator
中介者模式很好的詮釋了迪米特法則,任意兩個不相關的對象之間若是須要關聯,那麼須要經過第三個類來進行。中介者就是把一組對象進行封裝,屏蔽了類之間的交互細節,使不一樣的類直接不須要持有對方引用也能夠進行訪問。
中介者Mediator
會持有同事類(就是須要處理交互邏輯的對象)Colleague
的引用,同時每一個colleague
也會持有Mediator
一份引用。這樣colleague
若是有任何和別的類交互的請求就會發給Mediator
,對改組對象進行了解耦合。其實咱們平時常常寫的視圖控制器自己就是一個中介者,它來控制着不一樣對象之間的交互行爲。
中介者Mediator
@interface Mediator : NSObject
@property (nonatomic, weak) ColleagueA *collA;
@property (nonatomic, weak) ColleagueB *collB;
- (void)notify:(id)obj;
@end
@implementation Mediator
- (void)notify:(id)obj {
if (obj == self.collA) {
[self.collB notified:@"B Notified"];
} else {
[self.collA notified:@"A Notified"];
}
}
@end
複製代碼
同事類基類Colleague
@interface Colleague : NSObject
@property (nonatomic, weak) Mediator *mediator;
- (instancetype)initWithMediator:(Mediator *)mediator;
- (void)notifyAnother;
- (void)notified:(NSString *)message;
@end
@implementation Colleague
- (instancetype)initWithMediator:(Mediator *)mediator {
if (self = [super init]) {
self.mediator = mediator;
}
return self;
}
- (void)notifyAnother {
[self.mediator notify:self];
}
- (void)notified:(NSString *)message {
NSLog(@"%@", message);
}
@end
複製代碼
同事類AColleagueA
@interface ColleagueA : Colleague
@end
@implementation ColleagueA
@end
複製代碼
設置同事類BColleagueB
與同事類A一致
控制器實現
Mediator *mediator = [[Mediator alloc] init];
ColleagueA *caA = [[ColleagueA alloc] initWithMediator:mediator];
ColleagueB *caB = [[ColleagueB alloc] initWithMediator:mediator];
mediator.collA = caA;
mediator.collB = caB;
[caA notifyAnother];
[caB notifyAnother];
複製代碼
結果展現
2019-04-18 23:27:55.290764+0800 MediatorPattern[86780:1876003] B Notified
2019-04-18 23:27:55.290933+0800 MediatorPattern[86780:1876003] A Notified
複製代碼
雖然對於處理應用程序的行爲分散於不一樣對象而且對象相互依存的狀況,中介者模式很是有用,可是應當注意避免讓中介者過於龐大而難以維護.若是已經這樣了,能夠考慮使用另外一種設計模式把它分解.要創造性地混用和組合各類設計模式解決同一個問題.
擴展: 組件化和模塊化間用於各個模塊的跳轉交互所用的就是中介者模式.
Cocoa Touch
框架中使用的觀察者模式--通知和鍵-值觀察定義模型類Person
UIKIT_EXTERN NSNotificationName const PersonNameWillChangeNotification;
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
NSNotificationName const PersonNameWillChangeNotification = @"PersonNameWillChangeNotification";
@implementation Person
- (void)setName:(NSString *)name {
_name = name;
// 發送通知
[[NSNotificationCenter defaultCenter] postNotificationName:PersonNameWillChangeNotification object:_name];
}
@end
複製代碼
控制器實現
對象賦初值
_xiaoming = [[Person alloc] init];
_xiaoming.name = @"xiaoming";
_xiaoming.age = 11;
複製代碼
經過通知監聽名字更替
// 通知更換了名字
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changePersonName:) name:PersonNameWillChangeNotification object:nil];
_xiaoming.name = @"wangxiaoming";
- (void)changePersonName:(NSNotification *)noti {
NSLog(@"newName = %@", noti.object);
}
//2019-03-29 22:39:23.243012+0800 ObserverPattern[9755:1317800] newName = wangxiaoming
複製代碼
經過鍵-值觀察監聽年齡變化
[self.xiaoming addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
_xiaoming.age = 28;
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"newAge = %ld", [change[NSKeyValueChangeNewKey] integerValue]);
//2019-03-29 22:39:23.243229+0800 ObserverPattern[9755:1317800] newAge = 28
}
複製代碼
具體實現
- (void)viewDidLoad {
[super viewDidLoad];
_xiaoming = [[Person alloc] init];
_xiaoming.name = @"xiaoming";
_xiaoming.age = 11;
// 通知更換了名字
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changePersonName:) name:PersonNameWillChangeNotification object:nil];
// 監聽xiaoming年齡變化
[self.xiaoming addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
_xiaoming.name = @"wangxiaoming";
_xiaoming.age = 28;
// 移除相關監聽
[[NSNotificationCenter defaultCenter] removeObserver:self name:PersonNameWillChangeNotification object:nil];
[self.xiaoming removeObserver:self forKeyPath:@"age"];
}
- (void)changePersonName:(NSNotification *)noti {
NSLog(@"newName = %@", noti.object);
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"newAge = %ld", [change[NSKeyValueChangeNewKey] integerValue]);
}
複製代碼
通知和KVO
是iOS
中經常使用的數據交互傳輸的兩種手段.特別是在需求須要雙向綁定的狀況下KVO
是很是適合使用的.開發中可使用KVO
來實現類MVVM
這樣的框架.
將對象組合成樹形結構以表示"部分-總體"的層次結構.組合使得用戶對單個對象和組合對象的使用具備一致性.
原文: 組合模式(Composite)
定義基礎組件類Components
@interface Components : NSObject
@property (nonatomic, copy) NSString *name;
- (instancetype)initWithName:(NSString *)name;
- (void)addComponent:(Components *)component;
- (void)removeComponent:(Components *)component;
- (void)display:(NSInteger)depth;
@end
@implementation Components
- (instancetype)initWithName:(NSString *)name {
if (self = [super init]) {
self.name = name;
}
return self;
}
- (void)addComponent:(Components *)component {
return;
}
- (void)removeComponent:(Components *)component {
return;
}
- (void)display:(NSInteger)depth {
return;
}
@end
複製代碼
定義葉子的子類Leaf
@interface Leaf : Components
@end
@implementation Leaf
- (void)addComponent:(Components *)component {
NSLog(@"can not add");
}
- (void)removeComponent:(Components *)component {
NSLog(@"can not remove");
}
- (void)display:(NSInteger)depth {
NSLog(@"[level:%ld_%@]",depth, self.name);
}
@end
複製代碼
定義節點類Composite
,節點可包含新節點或新葉子
@interface Composite : Components
@property (nonatomic, strong) NSMutableArray *childArr;
@end
@implementation Composite
- (instancetype)initWithName:(NSString *)name {
if (self = [super initWithName:name]) {
self.childArr = [[NSMutableArray alloc] init];
}
return self;
}
- (void)addComponent:(Components *)component {
[self.childArr addObject:component];
}
- (void)removeComponent:(Components *)component {
[self.childArr removeObject:component];
}
- (void)display:(NSInteger)depth {
NSLog(@"[level:%ld_%@]",depth, self.name);
for (Components *component in self.childArr) {
[component display:depth + 1];
}
}
@end
複製代碼
控制器實現
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Composite *root = [[Composite alloc] initWithName:@"root"];
[root addComponent:[[Leaf alloc] initWithName:@"leaf-A"]];
[root addComponent:[[Leaf alloc] initWithName:@"leaf-B"]];
Composite *comp0 = [[Composite alloc] initWithName:@"comp0"];
[comp0 addComponent:[[Leaf alloc] initWithName:@"comp0-A"]];
[comp0 addComponent:[[Leaf alloc] initWithName:@"comp0-B"]];
[root addComponent:comp0];
Composite *comp1 = [[Composite alloc] initWithName:@"comp1"];
[comp1 addComponent:[[Leaf alloc] initWithName:@"comp1-A"]];
[comp1 addComponent:[[Leaf alloc] initWithName:@"comp1-B"]];
[root addComponent:comp1];
Composite *comp00 = [[Composite alloc] initWithName:@"comp00"];
[comp00 addComponent:[[Leaf alloc] initWithName:@"comp00-A"]];
[comp00 addComponent:[[Leaf alloc] initWithName:@"comp00-B"]];
[comp0 addComponent:comp00];
Leaf *leafC = [[Leaf alloc] initWithName:@"leaf-C"];
[root addComponent:leafC];
[root addComponent:[[Leaf alloc] initWithName:@"leaf-D"]];
[root display:0];
NSLog(@"-------- 刪除leaf-C --------");
// 刪除leaf-C
[root removeComponent:leafC];
[root display:0];
NSLog(@"-------- 刪除comp0 --------");
// 刪除comp0
[root removeComponent:comp0];
[root display:0];
}
@end
複製代碼
結果展現
[level:0_root]
[level:1_leaf-A]
[level:1_leaf-B]
[level:1_comp0]
[level:2_comp0-A]
[level:2_comp0-B]
[level:2_comp00]
[level:3_comp00-A]
[level:3_comp00-B]
[level:1_comp1]
[level:2_comp1-A]
[level:2_comp1-B]
[level:1_leaf-C]
[level:1_leaf-D]
-------- 刪除leaf-C --------
[level:0_root]
[level:1_leaf-A]
[level:1_leaf-B]
[level:1_comp0]
[level:2_comp0-A]
[level:2_comp0-B]
[level:2_comp00]
[level:3_comp00-A]
[level:3_comp00-B]
[level:1_comp1]
[level:2_comp1-A]
[level:2_comp1-B]
[level:1_leaf-D]
-------- 刪除comp0 --------
[level:0_root]
[level:1_leaf-A]
[level:1_leaf-B]
[level:1_comp1]
[level:2_comp1-A]
[level:2_comp1-B]
[level:1_leaf-D]
複製代碼
Cocoa Touch
框架中,UIView
被組織成一個組合結構.每一個UIView
的實例能夠包含UIView
的其餘實例,造成統一的樹形結構.讓客戶端對單個UIView
對象和UIView
的組合統一對待.
組合模式的主要意圖是讓樹形結構中的每一個節點具備相同的抽象接口.這樣整個結構可做爲一個統一的抽象結構使用,而不暴露其內部展現.對每一個節點(葉節點或組合體)的任何操做,能夠經過協議或抽象基類中定義的相同接口來進行.
提供一種方法順序訪問一個聚合對象中各個元素,而又不須要暴露該對象的內部表示.
List
與ListIterator
之間關係的類圖
抽象列表與迭代器之間關係的類圖
參考:iOS設計模式--迭代器
// 系統迭代器
NSArray *arr = @[@"A", @"B", @"C", @"D"];
NSEnumerator *enumerator = [arr objectEnumerator];
NSString *obj = nil;
while ((obj = enumerator.nextObject)) {
NSLog(@"%@", obj);
}
複製代碼
定義節點類Node
@interface Node : NSObject
/// 下一節點
@property (nonatomic, strong) Node *nextNode;
@property (nonatomic, strong) NSString *nodeName;
+ (instancetype)nodeWithName:(NSString *)name;
@end
@implementation Node
+ (instancetype)nodeWithName:(NSString *)name {
Node *node = [[Node alloc] init];
node.nodeName = name;
return node;
}
@end
複製代碼
定義鏈表類LinkedList
@interface LinkedList : NSObject
@property (nonatomic, strong, readonly) Node *firstNode;
@property (nonatomic, assign, readonly) NSInteger count;
- (void)addItem:(NSString *)item;
@end
@interface LinkedList ()
@property (nonatomic, strong) Node *firstNode;
@property (nonatomic, assign) NSInteger count;
@end
@implementation LinkedList
- (void)addItem:(NSString *)item {
if (self.firstNode == nil) {
self.firstNode = [Node nodeWithName:item];
self.count++;
} else {
[self addItem:item node:self.firstNode];
}
}
- (void)addItem:(NSString *)item node:(Node *)node {
if (!node.nextNode) {
node.nextNode = [Node nodeWithName:item];
self.count++;
} else {
[self addItem:item node:node.nextNode];
}
}
@end
複製代碼
定義協議IteratorProtocol
@protocol IteratorProtocol <NSObject>
// 下一個元素
- (id)nextObject;
@end
複製代碼
定義迭代器類LinkedListIterator
@interface LinkedListIterator : NSObject<IteratorProtocol>
+ (instancetype)linkedListIteratorWithLinkedList:(LinkedList *)linkedList;
@end
@interface LinkedListIterator ()
@property (nonatomic, strong) LinkedList *linkedList;
@property (nonatomic, strong) Node *currentNode;
@end
@implementation LinkedListIterator
+ (instancetype)linkedListIteratorWithLinkedList:(LinkedList *)linkedList {
LinkedListIterator *iterator = [[LinkedListIterator alloc] init];
iterator.linkedList = linkedList;
return iterator;
}
- (id)nextObject {
if (!self.currentNode) {
self.currentNode = self.linkedList.firstNode;
} else {
self.currentNode = self.currentNode.nextNode;
}
return self.currentNode;
}
@end
複製代碼
控制器實現
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 系統迭代器
NSArray *arr = @[@"A", @"B", @"C", @"D"];
NSEnumerator *enumerator = [arr objectEnumerator];
NSString *obj = nil;
while ((obj = enumerator.nextObject)) {
NSLog(@"%@", obj);
}
NSLog(@"------------------------------------");
LinkedList *list = [[LinkedList alloc] init];
[list addItem:@"A"];
[list addItem:@"B"];
[list addItem:@"C"];
[list addItem:@"D"];
LinkedListIterator *iterator = [LinkedListIterator linkedListIteratorWithLinkedList:list];
Node *node;
while ((node = iterator.nextObject)) {
NSLog(@"%@", node.nodeName);
}
}
@end
複製代碼
結果展現
2019-04-07 23:00:13.950111+0800 IteratorPattern[10885:90208] A
2019-04-07 23:00:13.950321+0800 IteratorPattern[10885:90208] B
2019-04-07 23:00:13.950442+0800 IteratorPattern[10885:90208] C
2019-04-07 23:00:13.950584+0800 IteratorPattern[10885:90208] D
2019-04-07 23:00:13.950706+0800 IteratorPattern[10885:90208] ------------------------------------
2019-04-07 23:00:13.950840+0800 IteratorPattern[10885:90208] A
2019-04-07 23:00:13.951111+0800 IteratorPattern[10885:90208] B
2019-04-07 23:00:13.951952+0800 IteratorPattern[10885:90208] C
2019-04-07 23:00:13.953016+0800 IteratorPattern[10885:90208] D
複製代碼
蘋果公司用本身的命名規則"枚舉器/枚舉"改寫了迭代器模式,用於相關基礎類的各類方法.
基礎框架中的NSEnumerator
類實現了迭代器模式. NSArray
、NSSet
、NSDictionary
這樣的集合類,定義了返回與集合的類型相應的NSEnumerator
子類實例的方法.
迭代器模式與訪問者模式有些相似,尤爲是把遍歷算法放到訪問者模式中或者在遍歷聚合體時讓內部迭代器對元素執行操做的時候.組合模式經常依靠迭代器來遍歷其遞歸結構.多態的迭代器依靠工廠方法來實例化適當的迭代器具體子類.
表示一個做用於某個對象結構中的各元素的操做.它讓咱們能夠在不改變各元素的類的前提下定義做用於這些元素的新操做.
協議類Mark
// 不論線條仍是點,其實都是在介質上留下的標誌(Mark),它爲全部具體類定義了屬性和方法
@protocol Mark <NSObject>
@property (nonatomic, assign) CGPoint location;
@property (nonatomic, assign) CGSize size;
@property (nonatomic, strong) id<Mark> lastChild;
- (void)addMark:(id<Mark>)mark;
- (void)removeMark:(id<Mark>)mark;
- (void)acceptMarkVisitor:(id<MarkVisitor>)visitor;
@end
複製代碼
組件類點Dot
// 點,組件只有一個點,那麼它會表現爲一個實心圓,在屏幕上表明一個點
@interface Dot : NSObject<Mark>
@end
@implementation Dot
@synthesize location;
@synthesize lastChild;
@synthesize size;
- (void)addMark:(id<Mark>)mark {}
- (void)removeMark:(id<Mark>)mark {}
- (id<Mark>)lastChild {
return nil;
}
- (void)acceptMarkVisitor:(id<MarkVisitor>)visitor {
[visitor visitDot:self];
}
@end
複製代碼
組件類頂點Vertex
// 頂點,鏈接起來的一串頂點,被繪製成鏈接起來的線條
@interface Vertex : NSObject<Mark>
@end
@implementation Vertex
@synthesize location;
@synthesize lastChild;
@synthesize size;
- (void)addMark:(id<Mark>)mark {}
- (void)removeMark:(id<Mark>)mark {}
- (id<Mark>)lastChild {
return nil;
}
- (void)acceptMarkVisitor:(id<MarkVisitor>)visitor {
[visitor visitVertex:self];
}
@end
複製代碼
組件類線條Stroke
@interface Stroke ()
@property (nonatomic, strong) NSMutableArray<id<Mark>> *children;
@end
@implementation Stroke
@dynamic location;
- (instancetype)init {
if (self = [super init]) {
_children = [[NSMutableArray alloc] init];
}
return self;
}
- (void)addMark:(id<Mark>)mark {
[_children addObject:mark];
}
- (void)removeMark:(id<Mark>)mark {
[_children removeObject:mark];
}
- (void)setLocation:(CGPoint)location {
}
- (CGPoint)location {
if (_children.count == 0) {
return CGPointZero;
} else {
return self.children.firstObject.location;
}
}
- (id<Mark>)lastChild {
return _children.lastObject;
}
- (void)acceptMarkVisitor:(id<MarkVisitor>)visitor {
for (id<Mark> dot in self.children) {
[dot acceptMarkVisitor:visitor];
}
[visitor visitStroke:self];
}
@end
複製代碼
定義訪問者接口類MarkVisitor
// 訪問者接口
@protocol MarkVisitor <NSObject>
- (void)visitMark:(id<Mark>)mark;
- (void)visitVertex:(Vertex *)vertex;
- (void)visitDot:(Dot *)dot;
- (void)visitStroke:(Stroke *)stroke;
@end
複製代碼
定義繪製類MarkRenderer
// 具體的訪問者,MarkRenderer繪製訪問者,它是對這些點和先進行繪製操做的
@interface MarkRenderer : NSObject<MarkVisitor>
- (instancetype)initWithCGContext:(CGContextRef)context;
@end
@interface MarkRenderer ()
@property (nonatomic, assign) CGContextRef context;
@property (nonatomic, assign) BOOL shouldMoveContextToDot;
@end
@implementation MarkRenderer
- (instancetype)initWithCGContext:(CGContextRef)context {
if (self = [super init]) {
self.context = context;
self.shouldMoveContextToDot = YES;
}
return self;
}
- (void)visitMark:(id<Mark>)mark {
}
- (void)visitVertex:(Vertex *)vertex {
CGFloat x = vertex.location.x;
CGFloat y = vertex.location.y;
if (self.shouldMoveContextToDot) {
CGContextMoveToPoint(self.context, x, y);
self.shouldMoveContextToDot = NO;
} else {
CGContextAddLineToPoint(self.context, x, y);
}
}
- (void)visitDot:(Dot *)dot {
CGFloat x = dot.location.x;
CGFloat y = dot.location.y;
CGRect frame = CGRectMake(x, y, 2, 2);
CGContextSetFillColorWithColor(self.context, [[UIColor blackColor] CGColor]);
CGContextFillEllipseInRect(self.context, frame);
}
- (void)visitStroke:(Stroke *)stroke {
CGContextSetStrokeColorWithColor(self.context, [UIColor blueColor].CGColor);
CGContextSetLineWidth(self.context, 1);
CGContextSetLineCap(self.context, kCGLineCapRound);
CGContextStrokePath(self.context);
self.shouldMoveContextToDot = YES;
}
@end
複製代碼
定義用於展現的視圖CanvasView
@interface CanvasView : UIView
@property (nonatomic, strong) id<Mark> mark;
@end
@implementation CanvasView
- (void)setMark:(id<Mark>)mark {
_mark = mark;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
MarkRenderer *markRender = [[MarkRenderer alloc] initWithCGContext:context];
[self.mark acceptMarkVisitor:markRender];
}
@end
複製代碼
控制器實現
@interface ViewController ()
@property (nonatomic, strong) id<Mark> parentMark;
@property (nonatomic, strong) CanvasView *canvasView;
@property (nonatomic, assign) CGPoint startPoint;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.parentMark = [[Stroke alloc] init];
[self setupView];
}
- (void)setupView
{
CanvasView *canvasView = [[CanvasView alloc] initWithFrame:self.view.frame];
canvasView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:canvasView];
self.canvasView = canvasView;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.startPoint = [[touches anyObject] locationInView:self.canvasView];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CGPoint lastPoint = [[touches anyObject] previousLocationInView:self.canvasView];
if (CGPointEqualToPoint(lastPoint, self.startPoint)) {
id <Mark> newStroke = [[Stroke alloc] init];
[self addMark:newStroke shouldAddToPreviousMark:NO];
}
CGPoint currentPoint = [[touches anyObject] locationInView:self.canvasView];
Vertex *vertex = [[Vertex alloc] init];
vertex.location = currentPoint;
[self addMark:vertex shouldAddToPreviousMark:vertex];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CGPoint lastPoint = [[touches anyObject] previousLocationInView:self.canvasView];
CGPoint currentPoint = [[touches anyObject] locationInView:self.canvasView];
if (CGPointEqualToPoint(lastPoint, currentPoint))
{
Dot *singleDot = [[Dot alloc] init];
singleDot.location = currentPoint;
[self addMark:singleDot shouldAddToPreviousMark:NO];
}
self.startPoint = CGPointZero;
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.startPoint = CGPointZero;
}
- (void)addMark:(id<Mark>)mark shouldAddToPreviousMark:(BOOL)shouldAddToPreviousMark
{
if (shouldAddToPreviousMark) {
[self.parentMark.lastChild addMark:mark];
} else {
[self.parentMark addMark:mark];
}
self.canvasView.mark = self.parentMark;
}
@end
複製代碼
訪問者模式是擴展組合結構功能的一種強有力的方式.若是組合結構具備精心設計的基本操做,並且結構未來也不會變動,就可使用訪問者模式,用各類不一樣用途的訪問者,以一樣的方式訪問這個組合結構.訪問者模式用盡量少的修改,能夠把組合結構與其餘訪問者類中的相關算法分離.
普通實現
設置協議的抽象(咱們想讓全部的ImageComponent
都能支持UIImage
的重畫方法)
@protocol ImageComponent <NSObject>
@optional
- (void)drawAtPoint:(CGPoint)point;
- (void)drawAtPoint:(CGPoint)point blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
- (void)drawInRect:(CGRect)rect;
- (void)drawInRect:(CGRect)rect blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
- (void)drawAsPatternInRect:(CGRect)rect;
@end
複製代碼
設置核心的裝飾類,也是濾鏡基類
@interface ImageFilter : NSObject <ImageComponent>
{
@private
id <ImageComponent> component_;
}
/// 保持一個ImageComponent的引用,這個引用會被其餘具體的裝飾器裝飾.
@property (nonatomic, strong) id<ImageComponent> component;
- (void)apply;
- (id)initWithImageComponent:(id<ImageComponent>)component;
- (id)forwardingTargetForSelector:(SEL)aSelector;
複製代碼
- (id)initWithImageComponent:(id<ImageComponent>)component {
if (self = [super init]) {
[self setComponent:component];
}
return self;
}
- (void)apply {
// 應該由子類重載,應用真正的濾鏡
}
/// 截獲消息
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSString *selectorName = NSStringFromSelector(aSelector);
if ([selectorName hasPrefix:@"draw"]) {
[self apply];
}
return component_;
}
複製代碼
設置仿射變換濾鏡
- (instancetype)initWithImageComponent:(id<ImageComponent>)component
transform:(CGAffineTransform)transform {
if (self = [super initWithImageComponent:component]) {
[self setTransform:transform];
}
return self;
}
- (void)apply {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextConcatCTM(context, transform_);
}
複製代碼
設置陰影濾鏡
- (void)apply {
CGContextRef context = UIGraphicsGetCurrentContext();
CGSize offset = CGSizeMake(-25, 15);
CGContextSetShadow(context, offset, 20.0);
}
複製代碼
視圖展現配置
- (void)setImage:(UIImage *)image {
image_ = image;
}
- (void)drawRect:(CGRect)rect {
[image_ drawInRect:rect];
}
複製代碼
控制器配置
UIImage *img = [UIImage imageNamed:@"13.jpeg"];
CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(-M_PI / 4.0);
CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(-img.size.width / 2.0, img.size.height / 8.0);
CGAffineTransform finalTransform = CGAffineTransformConcat(rotateTransform, translateTransform);
id<ImageComponent>transformedImage = [[ImageTransformFilter alloc] initWithImageComponent:img transform:finalTransform];
id<ImageComponent>finalImage = [[ImageShadowFilter alloc] initWithImageComponent:transformedImage];
DecoratorView *decoratorView = [[DecoratorView alloc] initWithFrame:self.view.bounds];
[decoratorView setImage:(UIImage *)finalImage];
[self.view addSubview:decoratorView];
複製代碼
設置基礎濾鏡配置方法
@interface UIImage (BaseFilter)
- (CGContextRef)beginContext;
- (UIImage *)getImageFromCurrentImageContext;
- (void)endContext;
@end
複製代碼
@implementation UIImage (BaseFilter)
- (CGContextRef)beginContext {
CGSize size = [self size];
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
return context;
}
- (UIImage *)getImageFromCurrentImageContext {
[self drawAtPoint:CGPointZero];
UIImage *imageOut = UIGraphicsGetImageFromCurrentImageContext();
return imageOut;
}
- (void)endContext {
UIGraphicsEndImageContext();
}
@end
複製代碼
設置仿射變換濾鏡方法
@implementation UIImage (Transform)
- (UIImage *)imageWithTransform:(CGAffineTransform)transform {
CGContextRef context = [self beginContext];
CGContextConcatCTM(context, transform);
UIImage *imageOut = [self getImageFromCurrentImageContext];
[self endContext];
return imageOut;
}
@end
複製代碼
設置陰影濾鏡方法
@implementation UIImage (Shadow)
- (UIImage *)imageWithDropShadow {
CGContextRef context = [self beginContext];
CGSize offset = CGSizeMake(-25, 15);
CGContextSetShadow(context, offset, 20.0);
UIImage *imageOut = [self getImageFromCurrentImageContext];
[self endContext];
return imageOut;
}
@end
複製代碼
控制器配置
- (void)viewDidLoad {
[super viewDidLoad];
UIImage *img = [UIImage imageNamed:@"13.jpeg"];
CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(-M_PI / 4.0);
CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(-img.size.width / 2.0, img.size.height / 8.0);
CGAffineTransform finalTransform = CGAffineTransformConcat(rotateTransform, translateTransform);
UIImage *transformedImage = [img imageWithTransform:finalTransform];
UIImage *finalImage = [transformedImage imageWithDropShadow];
DecoratorView *decoratorView = [[DecoratorView alloc] initWithFrame:self.view.bounds];
[decoratorView setImage:(UIImage *)finalImage];
[self.view addSubview:decoratorView];
}
複製代碼
優勢:
裝飾模式在OC中會有不一樣的實現方式即爲範疇.真正子類方式的實際使用是一種較爲結構化的方式鏈接各類裝飾器.範疇的方式更爲簡單和輕量,適用於現有類只須要少許裝飾器的應用程序.
使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間發生耦合.此模式將這些對象連成一條鏈,並沿着這條鏈傳遞請求,直到有一個對象處理它爲止.
責任鏈類圖
請求處理典型結構圖
接口處理請求的協議ChainOfResponsibilityProtocol
@protocol ChainOfResponsibilityProtocol <NSObject>
@property (nonatomic, strong) id<ChainOfResponsibilityProtocol> successor;
- (void)handlerRequest:(id)request;
@end
複製代碼
定義鏈頭類HeadChain
/// 鏈頭
@interface HeadChain : NSObject<ChainOfResponsibilityProtocol>
@end
@interface HeadChain ()
@property (nonatomic, weak) id<ChainOfResponsibilityProtocol> nextSuccessor;
@end
@implementation HeadChain
@synthesize successor;
- (void)setSuccessor:(id<ChainOfResponsibilityProtocol>)successor {
self.nextSuccessor = successor;
}
- (id<ChainOfResponsibilityProtocol>)successor {
return self.nextSuccessor;
}
- (void)handlerRequest:(id)request {
[self.successor handlerRequest:request];
}
@end
複製代碼
定義驗證手機號碼節點類PhoneNumChain
/// 驗證手機號碼節點
@interface PhoneNumChain : NSObject<ChainOfResponsibilityProtocol>
@end
@interface PhoneNumChain ()
@property (nonatomic, weak) id<ChainOfResponsibilityProtocol> nextSuccessor;
@end
@implementation PhoneNumChain
@synthesize successor;
- (void)setSuccessor:(id<ChainOfResponsibilityProtocol>)successor {
self.nextSuccessor = successor;
}
- (id<ChainOfResponsibilityProtocol>)successor {
return self.nextSuccessor;
}
- (void)handlerRequest:(id)request {
NSString *str = request;
if ([str isKindOfClass:[NSString class]] && str.length > 0) {
// 匹配電話號碼(手機號以13, 15,18開頭,八個 \d 數字字符)
BOOL isMatch = [str isMatch:RX(@"^((13[0-9])|(15[^4,\\D])|(18[0,0-9]))\\d{8}$")];
if (isMatch) {
NSLog(@"%@ is a PhoneNum", str);
} else {
[self.successor handlerRequest:request];
}
} else {
[self.successor handlerRequest:request];
}
}
@end
複製代碼
驗證郵箱節點類EmailChain
/// 驗證郵箱節點
@interface EmailChain : NSObject<ChainOfResponsibilityProtocol>
@end
@interface EmailChain ()
@property (nonatomic, weak) id<ChainOfResponsibilityProtocol> nextSuccessor;
@end
@implementation EmailChain
@synthesize successor;
- (void)setSuccessor:(id<ChainOfResponsibilityProtocol>)successor {
self.nextSuccessor = successor;
}
- (id<ChainOfResponsibilityProtocol>)successor {
return self.nextSuccessor;
}
- (void)handlerRequest:(id)request {
NSString *str = request;
if ([str isKindOfClass:[NSString class]] && str.length > 0) {
// 匹配郵箱
BOOL isMatch = [str isMatch:RX(@"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}")];
if (isMatch) {
NSLog(@"%@ is a Email", str);
} else {
[self.successor handlerRequest:request];
}
} else {
[self.successor handlerRequest:request];
}
}
@end
複製代碼
驗證用戶名節點類UserNameChain
/// 驗證用戶名節點
@interface UserNameChain : NSObject<ChainOfResponsibilityProtocol>
@end
@interface UserNameChain ()
@property (nonatomic, weak) id<ChainOfResponsibilityProtocol> nextSuccessor;
@end
@implementation UserNameChain
@synthesize successor;
- (void)setSuccessor:(id<ChainOfResponsibilityProtocol>)successor {
self.nextSuccessor = successor;
}
- (id<ChainOfResponsibilityProtocol>)successor {
return self.nextSuccessor;
}
- (void)handlerRequest:(id)request {
NSString *str = request;
if ([str isKindOfClass:[NSString class]] && str.length > 0) {
// 匹配用戶名(用戶名長度爲6-20位之間,大小寫字母或者數字都可)
BOOL isMatch = [str isMatch:RX(@"^[A-Za-z0-9]{6,20}+$")];
if (isMatch) {
NSLog(@"%@ is a UserName", str);
} else {
[self.successor handlerRequest:request];
}
} else {
[self.successor handlerRequest:request];
}
}
@end
複製代碼
鏈尾類EndChain
/// 鏈尾
@interface EndChain : NSObject<ChainOfResponsibilityProtocol>
@end
@interface EndChain ()
@property (nonatomic, weak) id<ChainOfResponsibilityProtocol> nextSuccessor;
@end
@implementation EndChain
@synthesize successor;
- (void)setSuccessor:(id<ChainOfResponsibilityProtocol>)successor {
}
- (id<ChainOfResponsibilityProtocol>)successor {
return self;
}
- (void)handlerRequest:(id)request {
NSLog(@"沒法處理該請求");
}
@end
複製代碼
控制器實現
// 設置各請求鏈條
HeadChain *headChain = [[HeadChain alloc] init];
PhoneNumChain *phoneNumChain = [[PhoneNumChain alloc] init];
EmailChain *emailChain = [[EmailChain alloc] init];
UserNameChain *userNameChain = [[UserNameChain alloc] init];
EndChain *endChain = [[EndChain alloc] init];
// 設置下一連接點
headChain.successor = phoneNumChain;
phoneNumChain.successor = emailChain;
emailChain.successor = userNameChain;
userNameChain.successor = endChain;
// 處理事件
[headChain handlerRequest:@"18659007343"];
[headChain handlerRequest:@"18659007343@qq.com"];
[headChain handlerRequest:@"abc1865900"];
[headChain handlerRequest:@"====="];
複製代碼
結果展現
2019-04-17 00:26:21.661847+0800 ChainOfResponsibilityPattern[84339:1697824] 18659007343 is a PhoneNum
2019-04-17 00:26:21.662235+0800 ChainOfResponsibilityPattern[84339:1697824] 18659007343@qq.com is a Email
2019-04-17 00:26:21.662562+0800 ChainOfResponsibilityPattern[84339:1697824] abc1865900 is a UserName
2019-04-17 00:26:21.662889+0800 ChainOfResponsibilityPattern[84339:1697824] 沒法處理該請求
複製代碼
在Cocoa
中UIResponder
類就是責任鏈的一個實例.當操做應用時,進行點擊,點擊操做經過責任鏈一步步走下去直到找到相對應要響應的UIResponder
.
定義一個操做中算法的骨架,而將一些步驟延遲到子類中.模板方法使子類能夠衝定義算法的某些特定步驟而不改變該算法的結構.
定義基礎類AnySandwich
@interface AnySandwich : NSObject
- (void)make;
- (void)prepareBread;
- (void)putBreadOnPlate;
- (void)addMeat;
- (void)addCondiments;
- (void)serve;
- (void)extraStep;
@end
複製代碼
內部實現製做三明治的基本流程. extraStep
爲鉤子方法,子類可實現也能夠不用實現. prepareBread
addMeat
addCondiments
的具體實現交由子類.如若沒有實現,將libc++abi.dylib: terminating with uncaught exception of type NSException
運行失敗來限制子類.
@implementation AnySandwich
- (void)make {
[self prepareBread];
[self putBreadOnPlate];
[self addMeat];
[self addCondiments];
[self serve];
[self extraStep];
}
- (void)putBreadOnPlate {
// 作任何三明治都要先把麪包放在盤子上
}
- (void)serve {
// 任何三明治作好了都要上餐
}
#pragma mark -
#pragma Detail will be handled by subclasses
- (void)prepareBread {
// 要保證子類重載這個方法
[NSException raise:NSInternalInconsistencyException
format:@"You must override%@in a subclass", NSStringFromSelector(_cmd)];
}
- (void)addMeat {
// 要保證子類重載這個方法
[NSException raise:NSInternalInconsistencyException
format:@"You must override%@in a subclass", NSStringFromSelector(_cmd)];
}
- (void)addCondiments {
// 要保證子類重載這個方法
[NSException raise:NSInternalInconsistencyException
format:@"You must override%@in a subclass", NSStringFromSelector(_cmd)];
}
@end
複製代碼
ReubenSandwich
實現
@implementation ReubenSandwich
- (void)prepareBread {
[self cutRyeBread];
}
- (void)addMeat {
[self addCornBeef];
}
- (void)addCondiments {
[self addSauerkraut];
[self addThousandIslandDressing];
[self addSwissCheese];
}
- (void)extraStep {
[self grillIt];
}
#pragma mark -
#pragma mark ReubenSandwich Specific Methods
- (void)cutRyeBread {
// 魯賓三明治須要兩片黑麥麪包
}
- (void)addCornBeef {
// 魯賓三明治加大量醃牛肉
}
- (void)addSauerkraut {
// 加入德國酸菜
}
- (void)addThousandIslandDressing {
// 加入千島醬
}
- (void)addSwissCheese {
// 加入上等瑞士奶酪
}
- (void)grillIt {
// 最後要把它烤一下
}
@end
複製代碼
模板方法是代碼複用的基本技術,是抽出共同行爲放入框架類中的手段.這一方式有助於提升可擴展性與可複用性,而維持各類類之間的鬆耦合.
建立算法抽象基類,開放驗證接口並實現
@interface StringValidator : NSObject
- (BOOL)validateString:(NSString *)str error:(NSError **)error;
@end
@implementation StringValidator
- (BOOL)validateString:(NSString *)str error:(NSError * _Nullable __autoreleasing *)error {
if (error) {
*error = nil;
}
return NO;
}
@end
複製代碼
建立相關的算法繼承類 NumbericValidator
數值判斷類
@interface NumbericValidator : StringValidator
- (BOOL)validateString:(NSString *)str error:(NSError * _Nullable __autoreleasing *)error;
@end
@implementation NumbericValidator
- (BOOL)validateString:(NSString *)str error:(NSError *__autoreleasing _Nullable *)error {
NSError *regError = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[0-9]*$" options:NSRegularExpressionAnchorsMatchLines error:®Error];
NSUInteger numberOfMatchs = [regex numberOfMatchesInString:str options:NSMatchingAnchored range:NSMakeRange(0, str.length)];
if (numberOfMatchs == 0) {
if (error != nil) {
NSString *description = NSLocalizedString(@"Failed", @"");
NSString *reason = NSLocalizedString(@"can contain only numberical", @"");
NSArray *objArray = [NSArray arrayWithObjects:description, reason, nil];
NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey, NSLocalizedFailureReasonErrorKey, nil];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray];
*error = [NSError errorWithDomain:@"StringErrorDomain" code:1001 userInfo:userInfo];
}
return NO;
}
return YES;
}
@end
複製代碼
AlphaValidator
字母判斷類
@interface AlphaValidator : StringValidator
- (BOOL)validateString:(NSString *)str error:(NSError * _Nullable __autoreleasing *)error;
@end
@implementation AlphaValidator
- (BOOL)validateString:(NSString *)str error:(NSError *__autoreleasing _Nullable *)error {
NSError *regError = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[a-zA-Z]*$" options:NSRegularExpressionAnchorsMatchLines error:®Error];
NSUInteger numberOfMatchs = [regex numberOfMatchesInString:str options:NSMatchingAnchored range:NSMakeRange(0, str.length)];
if (numberOfMatchs == 0) {
if (error != nil) {
NSString *description = NSLocalizedString(@"Failed", @"");
NSString *reason = NSLocalizedString(@"can contain only alpha", @"");
NSArray *objArray = [NSArray arrayWithObjects:description, reason, nil];
NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey, NSLocalizedFailureReasonErrorKey, nil];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray];
*error = [NSError errorWithDomain:@"StringErrorDomain" code:1002 userInfo:userInfo];
}
return NO;
}
return YES;
}
@end
複製代碼
算法策略類StringValidatorManager
@interface StringValidatorManager : NSObject
+ (BOOL)numbervalidString:(NSString *)str error:(NSError **)error;
+ (BOOL)alphaString:(NSString *)str error:(NSError **)error;
@end
@implementation StringValidatorManager
+ (BOOL)numbervalidString:(NSString *)str error:(NSError * _Nullable __autoreleasing *)error {
NumbericValidator *number = [[NumbericValidator alloc] init];
return [number validateString:str error:error];
}
+ (BOOL)alphaString:(NSString *)str error:(NSError * _Nullable __autoreleasing *)error {
AlphaValidator *alpha = [[AlphaValidator alloc] init];
return [alpha validateString:str error:error];
}
@end
複製代碼
控制器實現
NSString *test1 = @"112";
NSError *error0;
[StringValidatorManager numbervalidString:test1 error:&error0];
if (error0) {
NSLog(@"0: %@", error0.localizedFailureReason);
}
NSError *error1;
[StringValidatorManager alphaString:test1 error:&error1];
if (error1) {
NSLog(@"1: %@", error1.localizedFailureReason);
}
NSString *test2 = @"adsf";
NSError *error2;
[StringValidatorManager numbervalidString:test2 error:&error2];
if (error2) {
NSLog(@"2: %@", error2.localizedFailureReason);
}
NSError *error3;
[StringValidatorManager alphaString:test2 error:&error3];
if (error3) {
NSLog(@"3: %@", error3.localizedFailureReason);
}
複製代碼
結果展現
2018-12-25 22:22:45.027745+0800 StrategyPattern[21641:845119] 1: can contain only alpha
2018-12-25 22:22:45.028076+0800 StrategyPattern[21641:845119] 2: can contain only numberical
複製代碼
優勢
策略模式與裝飾模式有些類似.裝飾器從外部擴展對象的行爲,而各類策略則被封裝在對象之中.因此說裝飾器改變對象的"外表"而策略改變對象的"內容".
將請求封裝成一個對象,從而可用不一樣的請求對客戶進行參數化,對請求排隊或記錄請求日誌,以及支持可撤銷的操做.
Client
建立ConcreteCommand
對象並設定器receiver
; Invoker
要求通用命令(其實是ConcreteCommand
)實施請求; Command
要爲Invoker
所知的通用接口; ConcreteCommand
起Receiver
和對它的操做action
之間的中間人做用; Receiver
能夠隨着由Command
(ConcreteCommand
)對象實施的相應請求,而執行實際操做的任何對象.
接口協議CommandInterface
/// 命令對象的公共接口(按鈕執行的動做)
@protocol CommandInterface <NSObject>
/// 執行命令
- (void)execute;
/// 撤銷
- (void)undo;
@end
複製代碼
定義燈類Light
/// 電燈類
@interface Light : NSObject
- (void)lightOn;
- (void)lightOff;
@end
@implementation Light
- (void)lightOn {
NSLog(@"開燈");
}
- (void)lightOff {
NSLog(@"關燈");
}
@end
複製代碼
定義CD播放器類CDPlayer
/// CD播放器類
@interface CDPlayer : NSObject
- (void)CDPlayerOn;
- (void)CDPlayerOff;
@end
@implementation CDPlayer
- (void)CDPlayerOn {
NSLog(@"打開CD播放器");
}
- (void)CDPlayerOff {
NSLog(@"關閉CD播放器");
}
@end
複製代碼
打開燈命令類LightOnCommand
/// 打開燈命令類
@interface LightOnCommand : NSObject<CommandInterface>
- (instancetype)initWithLight:(Light *)light;
@end
@implementation LightOnCommand
- (instancetype)initWithLight:(Light *)light {
if (self = [super init]) {
self.light = light;
}
return self;
}
- (void)execute {
[self.light lightOn];
}
- (void)undo {
[self.light lightOff];
}
@end
複製代碼
關燈命令類LightOffCommand
/// 關燈命令類
@interface LightOffCommand : NSObject<CommandInterface>
- (instancetype)initWithLight:(Light *)light;
@end
@implementation LightOffCommand
- (instancetype)initWithLight:(Light *)light {
if (self = [super init]) {
self.light = light;
}
return self;
}
- (void)execute {
[self.light lightOff];
}
- (void)undo {
[self.light lightOn];
}
@end
複製代碼
CD播放器播放類CDPlayerOnCommand
/// CD播放器播放類
@interface CDPlayerOnCommand : NSObject<CommandInterface>
- (instancetype)initWithCDPlayer:(CDPlayer *)cdPlayer;
@end
@implementation CDPlayerOnCommand
- (instancetype)initWithCDPlayer:(CDPlayer *)cdPlayer {
if (self = [super init]) {
self.cdPlayer = cdPlayer;
}
return self;
}
- (void)execute {
[self.cdPlayer CDPlayerOn];
}
- (void)undo {
[self.cdPlayer CDPlayerOff];
}
@end
複製代碼
定義CD播放器關閉類CDPlayerOffCommand
/// CD播放器關閉類
@interface CDPlayerOffCommand : NSObject<CommandInterface>
- (instancetype)initWithCDPlayer:(CDPlayer *)cdPlayer;
@end
@implementation CDPlayerOffCommand
- (instancetype)initWithCDPlayer:(CDPlayer *)cdPlayer {
if (self = [super init]) {
self.cdPlayer = cdPlayer;
}
return self;
}
- (void)execute {
[self.cdPlayer CDPlayerOff];
}
- (void)undo {
[self.cdPlayer CDPlayerOn];
}
@end
複製代碼
定義遙控器類RemoteControl
/// 命令調用者(遙控器)
@interface RemoteControl : NSObject
//@property (nonatomic, strong) id<CommandInterface> slot;
- (void)onClickWithIdx:(NSInteger)idx;
- (void)offClickWithIdx:(NSInteger)idx;
- (void)setCommandWithIdx:(NSInteger)idx
onCommand:(id<CommandInterface>)onCommand
offCommand:(id<CommandInterface>)offCommand;
/// 撤銷剛纔的操做
- (void)undoAction;
/// 撤銷全部操做
- (void)undoAllAction;
@end
@interface RemoteControl ()
@property (nonatomic, strong) NSArray<id<CommandInterface>> *onCommands;
@property (nonatomic, strong) NSArray<id<CommandInterface>> *offCommands;
@property (nonatomic, strong) id<CommandInterface> undoCommand; ///< 上一次的命令
@property (nonatomic, strong) NSMutableArray<id<CommandInterface>> *completeCommandsArr;
@end
@implementation RemoteControl
- (instancetype)init {
if (self = [super init]) {
// 默認有4類命令類型
NSMutableArray *mOnArr = [[NSMutableArray alloc] init];
NSMutableArray *mOffArr = [[NSMutableArray alloc] init];
for (int i = 0; i < 4; ++i) {
[mOnArr addObject:[[DefaultCommand alloc] init]];
[mOffArr addObject:[[DefaultCommand alloc] init]];
}
self.onCommands = mOnArr.copy;
self.offCommands = mOnArr.copy;
self.completeCommandsArr = [[NSMutableArray alloc] init];
}
return self;
}
- (void)onClickWithIdx:(NSInteger)idx {
if (idx >= self.onCommands.count || idx < 0) {
return;
}
[self.onCommands[idx] execute];
self.undoCommand = self.onCommands[idx];
[self.completeCommandsArr addObject:self.onCommands[idx]];
}
- (void)offClickWithIdx:(NSInteger)idx {
if (idx >= self.offCommands.count || idx < 0) {
return;
}
[self.offCommands[idx] execute];
self.undoCommand = self.offCommands[idx];
[self.completeCommandsArr addObject:self.offCommands[idx]];
}
- (void)undoAction {
[self.undoCommand undo];
[self.completeCommandsArr removeObject:self.undoCommand];
}
- (void)undoAllAction {
[self.completeCommandsArr enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id<CommandInterface> _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj undo];
}];
[self.completeCommandsArr removeAllObjects];
}
- (void)setCommandWithIdx:(NSInteger)idx
onCommand:(id<CommandInterface>)onCommand
offCommand:(id<CommandInterface>)offCommand {
if (idx < 0 || idx >= self.onCommands.count) {
return;
}
if (idx < 0 || idx >= self.offCommands.count) {
return;
}
NSMutableArray *mOnCommands = [self.onCommands mutableCopy];
[mOnCommands replaceObjectAtIndex:idx withObject:onCommand];
self.onCommands = mOnCommands;
NSMutableArray *mOffCommands = [self.offCommands mutableCopy];
[mOffCommands replaceObjectAtIndex:idx withObject:offCommand];
self.offCommands = mOffCommands;
}
@end
複製代碼
定義初始的命令類
/// 默認命令類(設備默認狀態)
@interface DefaultCommand : NSObject<CommandInterface>
@end
@implementation DefaultCommand
- (void)execute {
NSLog(@"默認命令狀態下");
}
- (void)undo {
NSLog(@"默認撤銷");
}
@end
複製代碼
定義面板用於提供給外接接口
/// 命令裝配者(將命令安裝到遙控器上)
@interface RemoteLoader : NSObject
@property (nonatomic, strong, readonly) RemoteControl *rc;
- (instancetype)initWithRemoteControl:(RemoteControl *)rc;
@end
@implementation RemoteLoader
- (instancetype)initWithRemoteControl:(RemoteControl *)rc {
if (self = [super init]) {
self.rc = rc;
[self configCommands];
}
return self;
}
- (void)configCommands {
Light *light = [[Light alloc] init];
LightOnCommand *lightOnCommand = [[LightOnCommand alloc] initWithLight:light];
LightOffCommand *lightOffCommand = [[LightOffCommand alloc] initWithLight:light];
[self.rc setCommandWithIdx:0 onCommand:lightOnCommand offCommand:lightOffCommand];
CDPlayer *cd = [[CDPlayer alloc] init];
CDPlayerOnCommand *cdPlayerOnCommand = [[CDPlayerOnCommand alloc] initWithCDPlayer:cd];
CDPlayerOffCommand *cdPlayerOffCommand = [[CDPlayerOffCommand alloc] initWithCDPlayer:cd];
[self.rc setCommandWithIdx:1 onCommand:cdPlayerOnCommand offCommand:cdPlayerOffCommand];
}
@end
複製代碼
控制器實現
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
RemoteControl *rc = [[RemoteControl alloc] init];
RemoteLoader *loader = [[RemoteLoader alloc] initWithRemoteControl:rc];
[loader.rc onClickWithIdx:0];
[loader.rc onClickWithIdx:1];
[loader.rc offClickWithIdx:0];
[loader.rc offClickWithIdx:1];
NSLog(@"------------執行撤銷動做------------");
[loader.rc undoAction];
[loader.rc undoAllAction];
}
@end
複製代碼
結果展現
2019-04-14 21:54:05.762810+0800 CommandPattern[68418:1486836] 開燈
2019-04-14 21:54:05.762981+0800 CommandPattern[68418:1486836] 打開CD播放器
2019-04-14 21:54:05.763088+0800 CommandPattern[68418:1486836] 關燈
2019-04-14 21:54:05.763198+0800 CommandPattern[68418:1486836] 關閉CD播放器
2019-04-14 21:54:05.763315+0800 CommandPattern[68418:1486836] ------------執行撤銷動做------------
2019-04-14 21:54:05.763417+0800 CommandPattern[68418:1486836] 打開CD播放器
2019-04-14 21:54:05.763661+0800 CommandPattern[68418:1486836] 開燈
2019-04-14 21:54:05.763770+0800 CommandPattern[68418:1486836] 關閉CD播放器
2019-04-14 21:54:05.763886+0800 CommandPattern[68418:1486836] 關燈
複製代碼
Cocoa Touch框架中使用命令模式的典型例子.
運用共享技術有效地支持大量細粒度的對象.
花朵視圖FlowerView
// 繪製一朵花朵圖案
@interface FlowerView : UIImageView
@end
@implementation FlowerView
- (void)drawRect:(CGRect)rect {
[self.image drawInRect:rect];
}
@end
複製代碼
花朵生成工廠類FlowerFactory
.
`FlowerFactory`用`flowerPool`聚合了一個花朵池的引用.
`flowerPool`是一個保存`FlowerView`的全部實例的數據結構.
`FlowerFactory`經過`flowerViewWithType:`方法返回`FlowerView`
實例.
複製代碼
static NSInteger kTotalNumberOfFlowTypes = 7;
typedef NS_ENUM(NSInteger, FlowerType) {
kAnemone = 0,
kCosmos,
kGerberas,
kHollyhock,
kJasmine,
kZinnia
};
NS_ASSUME_NONNULL_BEGIN
@interface FlowerFactory : NSObject
- (UIView *)flowerViewWithType:(FlowerType)type;
@end
NS_ASSUME_NONNULL_END
@interface FlowerFactory ()
@property (nonatomic, strong) NSMutableDictionary *flowerPool;
@end
@implementation FlowerFactory
- (UIView *)flowerViewWithType:(FlowerType)type {
UIView *flowerView = [_flowerPool objectForKey:@(type)];
if (flowerView) {
return flowerView;
}
NSString *imgName;
switch (type) {
case kAnemone:
imgName = @"anemone";
break;
case kCosmos:
imgName = @"cosmos";
break;
case kGerberas:
imgName = @"gerberas";
break;
case kHollyhock:
imgName = @"hollyhock";
break;
case kJasmine:
imgName = @"jasmine";
break;
case kZinnia:
imgName = @"zinnia";
break;
default:
break;
}
UIImage *img = [UIImage imageNamed:imgName];
if (!img) {
return nil;
}
FlowerView *tmpView = [[FlowerView alloc] init];
tmpView.image = img;
[self.flowerPool setObject:tmpView forKey:@(type)];
return tmpView;
}
- (NSMutableDictionary *)flowerPool {
if (!_flowerPool) {
NSMutableDictionary *mDic = [[NSMutableDictionary alloc] initWithCapacity:kTotalNumberOfFlowTypes];
_flowerPool = mDic;
}
return _flowerPool;
}
@end
複製代碼
花朵外部數據結構ExtrinsicFlowerState
#ifndef ExtrinsicFlowerState_h
#define ExtrinsicFlowerState_h
struct ExtrinsicFlowerState {
__unsafe_unretained UIView *flowerView;
CGRect area;
};
複製代碼
花朵展現視圖FlowerContainerView
@interface FlowerContainerView : UIView
@property (nonatomic, strong) NSArray *flowerList;
@end
@implementation FlowerContainerView
- (void)drawRect:(CGRect)rect {
for (NSValue *stateValue in self.flowerList) {
struct ExtrinsicFlowerState state;
[stateValue getValue:&state];
UIView *flowerView = state.flowerView;
CGRect frame = state.area;
[flowerView drawRect:frame];
}
}
@end
複製代碼
控制器實現
static NSInteger kFlowerListCount = 200;
@interface ViewController ()
@property (nonatomic, strong) FlowerFactory *flowerFactory;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
FlowerContainerView *containerView = [[FlowerContainerView alloc] initWithFrame:self.view.bounds];
containerView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:containerView];
_flowerFactory = [[FlowerFactory alloc] init];
NSMutableArray *flowerList = [[NSMutableArray alloc] initWithCapacity:kFlowerListCount];
for (int i = 0; i < kFlowerListCount; ++i) {
// 從工廠取得一個共享的花朵享元對象實例
FlowerType type = arc4random() % kTotalNumberOfFlowTypes;
UIView *flowerView = [_flowerFactory flowerViewWithType:type];
// 設置花朵顯示的區域
CGRect viewBounds = self.view.bounds;
CGFloat x = arc4random() % (int)CGRectGetWidth(viewBounds);
CGFloat y = arc4random() % (int)CGRectGetHeight(viewBounds);
CGFloat minSize = 10;
CGFloat maxSize = 60;
CGFloat size = (arc4random() % (int)(maxSize - minSize)) + minSize;
struct ExtrinsicFlowerState state;
state.flowerView = flowerView;
state.area = CGRectMake(x, y, size, size);
[flowerList addObject:[NSValue value:&state withObjCType:@encode(struct ExtrinsicFlowerState)]];
}
[containerView setFlowerList:flowerList.copy];
}
@end
複製代碼
經過享元對象可以節省的空間,取決於幾個因素:
然而,對共享對象外在狀態的傳遞、查找和計算,可能產生運行時的開銷,尤爲在外在狀態本來是做爲內在狀態來保存的時候.當享元的共享愈來愈多時,空間的節省會抵消這些開銷.共享的享元越多,節省的存儲就越多.節省直接跟共享的狀態相關.若是對象有大量內在和外在狀態,外在狀態又可以計算出來而不用存儲的時候,就能節省最大的空間.這樣咱們以兩種方式節省了存儲空間:共享減小了內在狀態的開銷,經過犧牲計算時間又節省了外在狀態的存儲空間.
爲其餘對象提供一種代理以控制對這個對象的訪問.
定義代理類SendGift
@protocol SendGift <NSObject>
- (void)sendGift;
@end
複製代碼
哆啦A夢
@interface Doraemon : NSObject<SendGift>
@end
複製代碼
僱主類
@interface Person : NSObject
@property (nonatomic, weak) id<SendGift> delegate;
@end
複製代碼
調用場景
Person *daxiong = [[Person alloc] init];
Doraemon *doraemon = [[Doraemon alloc] init];
// 哆啦A夢成爲代理,替大熊送禮物
daxiong.delegate = doraemon;
[daxiong.delegate sendGift];
複製代碼
主要思想是使用一個基本上跟實體對象行爲相同的代理.客戶端能夠"透明地"使用代理,即沒必要知悉所面對的只是一個代理而不是實體對象.當客戶端請求某些建立的開銷較大的功能時,代理將把請求轉發給實體對象,準備好請求的功能並返回給客戶端.客戶端不知道幕後發生了什麼.
代理模式在OC中會經常會出現而且平常開發也會用到,最爲典型的例子就是Applegate
類了,它被委託處理應用生命週期期間所面對的各類情況.
在不破壞封裝的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態.這樣之後就可將對象恢復到原先保存的狀態.
定義原發器類Originator
@interface Originator : NSObject
@property (nonatomic, copy) NSString *address;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (Memento *)createMemento;
- (void)setMemento:(Memento *)memento;
@end
@implementation Originator
- (Memento *)createMemento {
Memento *memo = [[Memento alloc] init];
memo.age = self.age;
memo.name = self.name;
return memo;
}
- (void)setMemento:(Memento *)memento {
self.age = memento.age;
self.name = memento.name;
}
- (NSString *)description {
return [NSString stringWithFormat:@"name = %@, age = %ld", self.name, self.age];
}
@end
複製代碼
定義備忘錄類Memento
@interface Memento : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
複製代碼
定義看管者類Caretaker
@interface Caretaker : NSObject
- (void)archiveMemento:(Memento *)memento;
- (Memento *)getMemento;
@end
@interface Caretaker ()
@property (nonatomic, strong) Memento *memento;
@end
@implementation Caretaker
- (void)archiveMemento:(Memento *)memento {
self.memento = memento;
}
- (Memento *)getMemento {
return self.memento;
}
@end
複製代碼
控制器實現
@implementation ViewController
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Originator *oriAtor = [[Originator alloc] init];
oriAtor.name = @"sansa";
oriAtor.age = 18;
NSLog(@"1 --- %@", oriAtor);
// 保存十八歲的樣子
Caretaker *taker = [[Caretaker alloc] init];
[taker archiveMemento:[oriAtor createMemento]];
// 過了好多年
oriAtor.age = 78;
NSLog(@"2 --- %@", oriAtor);
// 吃了藥劑,重返十八歲
[oriAtor setMemento:[taker getMemento]];
NSLog(@"3 --- %@", oriAtor);
}
@end
/* MementoPattern[38016:1796366] 1 --- name = sansa, age = 18
** MementoPattern[38016:1796366] 2 --- name = sansa, age = 78
** MementoPattern[38016:1796366] 3 --- name = sansa, age = 18
*/
複製代碼
**Cocoa Touch
框架在歸檔、屬性列表序列化和核心數據中採用了備忘錄模式.
設計模式是一套被反覆使用的、多數人知曉的、通過分類編目的、代碼設計經驗的總結。使用設計模式是爲了重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石同樣。項目中合理地運用設計模式能夠完美地解決不少問題,每種模式在現實中都有相應的原理來與之對應,每種模式都描述了一個在咱們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是設計模式能被普遍應用的緣由。
無論是什麼開發都會用到設計模式,學習好設計模式對咱們對編碼架構設計會有一個很好的提高.固然這也是會有至關大對學習成本,學習了理論後更須要去實踐,Talk is cheap. Show me the code.
理論和實踐結合這樣纔會有更好的理解.
最後引用喬幫主的話Stay hungry Stay foolish
共勉.
gitHub: PatternDemos