在了解設計模式之前先了解下設計時的原則;
1. 設計原則
1.1 單一職責原則; 一個類只負責一個功能;
UIView和CALayer的關系;這篇文章結尾
1.2 開閉原則; 對修改關閉, 對擴展開放;
后期迭代類, 函數(shù), 功能模塊時盡量不去更改, 而是通過繼承和合成復用方式解決問題;
1.3 接口隔離原則; 將協(xié)議細分為多個專門的協(xié)議, 而不是一個龐大的多功能協(xié)議;
UITableView的dataSource和delegate的模式, 一個負責數(shù)據(jù)一個負責處理代理回調;
1.4 依賴倒置原則; 抽象內(nèi)容不應該依賴具體實現(xiàn), 具體實現(xiàn)可以依賴于抽象;
1.5 里氏替換原則; 父類可以被子類無縫替換, 且原有功能不受影響;
KVO的實現(xiàn)過程, 中系統(tǒng)自動創(chuàng)建NSKVONotifying_子類替換原有類進而實現(xiàn)功能;
1.6 迪米特法則; 一個對象應該盡量少的去處理管理其他對象;
實現(xiàn)高內(nèi)聚低耦合;模塊之間的解耦;
1.7 合成復用原則; 盡量使用對象組合來達到復用的目的, 而不是繼承;
繼承時一旦基類發(fā)生變化, 那么他的派生類都會跟著變化;RXSwift中有才采用這種策略;
2. 設計模式
2.1 責任鏈模式;
責任鏈模式當主要思想是對象引用同一個類型的另一個對象; 每個對象的實現(xiàn)方法都一樣, 這樣可以形成一種模式就是如果當前對象不處理這個任務, 就把他拋給鏈上的另一個對象;實現(xiàn)方式有繼承和自我實現(xiàn)(next指針)?等方式;
Cocoa中的經(jīng)典用法就是UI的響應鏈;
優(yōu)點:
- 1.解耦請求的發(fā)送者和處理者;
- 2.簡化對象, 不需要關心結構;
- 3.鏈內(nèi)的成員可以隨時增減;
缺點:
- 1.出現(xiàn)問題調試較為麻煩;
- 2.任務并不一定保證能能被處理掉;
責任鏈實現(xiàn)的代碼講解:
#鏈條開始的類
#import <Foundation/Foundation.h>
@class HandleModel;
typedef void(^HandleSuccess)(BOOL handled);
typedef void(^HanleResult)(HandleModel * _Nullable handler, BOOL handled);
NS_ASSUME_NONNULL_BEGIN
@interface HandleModel : NSObject
///下一個響應者, 構成響應鏈的關鍵
@property (nonatomic, strong) HandleModel *nextHandler;
//響應者的處理方法
- (void)handle:(HanleResult)resultB taskType:(NSInteger)type;
///各個業(yè)務在該方法中做實際的處理事宜
- (void)handleBusiess:(HandleSuccess)callB taskType:(NSInteger)type;
@end
NS_ASSUME_NONNULL_END
#import "HandleModel.h"
@implementation HandleModel
///責任鏈入口方法, 如果當前類不處理就向鏈條的下一個指派
- (void)handle:(HanleResult)resultB taskType:(NSInteger)type{
HandleSuccess successB = ^(BOOL success){
if (success) {
resultB(self, success);
}else {
///當前不處理, 沿著責任鏈, 指派給下一個業(yè)務處理
if (self.nextHandler) {
[self.nextHandler handle:resultB taskType:type];
}else {
///沒有處理者, 傳為nil
resultB(nil, NO);
}
}
};
///當前業(yè)務進行處理
[self handleBusiess:successB taskType:type];
}
- (void)handleBusiess:(HandleSuccess)callB taskType:(NSInteger)type{
if (type < 10) {
///執(zhí)行邏輯處理, 處理完成后回調
callB(YES);
}else {
callB(NO);
}
}
@end
#鏈條上的另一個響應者實現(xiàn), 繼承自鏈條開始的類
#import "HandleModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface HandleModelA : HandleModel
@end
NS_ASSUME_NONNULL_END
#import "HandleModelA.h"
@implementation HandleModelA
//這個方法繼承自父類, 邏輯不需變動, 如果當前類不處理就向鏈條的下一個指派
//- (void)handle:(HanleResult)resultB taskType:(NSInteger)type {
// HandleSuccess successB = ^(BOOL success){
// if (success) {
// resultB(self, YES);
// }else {
// if (self.nextHandler) {
// [self.nextHandler handle:resultB taskType:type];
// }else {
// resultB(nil, NO);
// }
// }
// };
// [self handleBusiess:successB taskType:type];
//}
- (void)handleBusiess:(HandleSuccess)callB taskType:(NSInteger)type{
if (type >= 10 && type < 20) {
///執(zhí)行邏輯處理, 處理完成后回調
callB(YES);
}else {
callB(NO);
}
}
@end
實際運用:
- (void)responsiblityChain {
///這個數(shù)組作用是: 模擬一系列需要處理的任務
NSArray *taskArr = @[@(1), @(3), @(11), @(15), @(45), @(23), @(80), @(24), @(66)];
HandleModel *taskModel = [[HandleModel alloc] init];
HandleModel *taskModelA = [[HandleModelA alloc] init];
HandleModel *taskModelB = [[HandleModelB alloc] init];
HandleModel *taskModelC = [[HandleModelC alloc] init];
///創(chuàng)建一個責任鏈, 如果當前模型不處理, 就把任務向鏈中的下一個模型拋
taskModel.nextHandler = taskModelA;
taskModelA.nextHandler = taskModelB;
taskModelB.nextHandler = taskModelC;
///模擬執(zhí)行多個任務
for (NSNumber *taskNum in taskArr) {
[taskModel handle:^(HandleModel *handler, BOOL handled) {
NSLog(@"任務編號: %@ 執(zhí)行狀態(tài): %d 執(zhí)行者: %@", taskNum, handled, handler.class);
} taskType:taskNum.integerValue];
}
}
#執(zhí)行結果為
2019-05-05 16:10:26.395055+0800 DesignMode[7894:497216] 任務編號: 1 執(zhí)行狀態(tài): 1 執(zhí)行者: HandleModel
2019-05-05 16:10:26.395261+0800 DesignMode[7894:497216] 任務編號: 3 執(zhí)行狀態(tài): 1 執(zhí)行者: HandleModel
2019-05-05 16:10:26.395370+0800 DesignMode[7894:497216] 任務編號: 11 執(zhí)行狀態(tài): 1 執(zhí)行者: HandleModelA
2019-05-05 16:10:26.395509+0800 DesignMode[7894:497216] 任務編號: 15 執(zhí)行狀態(tài): 1 執(zhí)行者: HandleModelA
2019-05-05 16:10:26.395654+0800 DesignMode[7894:497216] 任務編號: 45 執(zhí)行狀態(tài): 1 執(zhí)行者: HandleModelC
2019-05-05 16:10:26.395768+0800 DesignMode[7894:497216] 任務編號: 23 執(zhí)行狀態(tài): 1 執(zhí)行者: HandleModelB
2019-05-05 16:10:26.395895+0800 DesignMode[7894:497216] 任務編號: 80 執(zhí)行狀態(tài): 0 執(zhí)行者: (null)
2019-05-05 16:10:26.396014+0800 DesignMode[7894:497216] 任務編號: 24 執(zhí)行狀態(tài): 1 執(zhí)行者: HandleModelB
2019-05-05 16:10:26.396124+0800 DesignMode[7894:497216] 任務編號: 66 執(zhí)行狀態(tài): 0 執(zhí)行者: (null)
2.2 橋接模式;
應用場景, 一個VC要適配多套數(shù)據(jù)模型時; 使用橋接模式能優(yōu)化處理;

