工厂模式三部曲 - 抽象工厂模式

该文章属于<简书 — 刘小壮>原创,转载请注明:

<简书 — 刘小壮> http://www.jianshu.com/p/750b82fa6a62git


这是__工厂模式三部曲__中的最后一篇了,在这篇文章中将会讲述__抽象工厂模式__,抽象工厂模式正如其名字同样,很是抽象。可是抽象工厂模式的功能却十分强大,对抽象工厂的利用也很是好。github

这篇文章中会像本系列第一篇同样,给出普通实现方式和使用了反射机制的实现两种代码,而且会说明这两种实现方式的区别。而且在文章的最后,会将这三种模式放在一块儿,对这三种工厂模式进行总结。数据库

本人理解可能不够深入,这一系列文章中存在的问题,欢迎你们提出,谢谢!设计模式


博客配图

什么是抽象工厂模式

#####简单了解一下 按照惯例,咱们先了解一下什么是抽象工厂模式。抽象工厂模式和工厂方法模式很类似,可是抽象工厂模式将抽象发挥的更加极致,是三种工厂模式中最抽象的一种设计模式。抽象工厂模式,也叫作__Kit__模式,提供了建立一系列相关抽象子类的接口,而无需指定它们具体的类型。缓存

抽象工厂模式中定义了抽象工厂类,抽象工厂类中定义了每一个系列的抽象子类建立所需的方法,这些方法对应着不一样类型的抽象子类实例化过程。每一个工厂子类都对应着一个系列,工厂子类经过重写这些方法来实例化当前系列的抽象子类。架构

工厂方法模式中抽象子类都是基于同一个抽象类的,是同一个类型的抽象子类,例如加、减、乘、除都属于运算类型。而__抽象工厂模式可能会有多个类型的抽象类,抽象子类分别继承自对应类型的抽象类,相同类型的抽象子类都是属于不一样系列的__。框架

抽象工厂模式包含四部分:
  • 抽象工厂类:定义建立抽象子类的具体行为,根据系列中不一样类型的抽象子类可能会有多种行为。
  • 工厂子类:继承自抽象工厂类,根据当前抽象子类对应的系列,重写父类定义的对应行为。对应的抽象子类系列不一样,行为的实现方式也不一样。
  • 抽象类:定义当前类型抽象子类的操做,子类继承父类完成具体的操做。在抽象工厂模式中,可能会有多种抽象类的定义。
  • 抽象子类:根据当前类型继承自对应的抽象类,并根据系列的不一样重写抽象类定义的实现。
我打算先讲一个例子

咱们上面讲了系列的概念,这里将会用一个例子来理解系列和抽象类的关系。假设如今须要用SqliteCoreData两种不一样的方式进行本地持久化,持久化的内容都是用户信息、搜索信息、设置信息三部分。学习

就拿Sqlite持久化方式来讲,Sqlite就是使用Sqlite数据库持久化方式的系列,下面对应着用户信息、搜索信息、设置信息三个类型,每一个类型就是一个抽象类。除了Sqlite这种持久化方式外,还有CoreData这种持久化方式,这是两个不一样的持久化方式,因此属于两个不一样的系列。优化

SqliteCoreData都表明着不一样的系列,其下面都分别对应着用户信息、搜索信息、设置信息三个类型的层级,在这种层级关系中,Sqlite的用户信息抽象子类对应着CoreData的用户信息抽象子类,这两个抽象子类都属于同一个类型,继承自同一个抽象类,分别被不一样系列的工厂子类建立。在抽象设计模式中,不一样系列相同类型的抽象子类都是一一对应的atom

SqliteCoreData属于不一样的系列,因此是两个不一样的工厂子类,这两个工厂子类具备相同的行为,就是用户信息、搜索信息、设置信息三部分的数据持久化,这就是三种不一样的持久化类型,也就是咱们上面说的类型。这三个行为定义在抽象工厂类中,抽象工厂类中定义每一个系列的抽象子类建立方法,SqliteCoreData继承自抽象工厂类,并分别实现继承过来的抽象子类建立方法。

经过上面的例子,咱们能够清晰的理解工厂类、抽象类、系列三者之间的关系,理解这三者的关系能够有助于咱们更好的理解抽象设计模式。

和工厂方法模式有什么不一样?

在工厂方法模式中,工厂子类负责抽象子类的实例化,每一个工厂子类对应着一个抽象子类,且具备惟一性。而在抽象工厂模式中,一个工厂子类表明一个系列,工厂子类根据当前系列对不一样类型的抽象子类进行建立。工厂方法模式中工厂子类对应的是一个类型的抽象子类,抽象工厂模式对应的是一个系列的抽象子类

