用OC程式碼認識六大設計原則

韋家冰發表於2017-12-13

用OC程式碼認識設計模式(一)--建立型模式 用OC程式碼認識設計模式(二)--結構型模式 用OC程式碼認識設計模式(三)--行為型模式 用OC程式碼認識六大設計原則

用OC程式碼認識六大設計原則

  • 單一職責原則:類的職責要單一;
  • 里氏替換原則:不要破壞繼承體系;
  • 依賴倒置原則:要面向介面程式設計;
  • 介面隔離原則:設計介面的時候要精簡;
  • 迪米特法原則:降低耦合;
  • 開放封閉原則:對擴充套件開放,對修改關閉。

#####1、單一職責原則(Single Responsibility Principle,簡稱SRP ) 定義:就一個類而言,應該僅有一個引起它變化的原因。

示例: 1、員工的工資計算。剛開始的時候,我們會新建一個員工類,在員工類裡面有一個計算工資的方法,程式碼如下所示:

//Employee.h
@interface Employee : NSObject
// 計算工資
- (void)calculateSalary:(NSString *)name;
@end

//Employee.m
@implementation Employee
- (void)calculateSalary:(NSString *)name
{
    NSLog(@"%@的工資是100",name);
}
@end
複製程式碼
Employee *employee = [[Employee alloc] init];
[employee calculateSalary:@"張三"];
[employee calculateSalary:@"李四"];

/*結果輸出
張三的工資是100
李四的工資是100
*/
複製程式碼

產品上線後,問題出來了,因為員工的崗位不同,工資的計算是不一樣的。一般有三種修改方法:

(1)簡單做法,修改calculateSalary,增加引數

// 計算工資,增加員工崗位的標識(Director:總監;Manager:經理;Staff:普通員工)
- (void)calculateSalary:(NSString *)name flag:(NSString *)flag
{
    if ([flag isEqualToString:@"Director"])
    {
        NSLog(@"%@總監的工資是10000",name);
    }
    else if ([flag isEqualToString:@"Manager"])
    {
        NSLog(@"%@經理的工資是1000",name);
    }
    else if ([flag isEqualToString:@"Staff"])
    {
        NSLog(@"%@員工的工資是100",name);
    }
}
複製程式碼

(2)新建方法

// 總監工資計算
- (void)directorCalculateSalary:(NSString *)name;
// 經理工資計算
- (void)managerCalculateSalary:(NSString *)name;
// 普通員工工資計算
- (void)staffCalculateSalary:(NSString *)name;
複製程式碼

(3)新建類

Director *director = [[Director alloc] init];
Manager *manager = [[Manager alloc] init];
Staff *staff = [[Staff alloc] init];
複製程式碼

上面三種方式各有優缺點,那麼在實際程式設計中,該採用哪一種呢?這個問題沒有標準答案,需要根據實際情況來確定。 但是隻有(3)是符合單一職責原則,也是最複雜的。

#####2、里氏替換原則(Liskov Substitution Principle,簡稱LSP) 定義:在使用基類的的地方可以任意使用其子類,能保證子類完美替換基類。

里氏替換原則通俗的來講就是:子類可以擴充套件父類的功能,但不能改變父類原有的功能。 它包含以下2層含義: 1、子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法; 2、子類中可以增加自己特有的方法。

#####3、依賴倒置原則(Dependence Inversion Principle,簡稱DIP) 定義:高層模組不應該依賴於低層模組,二者都應該依賴於抽象;抽象不應該依賴細節;細節應該依賴抽象。

例子: SalaryManage類負責工資的管理;Director是總監類,現在我們要通過SalaryManage類來給總監發放工資了,主要程式碼片段如下所示:

// SalaryManage.h
- (void)calculateSalary:(Director *)director
{
    [director calculateSalary];
}
複製程式碼
    // 員工類
    Director *director = [[Directoralloc] init];
    director.strName  = @"張三";
    
    // 工資管理(財務部)
    SalaryManage *salaryManage = [[SalaryManagealloc] init];
    [salaryManage calculateSalary:director];
複製程式碼

假設我們還需要給普通員工、財務總監、研發總監等更多的崗位傳送工資,那麼我們就只能不斷的去修改SalaryManage類來滿足業務的需求。產生這種現象的原因就是SalaryManage與Director之間的耦合性太高了,必須降低它們之間的耦合度才行。因此我們引入一個委託EmployeeDelegate,它提供一個發放工資的方法定義,如下所示: 然後我們讓具體的員工類Director、Manager等都實現EmployeeDelegate委託方法

@protocol EmployeeDelegate <NSObject> 
- (void)calculateSalary;
@end
複製程式碼
// 修改後的SalaryManage計算工資方法:
- (void)calculateSalary:(id<EmployeeDelegate>)employee
{
    [employee calculateSalary];
}
複製程式碼
    // 使用例子
    Director *director = [[Directoralloc] init];
    director.strName  = @"張三";
    
    Manager *manager = [[Manageralloc] init];
    manager.strName  = @"李四";
    
    SalaryManage *salaryManage = [[SalaryManagealloc] init];
    [salaryManage calculateSalary:director];
    [salaryManage calculateSalary:manager];
複製程式碼

這與物件介面卡模式差不多。

######4、介面隔離原則(Interface Segregation Principle,簡稱ISP) 定義:類間的依賴關係應該建立在最小的介面上

用OC程式碼認識六大設計原則
在這裡,我們定義了一個動物活動的介面IAnimal,裡面有4個方法:飛行fly、行走walk、吃eat和工作work,然後分別用人類People和鳥類Bird實現了這個介面。中國人類ChinesePeople和鸚鵡類Parrot通過介面IAnimal分別依賴類People和類Bird。很明顯,對於ChinesePeople來說,fly方法是多餘的,因為人不會飛;對於Parrot類來說,work方法是多餘的,因為鸚鵡不需要工作。介面IAnimal對於類ChinesePeople和類Parrot來說不是最小介面。

解決方案

將臃腫的介面IAnimal拆分為獨立的幾個介面,類ChinesePeople和類Parrot分別與它們需要的介面建立依賴關係,也就是採用介面隔離原則。修改後的UML圖如下所示:

用OC程式碼認識六大設計原則

#####5、迪米特法則(Law of Demeter,簡稱LoD) 定義:如果兩個類不必彼此直接通訊,那麼這兩個類就不應當發生直接的相互作用。如果其中的一個類需要呼叫另一個類的某一個方法的話,可以通過第三者轉發這個呼叫,這裡的第三者就是介面InmatesDelegate。

#####6、開放封閉原則(Open Close Principle,簡稱OCP)

定義:一個軟體實體(如類、模組、函式)應當對擴充套件開放,對修改關閉。

儘量讓設計的類做好後就不再修改,如果有新的需求,通過新加類的方式來滿足,而不去修改現有的類(程式碼)。

相關文章