代碼講解示例
#抽象業(yè)務類持有屬性抽象數(shù)據(jù)請求類
#import <Foundation/Foundation.h>
#import "RequestModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface BusiessModel : NSObject
///橋接模式的核心實現(xiàn), 抽象類持有;
@property (nonatomic, strong) RequestModel *requestModel;
///處理業(yè)務
- (void)handleTask;
@end
NS_ASSUME_NONNULL_END
#.m實現(xiàn)
#import "BusiessModel.h"
@implementation BusiessModel
/**
具體實現(xiàn)的時候會有四種組合去處理業(yè)務
BusinessA ---> Request1 Request2
BusinessB ---> Request1 Request2
*/
- (void)handleTask {
[self.requestModel requestData];
}
@end
#請求類的實例
#import "RequestModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface Request1 : RequestModel
///實例實現(xiàn)
- (void)requestData;
@end
NS_ASSUME_NONNULL_END
#.m實現(xiàn)
#import "Request1.h"
@implementation Request1
- (void)requestData {
NSLog(@"獲取數(shù)據(jù)2");
}
@end
#業(yè)務類的實例過程
#import "BusiessModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface BusiessA : BusiessModel
///實例實現(xiàn)
- (void)handleTask;
@end
NS_ASSUME_NONNULL_END
#.m實現(xiàn)
#import "BusiessA.h"
@implementation BusiessA
- (void)handleTask {
///在調用父類之前可以處理一些邏輯;
///調用父類實現(xiàn)
[super handleTask];
///在調用父類之后仍然可以處理一些邏輯;
}
@end
///橋接模式
- (void)bridge {
///根據(jù)業(yè)務情形選擇使用BusiessA或者BusiessB
BusiessModel *businessM = [[BusiessA alloc] init];
///根據(jù)業(yè)務情形選擇使用Request1或者Request2
RequestModel *requestM = [[Request2 alloc] init];
///抽象類的邏輯實例實現(xiàn)
businessM.requestModel = requestM;
///真正處理業(yè)務
[businessM handleTask];
}
2.3 適配器模式;
適用場景: 工程中的年代很久遠的類, 并且邏輯已經(jīng)很成熟, 基本上沒什么問題, 如果要對其更改或者擴展, 直接更改是不合適的, 這時通過適配器模式就行擴展比較合適;
代碼講解示例
#一個年代很久遠的類, 功能邏輯已經(jīng)很完善;
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface RemoteModel : NSObject
///這個類的其中一個功能, 已經(jīng)相對很完善
- (void)operationTask;
@end
NS_ASSUME_NONNULL_END
#.m實現(xiàn)
#import "RemoteModel.h"
@implementation RemoteModel
///這個類的其中一個功能, 已經(jīng)相對很完善
- (void)operationTask {
NSLog(@"處理一些邏輯");
}
@end
對其原先邏輯進行擴展
#import <Foundation/Foundation.h>
#import "RemoteModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface NowModel : NSObject
/**
現(xiàn)在想對RemoteModel中 - (void)operationTask; 方法進行添加一些新的邏輯;
因為原來的邏輯已經(jīng)很完整,完善; 通過適配器模式進行擴展;
*/
@property (nonatomic, strong) RemoteModel *remoteM;
///對原先邏輯進行擴展
- (void)nowOperationTask;
@end
NS_ASSUME_NONNULL_END
#.m實現(xiàn)
#import "NowModel.h"
@implementation NowModel
///對原先邏輯進行擴展
- (void)nowOperationTask {
NSLog(@"先執(zhí)行添加的新邏輯");
///然后執(zhí)行原先的邏輯
[self.remoteM operationTask];
NSLog(@"然后執(zhí)行一些補充的新邏輯");
}
@end
///適配器模式
- (void)adapter {
RemoteModel *remoteModel = [[RemoteModel alloc] init];
NowModel *nowModel = [[NowModel alloc] init];
nowM.remoteM = remoteModel;
[nowModel nowOperationTask];
}
2.4 單例模式;
使用場景, 工程中一些常用的邏輯可以放在單例中, 這樣可以快速獲取到; 因為單例整個生命周期只會創(chuàng)建一次, 節(jié)省資源;
代碼示例講解
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
///遵循這兩個協(xié)議是為了防止無意中進行了copy或mutableCopy而開辟新地址;
@interface GlobalModel : NSObject<NSCopying, NSMutableCopying>
+ (GlobalModel *)share;
@end
NS_ASSUME_NONNULL_END
#.m實現(xiàn)
#import "GlobalModel.h"
@implementation GlobalModel
+ (GlobalModel *)share {
static GlobalModel * model = nil;
///確保只能執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
///為什么不能用self進行alloc, 因為重寫下面的方法中返回了[self share]會造成循環(huán);
model = [[super allocWithZone:NULL] init];
});
return model;
}
///確保多次alloc也是同一塊地址
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [self share];
}
///確保即使進行copy了也是同一塊地址
- (id)copyWithZone:(NSZone *)zone {
return self;
}
///確保即使進行mutableCopy了也是同一塊地址
- (id)mutableCopyWithZone:(NSZone *)zone {
return self;
}
@end
///單例模式
- (void)singleton {
GlobalModel *model = [[GlobalModel alloc] init];
GlobalModel *model1 = [[GlobalModel alloc] init];
GlobalModel *model2 = [model copy];
GlobalModel *model3 = [model mutableCopy];
GlobalModel *model4 = [GlobalModel share];
NSLog(@"model地址: %@", model);
NSLog(@"model1地址1: %@", model1);
NSLog(@"model2地址2: %@", model2);
NSLog(@"model3地址3: %@", model3);
NSLog(@"model4地址4: %@", model4);
}
#不論是幾次alloc或者copy, mutableCopy始終是同一塊內(nèi)存地址
2019-05-06 17:19:42.822105+0800 DesignMode[13036:251989] model地址: <GlobalModel: 0x60000174c020>
2019-05-06 17:19:42.822246+0800 DesignMode[13036:251989] model1地址1: <GlobalModel: 0x60000174c020>
2019-05-06 17:19:42.822400+0800 DesignMode[13036:251989] model2地址2: <GlobalModel: 0x60000174c020>
2019-05-06 17:19:42.822511+0800 DesignMode[13036:251989] model3地址3: <GlobalModel: 0x60000174c020>
2019-05-06 17:19:42.822599+0800 DesignMode[13036:251989] model4地址4: <GlobalModel: 0x60000174c020>
參考文章
推薦 面向對象設計原則概述
面向對象設計原則之單一職責原則
面向對象設計原則之開閉原則
面向對象設計原則之接口隔離原則
面向對象設計原則之依賴倒置原則
面向對象設計原則之里氏替換原則
面向對象設計原則之迪米特法則
面向對象設計原則之合成復用原則
iOS 設計模式詳解