iOS设计模式之抽象工厂

632 阅读4分钟

定义

抽象工厂:提供了一个创建一系列相关或者相互依赖对象的接口,而无需指定他们具体的类。

抽象工厂与工厂方法的区别

抽象工厂 工厂方法
通过对象组合创建抽象产品 通过类继承创建抽象产品
创建多系列产品 创建一种产品
必须修改父类的接口才能支持新的产品 子类化创建者重载工厂方法来创建新产品

两个方法的相同目的为:创建对象而不让客户端知晓返回了什么确切的具体对象。

使用场景

使用抽象工厂模式的场景如下:

  • 需要创建一系列有关联的对象

搭配其他模式

  • 原型模式
  • 单例模式
  • 享元模式

类图结构

抽象工厂方法类图结构如下:

客户端client只知道AbstractFactory和AbstractProduct。不清楚如何创建具体的产品ProductA1,ProductA2,ProductB1,ProductB2。客户端client通过创建不同的AbstractFactory的实例,来生成不同的一套产品。

AbstractFactory将创建Product的实现细节隐藏在具体子类ConcreateFactory中。这种方案下,将来需要新增类型时,通过添加ConcreateFactory和Product即可实现。

代码实现

实现如下需求,在项目中,针对视图View,按钮button,以及功能区toorbar,进行Acme与Seirra两个品牌定制化显示,后续可能会增加其他品牌。 我们先实现抽象父类BrandFactory如下:

@interface BrandFactory : NSObject

+ (BrandFactory *)factory;

- (UIView *)brandView;
- (UIButton *)brandMainButton;
- (UIToolbar *)brandToorbar;

@end

@implementation BrandFactory

+ (BrandFactory *)factory{
#ifdef  USER_ACME
    return [AcmeBrandingFactory new];
#elifdef USER_SIERRA
    return [SierraBrandingFactory new];
#else
    return nil;
#endif
}

- (UIView *)brandView{return nil;}
- (UIButton *)brandMainButton{return nil;}
- (UIToolbar *)brandToorbar{return nil;}

@end

factory类方法中,根据项目的宏定义不同,返回不同的品牌子类。无需设计在运行时决定使用什么工厂的复杂机制。通过简单的预处理定义,完全在编译时决定,过程中不涉及其他类,否则,增加其他类会增加应用程序的复杂度。

定义各类品牌子视图的创建方法,在类实现中默认返回nil,由子类AcmeBrandingFactory与SierraBrandingFactory重载这些方法,返回品牌化特征的具体产品。

@interface AcmeBrandingFactory : BrandFactory

- (UIView *)brandView;
- (UIButton *)brandMainButton;
- (UIToolbar *)brandToorbar;

@end

@implementation AcmeBrandingFactory

- (UIView *)brandView{
    return [AcmeView new];
    
}

- (UIButton *)brandMainButton{
    return [AcmeButton new];
    
}

- (UIToolbar *)brandToorbar{
    return [AcmeToorbar new];
    
}

@end

@interface SierraBrandingFactory : BrandFactory

- (UIView *)brandView;
- (UIButton *)brandMainButton;
- (UIToolbar *)brandToorbar;

@end

@implementation SierraBrandingFactory

- (UIView *)brandView{
    return [SierraView new];
    
}
- (UIButton *)brandMainButton{
    return [SierraButton new];
    
}
- (UIToolbar *)brandToorbar{
    return [SierraToorbar new];
    
}

@end

AcmeBrandingFactory与SierraBrandingFactory都继承自相同的父类BrandFactory,并且重载实现brandView,brandMainButton,brandToorbar这些具体产品的创建方法,返回不同品牌特征的对应视图

在子类工厂的声明中,也重复声明了重载的父类方法,这是个好习惯,可以很明确的告诉外界,自己重载了父类的那些方法,明确定义了自己的具体实现。

当需要支持额外的一个品牌时,只需增加一个子类工厂与具体的产品实现即可。

下面我们来看一下如何调用该工厂方法创建具体的子类

- (void)loadView{
    BrandFactory *factory = [BrandFactory factory];
    [self.view addSubview:[factory brandView]];
    [self.view addSubview:[factory brandMainButton]];
    [self.view addSubview:[factory brandToorbar]];
}

在使用的ViewController的loadView方法中,根据定义不同返回对应的BrandFactory,然后使用工厂类生成不同的产品视图添加到当前视图中。

抽象工厂在Cocoa Touch中的使用

抽象工厂模式常见于Cocoa Touch框架中,例如NSNumber,NSArray,NSDictionary,NSString,以及NSData。这种实现方式我们也称之为类簇

类簇是基础框架中一种常见的设计模式,基于抽象工厂模式的思想。将若干相关的私有具体工厂子类集合到一个工友的抽象超类之下,典型的就是NSNumber。NSNumber本身就是一个高度抽象的工厂,而NSCFBoolean和NSCFNumber是具体工厂子类。根据下面的类工厂方法返回不同的工厂子类

+ (NSNumber *)numberWithChar:(char)value;
+ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
+ (NSNumber *)numberWithShort:(short)value;
+ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
+ (NSNumber *)numberWithInt:(int)value;
+ (NSNumber *)numberWithUnsignedInt:(unsigned int)value;
...

子类都会实现超类中如下的实现以完成统一的方法返回

@property (readonly) int intValue;
@property (readonly) BOOL boolValue;
@property (readonly, copy) NSString *stringValue;

- (NSComparisonResult)compare:(NSNumber *)otherNumber;

- (BOOL)isEqualToNumber:(NSNumber *)number

注意点: 如果有多个工厂类有共同的行为,则需要使用抽象父类,实现相同的行为,如没有相同的行为,则可以使用协议protocol来代替抽象父类,使代码更加解耦。

总结

一系列相关类的好的模式,应该作为一种抽象,不为客户端所见。抽象工厂可以顺畅的提供这种抽象,而不暴露创建过程中任何不必要的细节或者创建对象的确切类型。

软件设计的黄金法则:变动需要抽象