工厂方法模式一个工厂子类对应一个抽象子类的设计,会有很大的浪费,产生了过多的类。而__抽象工厂模式更好的利用了工厂子类__,使每一个工厂子类对应着一个系列的抽象子类,这种设计很是适用于两个具备相同结构关系,可是分属于不一样系列的系列之间的切换。

总之就是,工厂方法模式是针对单个类型的抽象类,而抽象工厂模式是针对具备相同结构的一系列类型的抽象类


业务场景

在上面讲到了数据持久化的例子,咱们的业务场景也根据上面的例子提出。

iOS中比较经常使用的数据持久化方案,应该就包括SqliteCoreData了,可能Sqlite的灵活性使其更加受欢迎。业务就是须要用SqliteCoreData两种不一样的方式进行本地持久化,持久化的内容是用户信息、搜索信息、设置信息三部分。

经过抽象工厂模式实现上面的需求,能够很方便的进行本地持久化方案的切换,下面的例子中将会演示一行代码切换数据持久化方案的例子。

UML类图

咱们根据上面的业务场景画了一个__UML__类图,下面类图中为了让你们看得更清晰,因此__用不一样颜色的线区分开了对应的类和功能__。

下面的黑色箭头是抽象子类和抽象类的继承关系;红色是用户工厂子类对应的抽象子类;黄色是搜索工厂子类对应的抽象子类;绿色是设置工厂子类对应的抽象子类。

抽象工厂模式

在这个__UML__类图中,咱们能够清晰的看出,以前工厂方法模式的工厂子类对应的是单一类型的抽象子类,上面__抽象工厂模式的工厂子类对应的是同一系列多个类型的抽象子类__,更好的利用了工厂子类,适合更加复杂的业务需求。抽象工厂类的方法定义也和工厂方法模式不太同样,因为工厂方法模式只建立一个抽象子类,因此直接用的类方法定义,抽象方法模式可能会建立多个类型的抽象子类,因此用的实例方法定义。

普通方式代码实现

这里代码实现按照上面举的例子,代码结构也彻底按照上面__UML__类图中画的结构,使整篇文章能够更加统一,更深入的理解这个设计模式。

代码量比较多,可是为了更好的体现出抽象工厂模式,因此就全贴出来了。

