定义
在软件设计中,如果客户想手动创建一个对象,需要知道对象的详细结构,包括其数据结构以及方法调用,如果运气不好,还可能因为该对象引用了其他对象,导致客户端还得了解其他对象,如此..使得该对象的创建变得复杂起来。之前讲过的工厂方法模式可以解决该类问题,不过工厂方法模式一般用来解决单个对象的创建,对于需要创建多个有关联的对象,那么可能就需要使用抽象工厂模式的方法了。此时,客户端只需要知道抽象出来的工厂以及自己所要的对象即可,不必清楚是谁创建以及怎样创建那些具体的对象。
抽象工厂的具体定义为:抽象工厂提供固定的接口,用于创建一系列有关联的对象,而不必指出其创建的细节。
ABSTRACT FACTORY: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.*
* The original definition appeared in Design Patterns, by the “Gang of Four” (Addison-Wesley,1994).
其类似的类图示意图可能如下所示
上述的类图示意图中,如果客户端想创建产品A和B,他只需要知道抽象工厂的固定接口createProductA和createProductB(当然还得知道AbstractFactory、AbstractProductA和AbstractProductB的存在),这样他就能够直接使用这两个接口创建自己所需要的产品A和B了,而不需要知道产品A和B是谁创建的,在哪创建以及其他细节问题。通过抽象工厂的封装,使得有关联的对象的创建变得透明、简单化。
此外,通过抽象工厂模式的编程方法,你如果后续想添加其他产品,则直接按照定好的抽象工厂接口实现即可,而不需要改变客户端的代码调用。
在这里还得补充一点关于工厂方法和抽象工厂的关系问题,上图中,抽象工厂是有包含工厂方法的设计模式的,其中createProductA和createProductB都属于工厂方法,更多的比较可见下表:
工厂方法和抽象工厂 抽象工厂 工厂方法 通过对象组合创建产品 通过类继承创建产品 创建一类有关联的产品,包括多种产品 创建一种产品 如果新增产品类型,必须修改抽象类的固定接口 子类化创建者并重载工厂方法以创建新产品
代码示例
还是用主题来做个例子吧,这个例子是一个界面主题,该主题包括三部分:主视图,按钮,工具栏,不同的主题,这些元素的样式不一样,其他的是一样的。因此由此可抽象出一个抽象工厂类,该类提供的固定接口是三个创建对象的方法,分别用来创建主视图、按钮和工具栏。主要的关系图如下所示:
抽象类的定义如下所示:
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface BrandFactory : NSObject + (BrandFactory*)fatoryWithName:(NSString*)name; - (UIView*) brandView; - (UIButton*) brandMainButton; - (UIToolbar*) brandToolbar; @end
上述代码定义了三个固定的接口,而类方法fatoryWithName则会根据名字选择对应的工厂。其中三个固定接口在抽象工厂类返回值是nil,而其中的类方法其实现如下所示:
+ (BrandFactory*)fatoryWithName:(NSString*)name { BrandFactory* factory = nil; if([name isEqualToString:@"Serra"]) { factory = [[SerraBrandFactory alloc] init]; } else if([name isEqualToString:@"Acme"]) { factory = [[AcmeBrandFactory alloc] init]; } return factory; }
对应于上图的关系图,实现的两个具体工厂类如下所示:
@implementation SerraBrandFactory - (UIView*) brandView { UIView* view = [[UIView alloc] init]; view.backgroundColor = [UIColor redColor]; return view; } - (UIButton*) brandMainButton { UIButton* button = [[UIButton alloc] init]; [button setTitle:@"Serra" forState:UIControlStateNormal]; return button; } - (UIToolbar*) brandToolbar { UIToolbar* toolbar = [[UIToolbar alloc] init]; toolbar.barStyle = UIBarStyleBlack; UIBarButtonItem* item = [[UIBarButtonItem alloc] init]; [item setImage:[UIImage imageNamed:@"cat.png"]]; toolbar.items = [NSArray arrayWithObject:item]; return toolbar; } @end
@implementation AcmeBrandFactory - (UIView*) brandView { UIView* view = [[UIView alloc] init]; view.backgroundColor = [UIColor blueColor]; return view; } - (UIButton*) brandMainButton { UIButton* button = [[UIButton alloc] init]; [button setTitle:@"Acme" forState:UIControlStateNormal]; return button; } - (UIToolbar*) brandToolbar { UIToolbar* toolbar = [[UIToolbar alloc] init]; toolbar.barStyle = UIBarStyleBlack; UIBarButtonItem* item = [[UIBarButtonItem alloc] init]; [item setImage:[UIImage imageNamed:@"dog.png"]]; toolbar.items = [NSArray arrayWithObject:item]; return toolbar; } @end
上两个实现分别返回自身定制的产品对象,而且都是通过基类抽象工厂类的固定接口返回的,这样当客户端需要构建这些对象的时候,直接使用抽象工厂提供的接口即可完成对所需的对象的创建,客户端冻得代码调用如下所示:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 客户端加载视图 BrandFactory* brand = [BrandFactory fatoryWithName:@"Serra"]; //BrandFactory* brand = [BrandFactory fatoryWithName:@"Acme"]; UIView* brandView = [brand brandView]; brandView.frame = self.view.frame; [self.view addSubview:brandView]; UIButton* brandButton = [brand brandMainButton]; brandButton.frame = CGRectMake(60, 100, 200, 100); [self.view insertSubview:brandButton aboveSubview:brandView]; UIToolbar* brandToolbar = [brand brandToolbar]; brandToolbar.frame = CGRectMake(0, brandView.frame.size.height-40, brandView.frame.size.width, 40); [self.view insertSubview:brandToolbar aboveSubview:brandView]; }
使用抽象工厂的设计模式后,通过工厂的封装,客户端不再需要知道产品的具体细节,实现内部的黑盒透明化,同时,如果新增了产品后,客户端的代码调用改动会很小,只需要指定名字即可。如果不适用抽象工厂,那么常规的方法是这里需要自己手动创建每个产品对象,需要了解产品的具体细节,同时如果要修改为其他产品的话,要修改大量代码,可维护性和代码的重用性大大降低。因此,抽象工厂模式是好的,工厂设计模式是好的。
总结
抽象工厂以及其他的工厂模式是一种比较常见以及简单的编程设计模式,他也是最基本的设计模式,因为他涉及到许多类型对象的创建。在创建对象的时候,最好使用设计模式封装对象,使得对象抽象出来,具体细节得到隐藏。