設計模式之感悟和實踐1

背景

設計模式能夠說是老生常談的一個知識點。工做這麼多年來也是陸陸續續看過幾本書。好比《大話設計模式》、《Head First 設計模式》,這兩本書是前期接觸比較多,並且質量仍是不錯的兩本書,不過以前看的感受有點像豬八戒吃人參果——食而不知其味。編程

不少時候有的人在寫代碼的過程當中對設計模式並不爲然。像本人是作APP開發的,APP開發結束後,迭代和新增業務邏輯不是不少的話,確實之前寫成啥樣其實真的無所謂。直到近來接手了公司一個歷史悠久的主項目,因爲業務的發展,業務邏輯愈來愈多,改動愈來愈大,每次新增業務都舉步維艱,讓我深入的體會到了設計模式的優勢和威力。設計模式

近來正好又從新拾起設計模式在看另一本書《設計模式之禪》這本書,這本書較之其餘兩本書感受比較接地氣吧。此次看着很慢,每一個模式都在細細的琢磨,再加上以前項目的經驗和此次槽糕的老項目,收穫比較多,感悟也比較深。api

設計原則

要說設計模式,設計原則是必不可缺乏的。若是單單去看這幾個原則,其實一眼就能看完。原則只是原則,是要儘可能保持,而不是必須保持的哦!因此不能一言而論,根據業務和情景懂得變通。函數

單一原則

應該有且僅有一個緣由引發類的變動。組件化

這個原則在使用的過程當中要作到適度,若是過分使用的話,能夠將一個類中全部的方法都對應作成一個類。 其實在使用過程當中說白了就是根據業務或某一方面將功能歸類,同一功能的放在一塊兒,聲明一個接口。atom

好比上圖,咱們根據用戶的屬性和用的行爲,劃分爲兩個接口。

根據以上例子單一原則的有點也是顯而易見:類的複雜性下降,可維護性高。spa

里氏替換原則

只要父類出現的地方子類就能夠出現,並且替換爲子類也不會產生任何錯誤或異常,使用這可能根本就不須要知道是父類仍是子類。可是,反過來就不行了。設計

這個原則用到的其實就是類的繼承,在一些狀況下繼承的優勢不言而喻,不過在項目中不要隨便使用繼承。繼承是能夠用其餘設計模式替換的,好比裝飾模式等。 繼承具備如下缺點:3d

  • 繼承是侵入性的。只要繼承,就必須擁有父類的全部屬性和方法。這個危害性仍是挺大的!
  • 下降了代碼耦合性。子類必須擁有父類的屬性和方法,讓子類自由的世界中多了些約束
  • 增長了耦合性。當父類的常量、變量和方法被修改時,須要考慮子類的修改,並且在缺少規範的環境下,這種修改時毀滅性的。

若是子類不能完整地實現父類的方法,或者父類的某些方法在子類中已經發生"畸形",則建議斷開父子繼承關係,採用依賴、彙集、組合等關係代替繼承code

依賴倒置原則

高層模塊不該該依賴底層模塊,二者都應該依賴其抽象(接口或實現類)。一句大白話就是面向接口編程

也就是APP開發過程當中,只要有接口文檔,咱們就能夠實現APP啦,不須要後臺人員api的實現。也能夠這麼理解。

接口隔離原則

創建單一接口,不要創建臃腫龐大的接口。

單一職責要求的是類和接口職責單一,注重的是職責,這是業務邏輯上的劃分,而接口隔離原則要求接口的方法儘可能少。

例如一個接口的職責可能包含10個方法,這10個方法都放在一個接口中,而且提供給多個模塊訪問,各個模塊按照規定的權限來訪問,在系統外經過文檔約束「不使用的方法不要訪問」,按照單一職位原則是容許的,按照接口隔離原則是不容許的,由於它要求「儘可能使用多個專門的接口」。就是指提供給每一個模塊的都應該是單一接口,提供給幾個模塊就應該有幾個接口,而不是建議一個龐大的臃腫的接口。 咱們在使用組件化的過程當中,因爲模塊間的調用,每一個模塊都對外聲明一個公共接口,這時候其實就違背了接口隔離原則。好比咱們能夠按照同一層級調用聲明一個接口,不一樣層級的調用聲明一個接口。

迪米特法則

一個對象應該對其餘對象有最少的瞭解

  • 只和朋友交流 朋友:出如今成員變量、方法的輸入輸出參數中的類稱爲成員朋友類,而出如今方法體內的類不屬於朋友類 好比咱們在方法中使用了一個局部對象變量,這就違背了這個原則
  • 是本身的就是本身的 若是一個方法放在本類中,既不增長類間關係,也對本類不產生負面影響,那就放置在本類中。

