設計模式之建立型模式

weixin_33978044發表於2018-01-03

前言

  前段時間看了《Head First設計模式》這本書,雖然沒有看完,但常用的設計模式基本都看了一遍。剛開始看完基本都能理解,也能體會到設計模式的妙處,但是設計模式在平時工作中用得較少,一段時間後再來回憶看過的這些東西,基本上只能記住它們的名字,其他的都想不起來。所以準備再統一複習一遍常用的設計模式,順便寫點自己理解的東西,當做筆記吧,希望以後想不起來時再回來看能夠迅速的想起。

正式開始

  言歸正傳,《Head First設計模式》這本書講了23種設計模式,網上大致將這23種模式分為三類,分別為:

  • 建立型模式:指的是用來建立物件以便能從系統中解耦。
    有工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
  • 結構型模式:指的是通過各個物件來組成大規模的物件結構。
    有介面卡模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
  • 行為型模式:指的是用來在物件之間管理演算法、關係等。
    有策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、直譯器模式。

建立者模式

0.簡單工廠模式

  簡單工廠模式其實不算在23種設計模式中,但在平時開發中用的場景還是比較多的,而且和工廠方法模式、抽象工廠模式類似,所以還是先看下簡單工廠模式。
  簡單工廠模式有一個工廠、一類產品,外部呼叫並不關心產品是怎麼建立的,它只需要告訴這個工廠,我需要什麼型別的產品即可,具體建立邏輯由工廠內部處理。工廠會根據呼叫者傳給它的型別進行產品的建立,關係圖如下:
圖1

舉個栗子,經典的汽車生產:
首先,建立汽車基類:

@interface Car : NSObject

- (void)carInfomation;

@end

然後建立它的具體實現類:

@interface AudiCar : Car

@end

@implementation AudiCar

- (void)carInfomation {
    NSLog(@"I am AudiCar");
}

@end
@interface BenzCar : Car

@end

@implementation BenzCar

- (void)carInfomation {
    NSLog(@"I am BenzCar");
}

@end

最後建立工廠:

@implementation CarsFactory

+ (Car *)createCarWithType:(CarType)carType {
    Car *car;
    switch (carType) {
        case CarTypeAudi:
            car = [[AudiCar alloc] init];
            break;
        case CarTypeBenz:
            car = [[BenzCar alloc] init];
            break;
    }
    
    return car;
}

@end

外部呼叫者只需給工廠提供引數即可:

- (void)testSimpleFactory {
    // Audi
    Car *audi = [CarsFactory createCarWithType:CarTypeAudi];
    [audi carInfomation];
    
    // Benz
    Car *benz = [CarsFactory createCarWithType:CarTypeBenz];
    [benz carInfomation];
}

測試結果:

2018-01-03 21:39:41.834124+0800 DesignPatternDemo[1582:77442] I am AudiCar
2018-01-03 21:39:41.834264+0800 DesignPatternDemo[1582:77442] I am BenzCar
總結

  簡單工廠模式可以將同一型別產品建立集中到工廠裡,使用者無需知道產品的內部建立邏輯,只需提供產品的型別,工廠就可以建立出相應的產品。
  雖然簡單工廠模式可以實現呼叫者跟產品之間的解耦,但是並不符合開閉原則,即產品的建立依賴於工廠類,每次新增一款新產品就得修改工廠內部的實現,產品越多switch(或者if-else)就越長。因此就有了工廠方法模式。

1.工廠方法模式

  工廠方法模式是建立一個工廠基類以及若干個具體工廠類,每個工廠只負責自己這款產品的建立,即每款產品有自己對應的一個工廠。這樣如果新加一款產品不需要去修改之前工廠實現,只需再實現一個建立新產品的工廠。具體關係如下:
關係圖2.

還是那個栗子:
汽車的結構不變,修改工廠的實現,首先建立一個工廠基類:

@interface CarBaseFactory : NSObject

+ (Car *)createCar;

@end

然後實現建立具體汽車的具體工廠:

@interface AudiCarFactory : CarBaseFactory

@end

@implementation AudiCarFactory

+ (Car *)createCar {
    AudiCar *car = [[AudiCar alloc] init];
    return car;
}

@end
@interface BenzCarFactory : CarBaseFactory

@end

@implementation BenzCarFactory

+ (Car *)createCar {
    BenzCar *car = [[BenzCar alloc] init];
    return car;
}

@end

外部呼叫者:

- (void)testFactoryMethod {
    // Audi
    Car *audi = [AudiCarFactory createCar];
    [audi carInfomation];
    
    // Benz
    Car *benz = [BenzCarFactory createCar];
    [benz carInfomation];
}

此時如果需要再加新的產品,就不需要再去改之前的程式碼,只需新增新的產品和建立該產品的工廠,程式碼如下:

新的汽車保時捷:

@interface PorscheCar : Car

@end

@implementation PorscheCar

- (void)carInfomation {
    NSLog(@"I am PorscheCar");
}

@end

保時捷汽車工廠:

@interface PorscheCarFactory : CarBaseFactory

@end

@implementation PorscheCarFactory

+ (Car *)createCar {
    PorscheCar *car = [[PorscheCar alloc] init];
    return car;
}

@end

外部呼叫者:

- (void)testFactoryMethod {
    // Audi
    Car *audi = [AudiCarFactory createCar];
    [audi carInfomation];
    
    // Benz
    Car *benz = [BenzCarFactory createCar];
    [benz carInfomation];
    
    // Porsche
    Car *porsche = [PorscheCarFactory createCar];
    [porsche carInfomation];
}

