一蚂维、六大設(shè)計(jì)原則
單一職責(zé)原則:一個(gè)類只負(fù)責(zé)一件事
依賴倒置原則:抽象不該依賴于具體實(shí)現(xiàn)戳粒,具體實(shí)現(xiàn)可以依賴抽象
開閉原則:對(duì)修改關(guān)閉,對(duì)擴(kuò)展開放
里氏替換原則:父類可以被子類無縫替換鸟雏,且原有功能不受影響(例如:KVO)
接口隔離原則:使用多個(gè)專門的協(xié)議享郊、而不是一個(gè)龐大臃腫的協(xié)議(例如:UITableViewDelegate,UITableViewDataSource)
迪米特法則:一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象盡可能少的了解(高內(nèi)聚览祖、高耦合)
關(guān)于設(shè)計(jì)原則可以看這篇文章面向?qū)ο笤O(shè)計(jì)的六大設(shè)計(jì)原則(附 Demo 及 UML類圖)
二孝鹊、責(zé)任鏈模式
主要思想:對(duì)象引用了同一類型的另一個(gè)對(duì)象,形成一條鏈展蒂。鏈中的每個(gè)對(duì)象實(shí)現(xiàn)了相同的方法又活,處理對(duì)鏈中第一個(gè)對(duì)象發(fā)起的同一請(qǐng)求兽叮,如果一個(gè)對(duì)象不知道如何處理匕得,就把請(qǐng)求傳給下一個(gè)響應(yīng)器衔肢。
代碼示例:
@class BusinessObject;
typedef void(^CompletionBlock)(BOOL handled);
typedef void(^ResultBlock)(BusinessObject *handler, BOOL handled);
?
@interface BusinessObject : NSObject
?
// 下一個(gè)響應(yīng)者(響應(yīng)鏈構(gòu)成的關(guān)鍵)
@property (nonatomic, strong) BusinessObject *nextBusiness;
// 響應(yīng)者的處理方法
- (void)handle:(ResultBlock)result;
?
// 各個(gè)業(yè)務(wù)在該方法當(dāng)中做實(shí)際業(yè)務(wù)處理
- (void)handleBusiness:(CompletionBlock)completion;
@end
@implementation BusinessObject
?
// 責(zé)任鏈入口方法
- (void)handle:(ResultBlock)result
{
CompletionBlock completion = ^(BOOL handled){
// 當(dāng)前業(yè)務(wù)處理掉了炭菌,上拋結(jié)果
if (handled) {
result(self, handled);
}
else{
// 沿著責(zé)任鏈留凭,指派給下一個(gè)業(yè)務(wù)處理
if (self.nextBusiness) {
[self.nextBusiness handle:result];
}
else{
// 沒有業(yè)務(wù)處理, 上拋
result(nil, NO);
}
}
};
// 當(dāng)前業(yè)務(wù)進(jìn)行處理
[self handleBusiness:completion];
}
?
- (void)handleBusiness:(CompletionBlock)completion
{
/*
業(yè)務(wù)邏輯處理
如網(wǎng)絡(luò)請(qǐng)求辱匿、本地照片查詢等
*/
}
?
@end
三蜓陌、橋接模式
橋接模式的目的是把抽象層次結(jié)構(gòu)從其實(shí)現(xiàn)中分離出來徐许,使其能夠獨(dú)立變更。
Class A 和ClassB都是抽象類曲初。ClassA中一個(gè)成員變量是ClassB的對(duì)象体谒。ClassB中作為抽象類,只提供了默認(rèn)的接口臼婆,并沒有實(shí)現(xiàn)抒痒。B1、B2颁褂、B3是ClassB的三個(gè)子類故响,重寫父類的接口方法,提供不同的實(shí)現(xiàn)颁独,此時(shí)對(duì)于ClassB使用方來說彩届,是感知到不使用了哪個(gè)實(shí)現(xiàn)。ClassA中有一個(gè)handle處理方法誓酒,默認(rèn)調(diào)用成員變量ClassB對(duì)象中的接口方法惨缆。A1、A2丰捷、A3三個(gè)是ClassA的子類坯墨,對(duì)于子類來說可以覆寫父類的handle方法,做一些自定義的操作病往。
代碼示例: ClassA
#import <Foundation/Foundation.h>
#import "BaseObjectB.h"
@interface BaseObjectA : NSObject
?
// 橋接模式的核心實(shí)現(xiàn)
@property (nonatomic, strong) BaseObjectB *objB;
?
// 獲取數(shù)據(jù)
- (void)handle;
@end
#import "BaseObjectA.h"
?
@implementation BaseObjectA
?
/*
組合方式:
A1 --> B1捣染、B2、B3 3種
A2 --> B1停巷、B2耍攘、B3 3種
A3 --> B1、B2畔勤、B3 3種
*/
- (void)handle
{
// override to subclass
// 處理objB中的方法蕾各。
[self.objB fetchData];
}
?
@end
ClassA的子類A1、A2庆揪、A3重寫父類中handle方法式曲。
#import "ObjectA1.h"
?
@implementation ObjectA1
?
- (void)handle
{
// before 業(yè)務(wù)邏輯操作
[super handle];
// after 業(yè)務(wù)邏輯操作
}
@end
ClassB 實(shí)現(xiàn)
#import <Foundation/Foundation.h>
?
@interface BaseObjectB : NSObject
?
- (void)fetchData;
?
@end
#import "BaseObjectB.h"
?
@implementation BaseObjectB
// 默認(rèn)邏輯實(shí)現(xiàn)
- (void)fetchData
{
// override to subclass
}
@end
ClassB的子類進(jìn)行具體的邏輯實(shí)現(xiàn)。
#import "ObjectB1.h"
?
@implementation ObjectB1
?
- (void)fetchData{
// 具體的邏輯處理
}
@end
使用方代碼實(shí)現(xiàn)
@interface BridgeDemo()
@property (nonatomic, strong) BaseObjectA *objA;
@end
?
@implementation BridgeDemo
?
/*
根據(jù)實(shí)際業(yè)務(wù)判斷使用那套具體數(shù)據(jù)
A1 --> B1缸榛、B2吝羞、B3 3種
A2 --> B1、B2内颗、B3 3種
A3 --> B1钧排、B2、B3 3種
*/
- (void)fetch
{
// 創(chuàng)建一個(gè)具體的ClassA
_objA = [[ObjectA1 alloc] init];
// 創(chuàng)建一個(gè)具體的ClassB
BaseObjectB *b1 = [[ObjectB1 alloc] init];
// 將一個(gè)具體的ClassB1 指定給抽象的ClassB
_objA.objB = b1;
// 獲取數(shù)據(jù)
[_objA handle];
}
@end
使用方中定義了ClassA對(duì)象均澳,可以使用A1恨溜、A2符衔、A3來創(chuàng)建不同的對(duì)象,獲取不同的實(shí)現(xiàn)組合糟袁。BaseObjectB也可以有不同的實(shí)現(xiàn)組合柏腻。通過橋接模式不同的組合可以實(shí)現(xiàn)對(duì)象之間的解耦。
橋接模式的優(yōu)點(diǎn):
分離抽象接口及其實(shí)現(xiàn)部分系吭。
橋接模式有時(shí)類似于多繼承方案五嫂,但是多繼承方案違背了類的單一職責(zé)原則(即一個(gè)類只有一個(gè)變化的原因),復(fù)用性比較差肯尺,而且多繼承結(jié)構(gòu)中類的個(gè)數(shù)非常龐大沃缘,橋接模式是比多繼承方案更好的解決方法。
橋接模式提高了系統(tǒng)的可擴(kuò)充性则吟,在兩個(gè)變化維度中任意擴(kuò)展一個(gè)維度槐臀,都不需要修改原有系統(tǒng)。
實(shí)現(xiàn)細(xì)節(jié)對(duì)客戶透明氓仲,可以對(duì)用戶隱藏實(shí)現(xiàn)細(xì)節(jié)水慨。
四、適配器
適配器模式(Adapter Pattern):將一個(gè)接口轉(zhuǎn)換成客戶希望的另一個(gè)接口敬扛,適配器模式使接口不兼容的那些類可以一起工作晰洒,其別名為包裝器(Wrapper)。適配器模式既可以作為類結(jié)構(gòu)型模式啥箭,也可以作為對(duì)象結(jié)構(gòu)型模式谍珊。
本節(jié)主要學(xué)習(xí)對(duì)象適配器模式,簡(jiǎn)單的類結(jié)構(gòu)如下急侥。
適配對(duì)象中一個(gè)成員變量指向被適配對(duì)象砌滞。
示例代碼:類Target是被適配對(duì)象,CoolTarget為適配對(duì)象坏怪。
Target類
#import <Foundation/Foundation.h>
?
@interface Target : NSObject
?
- (void)operation;
?
@end
#import "Target.h"
?
@implementation Target
?
- (void)operation
{
// 原有的具體業(yè)務(wù)邏輯
}
?
@end
CoolTarget類:
#import "Target.h"
?
// 適配對(duì)象
@interface CoolTarget : NSObject
?
// 被適配對(duì)象
@property (nonatomic, strong) Target *target;
?
// 對(duì)原有方法包裝
- (void)request;
?
@end
#import "CoolTarget.h"
?
@implementation CoolTarget
?
- (void)request
{
// 額外處理
[self.target operation];
// 額外處理
}
?
@end
適配器優(yōu)點(diǎn):
將目標(biāo)類和適配者類解耦贝润,通過引入一個(gè)適配器類來重用現(xiàn)有的適配者類,而無須修改原有代碼铝宵。
增加了類的透明性和復(fù)用性打掘,將具體的實(shí)現(xiàn)封裝在適配者類中,對(duì)于客戶端類來說是透明的捉超,而且提高了適配者的復(fù)用性胧卤。
靈活性和擴(kuò)展性都非常好唯绍,通過使用配置文件拼岳,可以很方便地更換適配器,也可以在不修改原有代碼的基礎(chǔ)上增加新的適配器類况芒,完全符合“開閉原則”惜纸。
三叶撒、單例
單例模式(SingletonPattern):?jiǎn)卫J酱_保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例耐版,這個(gè)類稱為單例類祠够,它提供全局訪問的方法。
單例模式的要點(diǎn)有三個(gè):一是某個(gè)類只能有一個(gè)實(shí)例粪牲;二是它必須自行創(chuàng)建這個(gè)實(shí)例古瓤;三是它必須自行向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。單例模式是一種對(duì)象創(chuàng)建型模式腺阳。單例模式又名單件模式或單態(tài)模式落君。
示例代碼:
@implementation Mooc
?
+ (id)sharedInstance
{
// 靜態(tài)局部變量
static Mooc *instance = nil;
// 通過dispatch_once方式 確保instance在多線程環(huán)境下只被創(chuàng)建一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 創(chuàng)建實(shí)例
instance = [[super allocWithZone:NULL] init];
});
return instance;
}
?
// 重寫方法【必不可少】
+ (id)allocWithZone:(struct _NSZone *)zone{
return [self sharedInstance];
}
?
// 重寫方法【必不可少】
- (id)copyWithZone:(nullable NSZone *)zone{
return self;
}
?
@end
注意點(diǎn):為了防止使用者創(chuàng)建對(duì)象,需要從重寫兩個(gè)方法allocWithZone
和copyWithZone:
亭引。另外instance = [[super allocWithZone:NULL] init];
需要使用super方法調(diào)用防止在第一創(chuàng)建時(shí)循環(huán)調(diào)用绎速。
四、命令模式
命令模式(CommandPattern):將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象焙蚓,從而使我們可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化纹冤;對(duì)請(qǐng)求排隊(duì)或者記錄請(qǐng)求日志,以及支持可撤銷的操作购公。命令模式是一種對(duì)象行為型模式萌京,其別名為動(dòng)作(Action)模式或事務(wù)(Transaction)模式。
代碼實(shí)例:一個(gè)命令對(duì)象和一個(gè)命令管理者宏浩。
Command
@class Command;
typedef void(^CommandCompletionCallBack)(Command* cmd);
?
@interface Command : NSObject
@property (nonatomic, copy) CommandCompletionCallBack completion; // 執(zhí)行回調(diào)
?
- (void)execute; // 執(zhí)行
- (void)cancel; // 取消
?
- (void)done; // 完成
?
@end
#import "Command.h"
#import "CommandManager.h"
@implementation Command
?
- (void)execute{
//override to subclass;
[self done];
}
?
- (void)cancel{
self.completion = nil;
}
?
- (void)done
{
dispatch_async(dispatch_get_main_queue(), ^{
if (_completion) {
_completion(self);
}
//釋放
self.completion = nil;
// 在數(shù)組中移除
[[CommandManager sharedInstance].arrayCommands removeObject:self];
});
}
?
@end
CommandManager
可以用CommandManager保證任務(wù)的順序執(zhí)行枫夺,使用一個(gè)正在執(zhí)行任務(wù)數(shù)組和一個(gè)等待執(zhí)行任務(wù)數(shù)組,可以參考SDWebImage圖片下載思路
#import <Foundation/Foundation.h>
#import "Command.h"
@interface CommandManager : NSObject
// 命令管理容器
@property (nonatomic, strong) NSMutableArray <Command*> *arrayCommands;
?
// 命令管理者以單例方式呈現(xiàn)
+ (instancetype)sharedInstance;
?
// 執(zhí)行命令
+ (void)executeCommand:(Command *)cmd completion:(CommandCompletionCallBack)completion;
?
// 取消命令
+ (void)cancelCommand:(Command *)cmd;
?
@end
#import "CommandManager.h"
?
@implementation CommandManager
?
// 命令管理者以單例方式呈現(xiàn)
+ (instancetype)sharedInstance
{
static CommandManager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[super allocWithZone:NULL] init];
});
return instance;
}
?
// 【必不可少】
+ (id)allocWithZone:(struct _NSZone *)zone{
return [self sharedInstance];
}
?
// 【必不可少】
- (id)copyWithZone:(nullable NSZone *)zone{
return self;
}
?
// 初始化方法
- (id)init
{
self = [super init];
if (self) {
// 初始化命令容器
_arrayCommands = [NSMutableArray array];
}
return self;
}
?
+ (void)executeCommand:(Command *)cmd completion:(CommandCompletionCallBack)completion
{
if (cmd) {
// 如果命令正在執(zhí)行不做處理绘闷,否則添加并執(zhí)行命令
if (![self _isExecutingCommand:cmd]) {
// 添加到命令容器當(dāng)中
[[[self sharedInstance] arrayCommands] addObject:cmd];
// 設(shè)置命令執(zhí)行完成的回調(diào)
cmd.completion = completion;
//執(zhí)行命令
[cmd execute];
}
}
}
?
// 取消命令
+ (void)cancelCommand:(Command *)cmd
{
if (cmd) {
// 從命令容器當(dāng)中移除
[[[self sharedInstance] arrayCommands] removeObject:cmd];
// 取消命令執(zhí)行
[cmd cancel];
}
}
?
// 判斷當(dāng)前命令是否正在執(zhí)行
+ (BOOL)_isExecutingCommand:(Command *)cmd
{
if (cmd) {
NSArray *cmds = [[self sharedInstance] arrayCommands];
for (Command *aCmd in cmds) {
// 當(dāng)前命令正在執(zhí)行
if (cmd == aCmd) {
return YES;
}
}
}
return NO;
}
@end
命令模式的優(yōu)點(diǎn)
降低系統(tǒng)的耦合度橡庞。
新的命令可以很容易地加入到系統(tǒng)中。
可以比較容易地設(shè)計(jì)一個(gè)命令隊(duì)列和宏命令(組合命令)印蔗。
可以方便地實(shí)現(xiàn)對(duì)請(qǐng)求的Undo和Redo扒最。