iOS-設(shè)計(jì)模式在開發(fā)中的應(yīng)用

設(shè)計(jì)模式.png

一蚂维、六大設(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ú)立變更。

1363078-98ccc9b19a331319.png

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)如下急侥。

1363078-6c858d3c2443e4d1.png

適配對(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è)方法allocWithZonecopyWithZone:亭引。另外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扒最。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市华嘹,隨后出現(xiàn)的幾起案子吧趣,更是在濱河造成了極大的恐慌,老刑警劉巖耙厚,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件强挫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡薛躬,警方通過查閱死者的電腦和手機(jī)俯渤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來型宝,“玉大人八匠,你說我怎么就攤上這事絮爷。” “怎么了梨树?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵坑夯,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我抡四,道長(zhǎng)柜蜈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任指巡,我火速辦了婚禮跨释,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘厌处。我一直安慰自己鳖谈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布阔涉。 她就那樣靜靜地躺著缆娃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瑰排。 梳的紋絲不亂的頭發(fā)上贯要,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音椭住,去河邊找鬼崇渗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛京郑,可吹牛的內(nèi)容都是我干的宅广。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼些举,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼跟狱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起户魏,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤驶臊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后叼丑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體关翎,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年鸠信,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纵寝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡症副,死狀恐怖店雅,靈堂內(nèi)的尸體忽然破棺而出政基,到底是詐尸還是另有隱情贞铣,我是刑警寧澤闹啦,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站辕坝,受9級(jí)特大地震影響窍奋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜酱畅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一琳袄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纺酸,春花似錦窖逗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至樊诺,卻和暖如春仗考,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背词爬。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工秃嗜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人顿膨。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓锅锨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親恋沃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子橡类,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容