開閉原則

一個軟件實體如類、模塊和函數應該對外擴展開放,對修改關閉。即軟件實體應該對擴展開放,對修改關閉,其含義是說一個軟件實體應該經過擴展來實現變化,而不是經過修改已有的代碼來實現變化。


上邊囉嗦一下設計模式的原則,其實咱們在項目實踐中也就是由於代碼違背了其中的原則,而後進行改進,進而演化出設計模式。

因此設計模式都是基於以上原則產生的。

場景使用

此次咱們介紹的是責任鏈模式

責任鏈模式的重點是在「鏈」上,由一條鏈去處理類似的請求在鏈中決定誰來處理這個請求,並返回相應的結果。

經過這個定義不知道你們有沒有能想到應用的場景呢?

像在咱們工程中因爲業務場景的複雜性,就存在大量的if...else判斷。這樣的邏輯致使業務交叉在一塊兒,致使每一個業務不清晰,擴展起來不是很方便,而且在iOS中會致使UIViewController臃腫。

這個時候咱們能夠引入責任鏈模式,調用方不用關心真正的業務處理,只要關心業務分類就行,真正的業務交給一個實體類來處理。

首先咱們看一下責任鏈模式通用類圖

這個看着可能有點不知道因此然,因此咱們來個實例講解一下,相信看了以後就會恍然大悟。

這是demo全部的類圖

ActionClickProtocol

@protocol ActionClickProtocol <NSObject>
- (void)handleClick;
- (void)setNext:(id<ActionClickProtocol>)actionClickHandle;
@end

typedef NS_ENUM(NSUInteger, HandleType) {
    CLICK1,
    CLICK2,
};
複製代碼

ActionClickHandle

@interface ActionClickHandle : NSObject<ActionClickProtocol>
@property (nonatomic,assign)HandleType type;
@property (nonatomic,strong)ActionClickHandle *nextHandle;
@end


@implementation ActionClickHandle
- (void)handleClick {
    NSLog(@"共有的處理方法");
}

- (void)setNext:(nonnull id<ActionClickProtocol>)actionClickHandle {
    self.nextHandle = actionClickHandle;
}
@end
複製代碼

ActionClickEvent1

@interface ActionClickEvent1 : ActionClickHandle
@end

@implementation ActionClickEvent1
-(void)handleClick{
    NSLog(@"事件1的處理");
}
@end
複製代碼

ActionClickEvent2

@interface ActionClickEvent2 : ActionClickHandle
@end

@implementation ActionClickEvent2
-(void)handleClick{
   NSLog(@"事件2的處理");
}
@end
複製代碼

MyHandle 這個類是核心對外使用的類

@interface MyHandle : NSObject
- (instancetype)initWithType:(HandleType)type;
- (void)handleClick;
@end

@interface MyHandle()
@property (nonatomic,assign)HandleType type;
@property (nonatomic,strong)ActionClickHandle *nextHandle;
@property (nonatomic,strong)ActionClickEvent1 *event1;
@property (nonatomic,strong)ActionClickEvent2 *event2;
@end
@implementation MyHandle
- (instancetype)initWithType:(HandleType)type{
    self = [super init];
    if (self) {
        _type = type;
        _event1 = [[ActionClickEvent1 alloc] init];
        _event1.type = CLICK1;
        _event2 = [[ActionClickEvent2 alloc] init];
        _event2.type = CLICK2;
        [_event1 setNextHandle:_event2];
        self.nextHandle = _event1;
    }
    return self;
}
- (void)handleClick{
    if (self.nextHandle.type==self.type) {
        [self.nextHandle handleClick];
    }else{
        while (self.nextHandle.type!=self.type) {
            self.nextHandle = self.nextHandle.nextHandle;
        }
        [self.nextHandle handleClick];
    }
}

@end
複製代碼

使用範例:

MyHandle *myHandle = [[MyHandle alloc] initWithType:CLICK2];
[myHandle handleClick];
複製代碼

咱們在使用的過程當中能夠面向model開發,將model傳入 handleClick方法中。type則是mode中根據接口數據的返回對應的不一樣類型。

這種使用方法徹底能夠避免if...else的使用,而且業務邏輯很清晰。

總結

其實項目使用過程當中,關鍵一點仍是要去發現那塊代碼會一直變,將常常變換的代碼進行設計模式的封裝,則之後的代碼擴展是很是的方便

個人博客

個人博客

相關文章
相關標籤/搜索