測試結果:

2018-01-03 22:11:46.448233+0800 DesignPatternDemo[1794:108045] I am AudiCar
2018-01-03 22:11:46.448372+0800 DesignPatternDemo[1794:108045] I am BenzCar
2018-01-03 22:11:46.448465+0800 DesignPatternDemo[1794:108045] I am PorscheCar
總結

  工廠方法模式通過每個工廠只負責一款產品的建立來解決簡單工廠模式不符合開閉原則的缺點,但也帶來了類檔案過多的缺點(大多數設計模式都會有這個缺點),很多時候這些類檔案都只實現很簡單的功能。

3.抽象工廠模式

  抽象工廠模式是工廠方法模式的升級版,或者說工廠方法模式是抽象工廠模式的特例。在工廠方法模式中一個工廠只能建立一款產品,而在抽象工廠模式中一個工廠可以建立一個產品簇(即一個產品和這個產品的許多線下產品)。關係圖如下:
關係圖3.

還是那個栗子,各種不同的汽車可能對應著不同的配件,比如Audi配置著發動機A、空調A,Benz配置著發動機B、空調B。
首先定義發動機:

@interface Engine : NSObject

- (void)engineInfomation;

@end
@interface EngineA : Engine

@end

@implementation EngineA

- (void)engineInfomation {
    NSLog(@"I am EngineA");
}

@end
@interface EngineB : Engine

@end

@implementation EngineB

- (void)engineInfomation {
    NSLog(@"I am EngineB");
}

@end

定義空調:

@interface AirConditioner : NSObject

- (void)airConditionerInfomation;

@end
@interface AirConditionerA : AirConditioner

@end

@implementation AirConditionerA

- (void)airConditionerInfomation {
    NSLog(@"I am AirConditionerA");
}

@end
@interface AirConditionerB : AirConditioner

@end

@implementation AirConditionerB

- (void)airConditionerInfomation {
    NSLog(@"I am AirConditionerB");
}

@end

修改汽車類,新增發動機和空調屬性:

@interface Car : NSObject

@property (nonatomic, strong) Engine *engine;

@property (nonatomic, strong) AirConditioner *airConditioner;

- (void)carInfomation;

@end

為工廠類新增建立發動機和空調的方法:

@interface CarBaseFactory : NSObject

+ (Car *)createCar;

+ (Engine *)createEngine;

+ (AirConditioner *)createAirConditioner;

@end
@implementation AudiCarFactory

+ (Car *)createCar {
    AudiCar *car = [[AudiCar alloc] init];
    return car;
}

+ (Engine *)createEngine {
    EngineA *engine = [[EngineA alloc] init];
    return engine;
}

+ (AirConditioner *)createAirConditioner {
    AirConditionerA *airConditioner = [[AirConditionerA alloc] init];
    return airConditioner;
}

@end
@implementation BenzCarFactory

+ (Car *)createCar {
    BenzCar *car = [[BenzCar alloc] init];
    return car;
}

+ (Engine *)createEngine {
    EngineB *engine = [[EngineB alloc] init];
    return engine;
}

+ (AirConditioner *)createAirConditioner {
    AirConditionerB *airConditioner = [[AirConditionerB alloc] init];
    return airConditioner;
}

@end

外部呼叫者呼叫:

- (void)testAbstractFactory {
    // Audi
    Car *audi = [AudiCarFactory createCar];
    audi.engine = [AudiCarFactory createEngine];
    audi.airConditioner = [AudiCarFactory createAirConditioner];
    [audi carInfomation];
    [audi.engine engineInfomation];
    [audi.airConditioner airConditionerInfomation];
    
    // Benz
    Car *benz = [BenzCarFactory createCar];
    benz.engine = [BenzCarFactory createEngine];
    benz.airConditioner = [BenzCarFactory createAirConditioner];
    [benz carInfomation];
    [benz.engine engineInfomation];
    [benz.airConditioner airConditionerInfomation];
}

測試結果:

2018-01-03 22:42:55.410243+0800 DesignPatternDemo[1958:151756] I am AudiCar
2018-01-03 22:42:55.410380+0800 DesignPatternDemo[1958:151756] I am EngineA
2018-01-03 22:42:55.410488+0800 DesignPatternDemo[1958:151756] I am AirConditionerA
2018-01-03 22:42:55.410605+0800 DesignPatternDemo[1958:151756] I am BenzCar
2018-01-03 22:42:55.410703+0800 DesignPatternDemo[1958:151756] I am EngineB
2018-01-03 22:42:55.410976+0800 DesignPatternDemo[1958:151756] I am AirConditionerB

總結

  工廠方法模式只有一個抽象產品類,而抽象工廠模式有多個。工廠方法模式的具體工廠類只能建立一個具體產品類的例項,而抽象工廠模式可以建立多個。可以這麼理解,抽象工廠就像一家完整的工廠,可以建立工廠裡的各種產品,而工廠方法就像工廠裡的一條產品線,只負責生產一款產品。

4.單例模式

  單例模式在iOS開發中相當重要,也相當常見。單例模式保證了在程式執行期間該物件只有一個例項。
示例程式碼:

@implementation CarPerformanceCenter

+ (instancetype)shareInstance {
    static CarPerformanceCenter *center;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        center = [[CarPerformanceCenter alloc] init];
    });
    
    return center;
}

@end

5.建造者模式、原型模式

  這兩種設計模式平時用得較少,暫時不討論,後面有時間再繼續研究。

相關文章