一、六大設計原則
單一職責原則:一個類只負責一件事
依賴倒置原則:抽象不該依賴于具體實現,具體實現可以依賴抽象
開閉原則:對修改關閉,對擴展開放
里氏替換原則:父類可以被子類無縫替換,且原有功能不受影響(例如:KVO)
接口隔離原則:使用多個專門的協議、而不是一個龐大臃腫的協議(例如:UITableViewDelegate,UITableViewDataSource)
迪米特法則:一個對象應當對其他對象盡可能少的了解(高內聚、高耦合)
關于設計原則可以看這篇文章面向對象設計的六大設計原則(附 Demo 及 UML類圖)
二、責任鏈模式
主要思想:對象引用了同一類型的另一個對象,形成一條鏈。鏈中的每個對象實現了相同的方法,處理對鏈中第一個對象發起的同一請求,如果一個對象不知道如何處理,就把請求傳給下一個響應器。
代碼示例:
@class BusinessObject;
typedef void(^CompletionBlock)(BOOL handled);
typedef void(^ResultBlock)(BusinessObject *handler, BOOL handled);
?
@interface BusinessObject : NSObject
?
// 下一個響應者(響應鏈構成的關鍵)
@property (nonatomic, strong) BusinessObject *nextBusiness;
// 響應者的處理方法
- (void)handle:(ResultBlock)result;
?
// 各個業務在該方法當中做實際業務處理
- (void)handleBusiness:(CompletionBlock)completion;
@end
@implementation BusinessObject
?
// 責任鏈入口方法
- (void)handle:(ResultBlock)result
{
CompletionBlock completion = ^(BOOL handled){
// 當前業務處理掉了,上拋結果
if (handled) {
result(self, handled);
}
else{
// 沿著責任鏈,指派給下一個業務處理
if (self.nextBusiness) {
[self.nextBusiness handle:result];
}
else{
// 沒有業務處理, 上拋
result(nil, NO);
}
}
};
// 當前業務進行處理
[self handleBusiness:completion];
}
?
- (void)handleBusiness:(CompletionBlock)completion
{
/*
業務邏輯處理
如網絡請求、本地照片查詢等
*/
}
?
@end
三、橋接模式
橋接模式的目的是把抽象層次結構從其實現中分離出來,使其能夠獨立變更。
Class A 和ClassB都是抽象類。ClassA中一個成員變量是ClassB的對象。ClassB中作為抽象類,只提供了默認的接口,并沒有實現。B1、B2、B3是ClassB的三個子類,重寫父類的接口方法,提供不同的實現,此時對于ClassB使用方來說,是感知到不使用了哪個實現。ClassA中有一個handle處理方法,默認調用成員變量ClassB對象中的接口方法。A1、A2、A3三個是ClassA的子類,對于子類來說可以覆寫父類的handle方法,做一些自定義的操作。
代碼示例: ClassA
#import <Foundation/Foundation.h>
#import "BaseObjectB.h"
@interface BaseObjectA : NSObject
?
// 橋接模式的核心實現
@property (nonatomic, strong) BaseObjectB *objB;
?
// 獲取數據
- (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 業務邏輯操作
[super handle];
// after 業務邏輯操作
}
@end
ClassB 實現
#import <Foundation/Foundation.h>
?
@interface BaseObjectB : NSObject
?
- (void)fetchData;
?
@end
#import "BaseObjectB.h"
?
@implementation BaseObjectB
// 默認邏輯實現
- (void)fetchData
{
// override to subclass
}
@end
ClassB的子類進行具體的邏輯實現。
#import "ObjectB1.h"
?
@implementation ObjectB1
?
- (void)fetchData{
// 具體的邏輯處理
}
@end
使用方代碼實現
@interface BridgeDemo()
@property (nonatomic, strong) BaseObjectA *objA;
@end
?
@implementation BridgeDemo
?
/*
根據實際業務判斷使用那套具體數據
A1 --> B1、B2、B3 3種
A2 --> B1、B2、B3 3種
A3 --> B1、B2、B3 3種
*/
- (void)fetch
{
// 創建一個具體的ClassA
_objA = [[ObjectA1 alloc] init];
// 創建一個具體的ClassB
BaseObjectB *b1 = [[ObjectB1 alloc] init];
// 將一個具體的ClassB1 指定給抽象的ClassB
_objA.objB = b1;
// 獲取數據
[_objA handle];
}
@end
使用方中定義了ClassA對象,可以使用A1、A2、A3來創建不同的對象,獲取不同的實現組合。BaseObjectB也可以有不同的實現組合。通過橋接模式不同的組合可以實現對象之間的解耦。
橋接模式的優點:
分離抽象接口及其實現部分。
橋接模式有時類似于多繼承方案,但是多繼承方案違背了類的單一職責原則(即一個類只有一個變化的原因),復用性比較差,而且多繼承結構中類的個數非常龐大,橋接模式是比多繼承方案更好的解決方法。
橋接模式提高了系統的可擴充性,在兩個變化維度中任意擴展一個維度,都不需要修改原有系統。
實現細節對客戶透明,可以對用戶隱藏實現細節。
四、適配器
適配器模式(Adapter Pattern):將一個接口轉換成客戶希望的另一個接口,適配器模式使接口不兼容的那些類可以一起工作,其別名為包裝器(Wrapper)。適配器模式既可以作為類結構型模式,也可以作為對象結構型模式。
本節主要學習對象適配器模式,簡單的類結構如下。
適配對象中一個成員變量指向被適配對象。
示例代碼:類Target是被適配對象,CoolTarget為適配對象。
Target類
#import <Foundation/Foundation.h>
?
@interface Target : NSObject
?
- (void)operation;
?
@end
#import "Target.h"
?
@implementation Target
?
- (void)operation
{
// 原有的具體業務邏輯
}
?
@end
CoolTarget類:
#import "Target.h"
?
// 適配對象
@interface CoolTarget : NSObject
?
// 被適配對象
@property (nonatomic, strong) Target *target;
?
// 對原有方法包裝
- (void)request;
?
@end
#import "CoolTarget.h"
?
@implementation CoolTarget
?
- (void)request
{
// 額外處理
[self.target operation];
// 額外處理
}
?
@end
適配器優點:
將目標類和適配者類解耦,通過引入一個適配器類來重用現有的適配者類,而無須修改原有代碼。
增加了類的透明性和復用性,將具體的實現封裝在適配者類中,對于客戶端類來說是透明的,而且提高了適配者的復用性。
靈活性和擴展性都非常好,通過使用配置文件,可以很方便地更換適配器,也可以在不修改原有代碼的基礎上增加新的適配器類,完全符合“開閉原則”。
三、單例
單例模式(SingletonPattern):單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例,這個類稱為單例類,它提供全局訪問的方法。
單例模式的要點有三個:一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。單例模式是一種對象創建型模式。單例模式又名單件模式或單態模式。
示例代碼:
@implementation Mooc
?
+ (id)sharedInstance
{
// 靜態局部變量
static Mooc *instance = nil;
// 通過dispatch_once方式 確保instance在多線程環境下只被創建一次
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;
}
?
@end
注意點:為了防止使用者創建對象,需要從重寫兩個方法allocWithZone
和copyWithZone:
。另外instance = [[super allocWithZone:NULL] init];
需要使用super方法調用防止在第一創建時循環調用。
四、命令模式
命令模式(CommandPattern):將一個請求封裝為一個對象,從而使我們可用不同的請求對客戶進行參數化;對請求排隊或者記錄請求日志,以及支持可撤銷的操作。命令模式是一種對象行為型模式,其別名為動作(Action)模式或事務(Transaction)模式。
代碼實例:一個命令對象和一個命令管理者。
Command
@class Command;
typedef void(^CommandCompletionCallBack)(Command* cmd);
?
@interface Command : NSObject
@property (nonatomic, copy) CommandCompletionCallBack completion; // 執行回調
?
- (void)execute; // 執行
- (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;
// 在數組中移除
[[CommandManager sharedInstance].arrayCommands removeObject:self];
});
}
?
@end
CommandManager
可以用CommandManager保證任務的順序執行,使用一個正在執行任務數組和一個等待執行任務數組,可以參考SDWebImage圖片下載思路
#import <Foundation/Foundation.h>
#import "Command.h"
@interface CommandManager : NSObject
// 命令管理容器
@property (nonatomic, strong) NSMutableArray <Command*> *arrayCommands;
?
// 命令管理者以單例方式呈現
+ (instancetype)sharedInstance;
?
// 執行命令
+ (void)executeCommand:(Command *)cmd completion:(CommandCompletionCallBack)completion;
?
// 取消命令
+ (void)cancelCommand:(Command *)cmd;
?
@end
#import "CommandManager.h"
?
@implementation CommandManager
?
// 命令管理者以單例方式呈現
+ (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) {
// 如果命令正在執行不做處理,否則添加并執行命令
if (![self _isExecutingCommand:cmd]) {
// 添加到命令容器當中
[[[self sharedInstance] arrayCommands] addObject:cmd];
// 設置命令執行完成的回調
cmd.completion = completion;
//執行命令
[cmd execute];
}
}
}
?
// 取消命令
+ (void)cancelCommand:(Command *)cmd
{
if (cmd) {
// 從命令容器當中移除
[[[self sharedInstance] arrayCommands] removeObject:cmd];
// 取消命令執行
[cmd cancel];
}
}
?
// 判斷當前命令是否正在執行
+ (BOOL)_isExecutingCommand:(Command *)cmd
{
if (cmd) {
NSArray *cmds = [[self sharedInstance] arrayCommands];
for (Command *aCmd in cmds) {
// 當前命令正在執行
if (cmd == aCmd) {
return YES;
}
}
}
return NO;
}
@end
命令模式的優點
降低系統的耦合度。
新的命令可以很容易地加入到系統中。
可以比較容易地設計一個命令隊列和宏命令(組合命令)。
可以方便地實現對請求的Undo和Redo。