iOS设计模式-工厂方法与抽象工厂

1. 什么是工厂模式

工厂模式(Factory pattern)在设计模式中属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象

2. 工厂模式分类

工厂模式一般可以分为三类:

  • 简单工厂模式(Simple Factory Pattern)
  • 工厂方法模式(Factory Method Pattern)
  • 抽象工厂模式(Abstract Factory Pattern)

接下来我们分别对这三个模式进行说明。

2.1 简单工厂模式

简单工厂模式又称作静态工厂方法模式,简单工厂模式专门定义一个工厂类来负责创建其他类的实例,且被创建的实例通常都具有共同的父类。它虽然不属于设计模式中的一种,但是在应用中依然很常见。

2.1.1 主要角色与关系

主要由三个角色组成:

  • 工厂类(Factory):简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
  • 抽象产品类(AbstractProduct):简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
  • 具体产品类(Product):是简单工厂模式的创建实体类。


    图片.png
2.1.2 代码实现

我们以生产电脑为例,假设有一个代工厂,它既可以生产苹果电脑,也可以生产小米电脑,这时候如果需要生产其中任意一种电脑,就直接找代工厂就行了。

  1. 创建一个产品抽象类Computer,定义好基础方法

// Computer.h

#import <Foundation/Foundation.h>

@interface Computer : NSObject
- (NSString *)productName;
@end


// Computer.m

#import "Computer.h"

@implementation Computer
- (NSString *)productName {
    NSAssert(false, @"must implement in subClass");
    return nil;
}
@end
  1. 创建产品具体类
    2.1 mac电脑类

// Mac.h
#import "Computer.h"

@interface Mac : Computer

@end


// Mac.m

#import "Mac.h"

@implementation Mac

- (NSString *)productName {
    return @"Mac";
}

@end

2.2 创建小米电脑类


// XiaoMi.h

#import "Computer.h"

@interface XiaoMi : Computer

@end


// XiaoMi.m

#import "XiaoMi.h"

@implementation XiaoMi

- (NSString *)productName {
    return @"XiaoMi";
}

@end
  1. 产品都已经定义好了,接下来我们需要定义一个工厂类,装门从事生产这些商品

// ComputerSimpleFactory.h
#import <Foundation/Foundation.h>
#import "Mac.h"
#import "XiaoMi.h"

@interface ComputerSimpleFactory : NSObject

+ (Computer *)createComputerWithType:(NSInteger)type;

@end


// ComputerSimpleFactory.m

#import "ComputerSimpleFactory.h"

@implementation ComputerSimpleFactory

+ (Computer *)createComputerWithType:(NSInteger)type {
    switch (type) {
        case 1: {
            //do something for create
            Mac *computer = [[Mac alloc] init];
            return computer;
        }
            break;
        case 2: {
            //do something for create
            XiaoMi *computer = [[XiaoMi alloc] init];
            return computer;
        }
        default:
            break;
    }
    return nil;
}

@end
  1. 调用测试
+ (void)test1 {
    Computer *macComputer = [ComputerSimpleFactory createComputerWithType:1];
    NSLog(@"computer name = %@", macComputer.productName);
    Computer *xmComputer = [ComputerSimpleFactory createComputerWithType:2];
    NSLog(@"computer name = %@", xmComputer.productName);
}
2.1.3 分析
  • 可以看出通过工厂类对产品创建进行了封装,使调用者不需要知道创建的具体逻辑,只需要调用工厂方法,即可得到相应的实例。一旦产品创建逻辑需要修改,也只需要修改工厂类即可,不必全局修改创建逻辑。
  • 系统扩展困难,一旦添加新产品就不得不修改工厂类创建方法逻辑,破坏了“开闭原则”。在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
2.1.4 适用范围
  • 适用于一些创建实例逻辑较为复杂,创建类型固定且被创建的类一般属于同一父类的情况。

2.2 工厂方法模式

工厂方法模式,又称工厂模式、多态工厂模式和虚拟构造器模式。