首先建立两个Model类,这两个Model类并不属于抽象工厂模式结构的一部分,只是为了更好的体现出面向模型开发。
[@interface](https://my.oschina.net/u/996807) User : NSObject
[@property](https://my.oschina.net/property) (nonatomic, copy  ) NSString  *userName;
[@property](https://my.oschina.net/property) (nonatomic, assign) NSInteger userAge;
[@end](https://my.oschina.net/u/567204)
@implementation User
@end

@interface City : NSObject
@property (nonatomic, copy) NSString *cityName;
@property (nonatomic, copy) NSString *cityCode;
@end
@implementation City
@end
用户信息系列相关类
@interface UserInfo : NSObject
- (void)setUserName:(User *)name;
@end
@implementation UserInfo
- (void)setUserName:(User *)name {}
@end

@interface SqliteUserInfo : UserInfo
@end
@implementation SqliteUserInfo
- (void)setUserName:(User *)name {
    NSLog(@"这里编写数据库持久化方案");
}
@end

@interface CoreDataUserInfo : UserInfo
@end
@implementation CoreDataUserInfo
- (void)setUserName:(User *)name {
    NSLog(@"这里编写CoreData持久化方案");
}
@end
搜索信息系列相关类
@interface SearchInfo : NSObject
- (void)setSearchCity:(City *)city;
@end
@implementation SearchInfo
- (void)setSearchCity:(City *)city {}
@end

@interface SqliteSearchInfo : SearchInfo
@end
@implementation SqliteSearchInfo
- (void)setSearchCity:(City *)city {
    NSLog(@"这里编写数据库持久化方案");
}
@end

@interface CoreDataSearchInfo : SearchInfo
@end
@implementation CoreDataSearchInfo
- (void)setSearchCity:(City *)city {
    NSLog(@"这里编写CoreData持久化方案");
}
@end
设置信息系列相关类
@interface SettingsInfo : NSObject
- (void)resetAllSettings;
@end
@implementation SettingsInfo
- (void)resetAllSettings {}
@end

@interface SqliteSettingsInfo : SettingsInfo
@end
@implementation SqliteSettingsInfo
- (void)resetAllSettings {
    NSLog(@"重置数据库设置信息");
}
@end

@interface CoreDataSettingsInfo : SettingsInfo
@end
@implementation CoreDataSettingsInfo
- (void)resetAllSettings {
    NSLog(@"重置CoreData设置信息");
}
@end
工厂抽象相关类
@interface Factory : NSObject
- (UserInfo *)CreateUserInfo;
- (SearchInfo *)CreateSearchInfo;
- (SettingsInfo *)CreateSettingsInfo;
@end
@implementation Factory
- (UserInfo *)CreateUserInfo {
    return nil;
}
- (SearchInfo *)CreateSearchInfo {
    return nil;
}
- (SettingsInfo *)CreateSettingsInfo {
    return nil;
}
@end

@interface SqliteFactory : Factory
@end
@implementation SqliteFactory
- (UserInfo *)CreateUserInfo {
    return [SqliteUserInfo new];
}
- (SearchInfo *)CreateSearchInfo {
    return [SqliteSearchInfo new];
}
- (SettingsInfo *)CreateSettingsInfo {
    return [SqliteSettingsInfo new];
}
@end

@interface CoreDataFactory : Factory
@end
@implementation CoreDataFactory
- (UserInfo *)CreateUserInfo {
    return [CoreDataUserInfo new];
}
- (SearchInfo *)CreateSearchInfo {
    return [CoreDataSearchInfo new];
}
- (SettingsInfo *)CreateSettingsInfo {
    return [CoreDataSettingsInfo new];
}
@end
外界使用
- (void)viewDidLoad {
    User *user = [User new];
    City *city = [City new];

    Factory *factory = [SqliteFactory new];
    UserInfo *userInfo = [factory CreateUserInfo];
    SearchInfo *searchInfo = [factory CreateSearchInfo];
    SettingsInfo *settingsInfo = [factory CreateSettingsInfo];

    [userInfo setUserName:user];
    [searchInfo setSearchCity:city];
    [settingsInfo resetAllSettings];
}

到此为止咱们就编写完抽象工厂设计模式的代码了,上面抽象工厂模式的示例中定义了三个类型的抽象类UserInfoSearchInfoSettingsInfo,抽象子类分别继承不一样类型的抽象类,并实现不一样系列的持久化代码。这三个类型的抽象类中定义了两种不一样的数据持久化方案,分别是Sqlite存储和CoreData存储,这就是两种数据持久化系列,分别用SqliteFactoryCoreDataFactory表示这两个系列。

代码中定义了抽象工厂类Factory类,并定义了三个抽象接口用来实例化不一样类型的抽象子类,两个工厂子类SqliteFactoryCoreDataFactory继承自抽象工厂类,内部分别实现了两种不一样系列的抽象子类实例化,例如SqliteFactory中会实例化关于Sqlite存储方式的抽象子类,并经过抽象工厂类中定义的抽象接口返回给外界使用。

统一控制工厂子类的切换

这三种工厂设计模式中除了简单工厂模式,工厂方法模式和抽象工厂模式都须要外界实例化不一样的工厂子类,这种在外界实例化工厂子类的代码可能会出如今多个地方,而在不少业务需求中都须要咱们统一切换某个功能,从代码上来讲就是切换工厂子类,怎样能够作到统一切换工厂子类的需求呢?

就拿上面的持久化方案的例子来讲,咱们定义了两种持久化方案,经过SqliteFactoryCoreDataFactory工厂子类来建立不一样的持久化方案。假设如今咱们项目比较庞大,代码量比较多,而且在多个地方用到了SqliteFactory工厂子类,如今需求是切换为CoreDataFactory的持久化方案,咱们应该怎样快速的切换持久化方案?

其实咱们能够经过typedef定义别名的方式切换工厂子类,在其余地方只须要使用咱们typedef定义的别名就能够,例以下面代码就能够作到修改一处typedef定义,就修改了整个项目的持久化方案。

仍是按照上面的抽象工厂模式的代码,这里只写出外界使用的代码部分
typedef SqliteFactory SaveFactory; //定义的工厂子类别名

@implementation TestTableViewController
- (void)viewDidLoad {
    User *user = [User new];
    City *city = [City new];

    Factory *factory = [SaveFactory new];
    UserInfo *userInfo = [factory CreateUserInfo];
    SearchInfo *searchInfo = [factory CreateSearchInfo];
    SettingsInfo *settingsInfo = [factory CreateSettingsInfo];

    [userInfo setUserName:user];
    [searchInfo setSearchCity:city];
    [settingsInfo resetAllSettings];
}

从上面的代码能够看到,咱们定义了一个SaveFactory的工厂子类别名,下面直接经过这个别名进行的工厂子类的实例化。由于若是存储相同的内容,项目中只会出现一种持久化方案,因此咱们只须要修改typedef的定义,就能够__切换整个项目的持久化方案__。

配合反射机制优化代码

对于抽象工厂模式的反射机制,实现方式和以前的简单工厂模式不太同样,我__采用的是预编译指令加常量字符串类名反射的方式实现的__。别的很少说,先上代码来看看,我这里贴出了主要代码,其余同样的地方我就不往外贴了,不浪费你们时间。

仍是按照上面的业务场景,我定义了两种同名不一样值的字符串常量,这些__常量字符串对应的就是抽象子类的类名__,一个条件分支就是一个系列的抽象子类,经过预编译指令#if来进行不一样系列的抽象子类间的__统一切换__,定义了SAVE_DATA_MODE_SQLITE宏定义来控制系列的切换。这种定义方式能够更方便的进行不一样系列间的切换,从使用上来看很是像咱们用预编译指令替换了以前的工厂子类,实际上从代码的角度上来讲这种方式对系列间的切换更加统一和方便。

#define SAVE_DATA_MODE_SQLITE 1

#if SAVE_DATA_MODE_SQLITE
static NSString * const kUserInfoClass     = @"SqliteUserInfo";
static NSString * const kSearchInfoClass   = @"SqliteSearchInfo";
static NSString * const kSettingsInfoClass = @"SqliteSettingsInfo";
#else
static NSString * const kUserInfoClass     = @"CoreDataUserInfo";
static NSString * const kSearchInfoClass   = @"CoreDataSearchInfo";
static NSString * const kSettingsInfoClass = @"CoreDataSettingsInfo";
#endif

下面是工厂类的定义,使用反射机制的抽象工厂模式刨除了工厂子类,只用一个工厂类来进行操做子类的实例化,这种方式和以前的简单工厂模式很是类似。不一样的是以前的简单工厂模式只须要初始化一个类型的抽象子类,而__抽象工厂模式须要初始化多个类型的抽象子类__。

因为咱们采用了反射机制,而且__由预编译指令进行系列间的切换__,因此这里就直接使用类方法了,哪里用就直接实例化抽象子类便可,不存在工厂子类之间的选择问题了。

@interface Factory : NSObject
+ (UserInfo *)CreateUserInfo;
+ (SearchInfo *)CreateSearchInfo;
+ (SettingsInfo *)CreateSettingsInfo;
@end

@implementation Factory
+ (UserInfo *)CreateUserInfo {
    return [[NSClassFromString(kUserInfoClass) alloc] init];
}
+ (SearchInfo *)CreateSearchInfo {
    return [[NSClassFromString(kSearchInfoClass) alloc] init];
}
+ (SettingsInfo *)CreateSettingsInfo {
    return [[NSClassFromString(kSettingsInfoClass) alloc] init];
}
@end

经过这种方式进行不一样系列的切换,只须要修改一个宏定义的值便可,也就是SAVE_DATA_MODE_SQLITE后面的__1__切换为__0__的步骤,这种方式是__符合咱们开放封闭原则的__。之后每一个系列增长新的类型后,只须要将新增长的两个类名对应添加在预编译指令中,在工厂方法中扩展一个实例化新类型的方法便可。这种方式对扩展是开放的,对修改是关闭的

对于上面的示例代码的编写须要注意一下,按照苹果的命名规范,常量的做用域若是只在一个类中,前面就用小写__k__修饰,若是修饰的常量在其余类中用到,也就是.h文件中用extern修饰的常量,不须要用小写__k__修饰。咱们在苹果的不少官方代码和Kit中也能够看到相同的定义,宏定义也是相同的规则。(extern修饰的常量在.m中不要用static修饰)

项目中若是用到任何预编译指令,在修改从新运行前,必定要clear一下,清除缓存,不然会由于缓存致使__bug__。

抽象工厂模式的优缺点

优势

抽象工厂模式正如其名字同样,理解起来很是抽象,正是由于这种抽象,使得抽象工厂模式很是强大和灵活,比其余两种工厂设计模式要强大不少。抽象工厂模式能够建立多个系列,而且每一个系列抽象子类一一对应,这种强大的功能是其余两种工厂模式都不能实现的。

经过抽象工厂模式统一控制多个系列的抽象子类,能够用多个系列的抽象子类完成一些复杂的需求。例如咱们文中说到的本地持久化方案的切换,最后经过咱们的不断优化,作到__只须要修改一个预编译指令的参数便可切换整个数据持久化方案__,这是其余工厂模式所不能完成的。

抽象工厂模式延续了工厂模式的优势,外界接触不到任何类型的抽象子类,而只须要知道不一样类型的抽象类便可,抽象子类的建立过程都在工厂子类中。这种设计方式充分的利用了面向对象语言中的多态特性,使灵活性大大提高。并且__抽象工厂模式是很是符合开放封闭原则的,对扩展的开放以及对修改的封闭都完美支持__。

缺点

抽象工厂模式带来的缺点也是显而易见的,最明显的缺点就是模式比较庞大,因此须要在适合的业务场景使用这种模式,不然会拔苗助长。


工厂模式三部曲总结

示例

到目前为止,工厂模式三部曲中的三种工厂模式都已经讲完了,在这里咱们将__简单回顾和总结一下这三种设计模式__。首先,我将根据三种工厂模式画三张一样需求的__UML__类图,看完这三张类图你们就对三种工厂模式清晰明了了。

需求就以如今比较火的乐视系列的乐视TV、乐视手机,小米系列的小米TV、小米手机来做为需求,这三张图主要体现工厂模式的总体架构。

简单工厂模式

工厂方法模式

抽象工厂模式

从这三张图中能够看出,简单工厂模式和工厂方法模式对应的是同一类型的操做结构,在当前例子中就是手机类型,由于只有一个类型,因此__尚未系列的概念__。在需求不太复杂,而且不须要多个系列间的切换时,能够考虑使用这两种设计模式。

以前的业务只有手机一种类型,在业务复杂以后出现了一个新类型的产品-电视,这时候工厂类就须要增长一种类型。因为需求更加复杂,这时候就__出现了系列的概念(以前工厂方法模式中类型单一,因此不须要系列的概念)__,乐视系列和小米系列,工厂子类变成了每一个工厂子类对应一个系列的设计,每一个系列中对应不一样类型的产品。

抽象工厂模式对应多个类型的操做结构,分属于不一样的系列。这种结构比较适合复杂的业务需求,例如文中将的Sqlite数据库和CoreData两种存储方式的切换,经过抽象工厂模式就很是好实现。

#####工厂模式总结 在这三种设计模式中都有一个共同的特色,就是继承自抽象类的抽象子类或工厂子类,都必须对抽象类定义的方法给出对应的实现(能够相同,也能够不一样),这种模式才叫作工厂模式。工厂模式的核心就是抽象和多态,抽象子类继承自抽象类,对抽象类中定义的方法和属性给出不一样的实现方式,经过多态的方式进行方法实现和调用,构成了工厂模式的核心。

在工厂类中对开放封闭原则有着完美的体现,对扩展的开放以及对修改的封闭。例如最抽象的抽象工厂模式,抽象工厂模式中增长新的系列,直接扩展一个工厂子类及对应的抽象子类,对整个模式框架不会带来其余影响。若是增长一个新的类型,建立新的类型对应的类,并对整个抽象工厂类及其子类进行方法扩展。

在外界使用抽象子类的功能时,不须要知道任何关于抽象子类的特征,抽象子类也不会出如今外界,__外界只须要和抽象类打交道__就能够。工厂模式将抽象子类的建立和实现分离,具体的建立操做由工厂类来进行,抽象子类只须要关注业务便可,外界不须要知道抽象子类实例化的过程。这种方式很是灵活并易于扩展,并且在大型项目中尤其明显,能够很好的避免代码量过多的问题。

对于这三种工厂模式的选择,我建议若是是像Sqlite数据库和CoreData切换,这样__业务中存在多个系列的需求__,使用抽象工厂模式。若是比较简单的建立单个类型的抽象子类,这种方式建议用简单工厂模式或工厂方法模式。三种设计模式的选择仍是要看需求和项目复杂度,用得好的话能够给代码带来极大的灵活性和扩展性

总结

设计模式主要是一种思想方面的东西,没有任何一种设计模式是万能的,并适应于各类业务场景的设计模式。因此在不一样的地方使用对应的设计模式,或者说根据业务须要设计一种适合当前业务场景的设计模式,这才是最理想的设计模式用法。


前段时间写了关于工厂模式的系列文章,这系列文章理解起来比较难懂。应广大读者的须要,这段时间专门给这系列文章补了Demo

Demo只是来辅助读者更好的理解文章中的内容,应该博客结合Demo一块儿学习,只看Demo仍是不能理解更深层的原理Demo中代码都会有注释,各位能够打断点跟着Demo执行流程走一遍,看看各个阶段变量的值。

Demo地址刘小壮的Github

相关文章
相关标签/搜索