设计模式之创建型模式

前言

  前段时间看了《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.建造者模式、原型模式

  这两种设计模式平时用得较少,暂时不讨论,后面有时间再继续研究。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342