Define an interface for creating an object,but let subclasses decide which class toinstantiate.Factory Method lets a class defer instantiation to subclasses.

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
这满足创建型模式中所要求的“创建与使用相分离”的特点。同时改进了简单工厂模式中不易拓展的问题。

2.2.1 主要角色与关系

主要角色组成:

  • 抽象工厂(AbstractFactory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(AbstractProduct):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。


    图片.png
2.2.2 代码实现

我们依然以生产电脑为例,为了保持生产质量,一个工厂我们只让他负责生产一种电脑,苹果电脑生产就交给苹果电脑工厂,小米电脑生产就交给小米电脑工厂。

  1. 创建和定义抽象产品Computer和具体产品Mac、XiaoMi。这里产品类和前面一致,不再重复阐述。
  2. 定义工厂抽象类ComputerMethodFactory

// ComputerMethodFactory.h

#import <Foundation/Foundation.h>
#import "Computer.h"

@interface ComputerMethodFactory : NSObject
+ (Computer *)createComputer;
@end


// ComputerMethodFactory.m

#import "ComputerMethodFactory.h"

@implementation ComputerMethodFactory

+ (Computer *)createComputer {
    NSAssert(false, @"must implement ins subClass");
    return nil;
}

@end
  1. 定义具体工厂类
    定义苹果电脑工厂类 MacComputerFactory
    定义小米电脑工厂类 XiaoMiComputerFactory
// MacComputerFactory.h

#import "ComputerMethodFactory.h"

@interface MacComputerFactory : ComputerMethodFactory

@end


// MacComputerFactory.h

#import "MacComputerFactory.h"
#import "Mac.h"

@implementation MacComputerFactory

+ (Computer *)createComputer {
    //do something for create
    Mac *macComputer = [[Mac alloc] init];
    return macComputer;
}

@end
// XiaoMiComputerFactory.h

#import "ComputerMethodFactory.h"

@interface XiaoMiComputerFactory : ComputerMethodFactory

@end


// XiaoMiComputerFactory.m

#import "XiaoMiComputerFactory.h"
#import "XiaoMi.h"

@implementation XiaoMiComputerFactory

+ (Computer *)createComputer {
    //do something for create
    XiaoMi *xmComputer = [[XiaoMi alloc] init];
    return xmComputer;
}

@end
  1. 接下来进行简单的调用测试
//工厂方法模式测试
+ (void)test2 {
    Computer *macComputer = [MacComputerFactory createComputer];
    NSLog(@"computer name = %@", macComputer.productName);
    Computer *xmComputer = [XiaoMiComputerFactory createComputer];
    NSLog(@"computer name = %@", xmComputer.productName);
}
2.2.3 分析
  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
  • 高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则.
  • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度
2.2.4 适用范围
  • 工厂方法模式在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。
  • 需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式,如:产品种类未来仍然可能保持新增的情况

2.3 抽象工厂模式

抽象工厂模式

Provide an interface for creating families of related or dependent objects withoutspecifying their concrete classes

为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品,可以生产一个产品族的产品。

2.3.1 主要角色与关系

主要由以下角色组成:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。


    图片.png
2.3.2 代码实现

我们以前面工厂方法的例子的基础上进行举例说明。现在我们希望工厂在原来生产电脑的基础上,同时支持生产手机。这个时候我们就需要首先先取得生产许可(在抽象工厂类上赋予生产电脑和手机的能力),然后在具体工厂类进行生产,苹果工厂类负责生产苹果手机和苹果电脑,小米工厂则负责生产小米手机和小米电脑。

  1. 创建和定义抽象产品Computer和具体产品Mac、XiaoMi。这里产品类和前面一致,不再重复阐述。
  2. 创建和定义新的抽象产品Phone和具体产品类iPhone 和XiaoMiPhone

// Phone.h
#import <Foundation/Foundation.h>

@interface Phone : NSObject

- (NSString *)productName;

@end


// phone.m
#import "Phone.h"

@implementation Phone

- (NSString *)productName {
    NSAssert(false, @"must implement in subClass");
    return nil;
}
@end

// iPhone.m
#import "Phone.h"

@interface iPhone : Phone

@end


// iPhone.m
#import "iPhone.h"

@implementation iPhone

- (NSString *)productName {
    return @"iphone";
}

@end
// XiaoMiPhone.h
#import "Phone.h"

@interface XiaoMiPhone : Phone

@end


// XiaoMiPhone.m
#import "XiaoMiPhone.h"

@implementation XiaoMiPhone
- (NSString *)productName {
    return @"XiaoMiPhone";
}
@end
  1. 创建和定义抽象工厂类 ElectronAbstractFactory,定义生产手机和电脑功能
// ElectronAbstractFactory
#import <Foundation/Foundation.h>
#import "Phone.h"
#import "Computer.h"

@protocol ElectronAbstractFactoryInterface <NSObject>
@required
+ (Phone *)createPhone;
+ (Computer *)createComputer;
@end

@interface ElectronAbstractFactory : NSObject<ElectronAbstractFactoryInterface>

@end


// ElectronAbstractFactory.m
#import "ElectronAbstractFactory.h"

@implementation ElectronAbstractFactory

+ (Phone *)createPhone {
    NSAssert(false, @"must implement in subClass");
    return nil;
}

+ (Computer *)createComputer {
    NSAssert(false, @"must implement in subClass");
    return nil;
}

@end
  1. 定义具体工厂类AppleElectronFactory和XiaoMiElectronFactory
// AppleElectronFactory.h
#import "ElectronAbstractFactory.h"

@interface AppleElectronFactory : ElectronAbstractFactory

@end


// AppleElectronFactory.m
#import "AppleElectronFactory.h"
#import "iPhone.h"
#import "Mac.h"

@implementation AppleElectronFactory

+ (Computer *)createComputer {
    //do something for create
    Mac *mac = [[Mac alloc] init];
    return mac;
}

+ (Phone *)createPhone {
     //do something for create
    iPhone *iphone = [[iPhone alloc] init];
    return iphone;
}

@end
// XiaoMiElectronFactory.h
#import "ElectronAbstractFactory.h"

@interface XiaoMiElectronFactory : ElectronAbstractFactory

@end


// XiaoMiElectronFactory.m
#import "XiaoMiElectronFactory.h"
#import "XiaoMiPhone.h"
#import "XiaoMi.h"

@implementation XiaoMiElectronFactory

+ (Computer *)createComputer {
    //do something for create
    XiaoMi *mac = [[XiaoMi alloc] init];
    return mac;
}

+ (Phone *)createPhone {
     //do something for create
    XiaoMiPhone *iphone = [[XiaoMiPhone alloc] init];
    return iphone;
}

@end
  1. 进行调用测试对应代码
+ (void)test3 {
    Computer *macComputer = [AppleElectronFactory createComputer];
    NSLog(@"computer name = %@", macComputer.productName);
    Phone *iphone = [AppleElectronFactory createPhone];
    NSLog(@"computer name = %@", [iphone productName]);
    Computer *xmComputer = [XiaoMiComputerFactory createComputer];
    NSLog(@"computer name = %@", xmComputer.productName);
    Phone *xiaomiPhone = [XiaoMiElectronFactory createPhone];
    NSLog(@"computer name = %@", [xiaomiPhone productName]);
}
2.3.3 分析
  • 抽象工厂模式除了具有工厂方法模式的优点外,可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理
  • 当增加一个新的产品族时不需要修改原代码,满足开闭原则
  • 但是当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改又与开闭原则相悖
2.3.4 适用范围
  • 当要被创建的对象是一系列具有相互关联、相互依赖的产品类时
  • 系统中有多个产品族,但是每次只会适用其中一个产品族的产品
  • 涉及不同运行环境的时候,都可以考虑使用抽象工厂模式。例如一个应用,需要在三个不同平台(Windows、Linux、Android 通过抽象工厂模式屏蔽掉操作系统对应用的影响。三个不同操作系统上的软件功能、应用逻辑、UI都应该是非常类似的,唯一不同的是调用不同的工厂方法,由不同的产品类去处理与操作系统交互的信息
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345