8. AbstractFactory 模式
设计模式 第八章 AbstractFactory 模式
- 8.1 所谓AbstractFactory模式是什么
- 8.2 例子
- 8.3 AbstractFactory 模式总结
8.1 所谓AbstractFactory模式是什么?
这一章节我们学习 AbstractFactory 模式。AbstractFactory 直译成日文是「抽象工厂」。所谓抽象工厂究竟是怎样的工厂呢?AbstractFactory 模式,提供专门实现实例的生成的类,达到必须保持一致性的一系列对象准确无误地生成这个目的。
我们拿制造「车」的代码举个例子。其中有一部分代码是向「车」对象的变量 car 中添加轮胎和方向盘。
car.addTire(new CarTire());
car.addHandle(new CarHandle());
上面的这种写法,在大多数时候没有问题。可是,也有可能发生由于程序员的不经意的失误造成意想不到的错误这种情况。比如原本要制造汽车的,却把自行车的轮胎给作为参数传递过去了。如下所示
car.addTire(new BicycleTire());
car.addHandle(new CarHandle());
在实际的汽车制造过程中,我们不会担心这样的错误,但是在程序编写的过程中我们往往会像上面那样使用了不该使用的对象。这个时候,我们预先定义一个能单独负责制造「车」所需的对象的工厂类。当需要生成轮胎、方向盘等对象的时候、我们使用这个工厂类来生成,这样就能避免上面的错误。同时、这个工厂类修改的时候、使用它的相关对象也能一股脑儿被修改。
8.2 例子
我们拿「锅式料理」的制造作为例子。锅式料理有很多种类,例如鸡肉汆锅、鸡素烧汆锅、泡菜汆锅、肉圆汆锅、鲑鱼汆锅。大体由以下的部分组成。
- 汤
- 主要成份(蛋白质)
- 蔬菜
- 其他成份
主要成份一般是指肉和鱼等含蛋白质的材料。我们把表示锅式料理的HotPot类定义成下面的样子。
public class HotPot{
private Pot pot;
private Soup soup;
private Protein protein;
private List vegetables;
private List otherIngredients;
public HotPot(Pot pot){
this.pot = pot;
}
public void addSoup(Soup sopu){
this.soup = soup;
}
public void addMain(Protein protein){
this.protein = protein;
}
public void addVegetables(List vegetables){
this.vegetables = vegetables;
}
public void addOtherIngredients(List otherIngredients){
this.otherIngredients = ingredients;
}
}
使用的「锅」,我们用构造函数的参数来指定。「汤」,我们通过指定「AddSoup」方法的参数「Soup」来添加。在这里,我们没有定义「Soup」类,但可以把它看成是「汤」类的父类。接下来,addMain 方法,它使用 Protein 对象作为参数。Protein 类这里也没有定义,但是可以把它看成是鸡肉、牛肉、豆腐等类的父类。蔬菜,我们通过指定addVegitables 方法的List类型的参数来添加。「其他成份」我们通过指定addOtherIngredients 方法的List类型的参数来添加。
首先我们来制作鸡肉汆锅这道料理。代码如下
public class SampleClass{
public static void main(String args[]){
HotPot hotPot = new HotPot();
hotPot.addSoup(new ChickenBonesSoup()); // 鶏がらを煮込んだスープ
hotPot.addMain(new Chicken()); // Main として鶏肉
List vegetables[] = new ArrayList();
vegetables.add(new ChineseCabbage()); // 白菜
vegetables.add(new Leek()); // ねぎ
vegetables.add(new Chrysanthemum()); // 春菊
hotPot.addVegetables(vegetables);
List otherIngredients = new ArrayList();
otherIngredients.add(new Tofu()); // 豆腐
hotPot.addOtherIngredients(otherIngredients);
}
}
这样鸡肉汆锅料理就完成了。但是,依照编程人员的不同,使用这种编程手法会导致制作成不同的鸡肉汆锅料理(向蔬菜List添加蔬菜时编程人员A添加了卷心菜,B可能添加大白菜)。基于某种理由,有些时候我们可能需要防止这种情况发生。在这种情况下,我们先准备好了专门生成鸡肉汆锅对象的类,当我们想制作鸡肉汆锅的时候用这个类生成实体对象。我们把这个类叫做MizutakiFactory类。MizutakiFactory类的代码如下
public class MizutakiFactory{
public Soup getSoup(){
return new ChickenBonesSoup();
}
public Protein getMain(){
return new Chicken();
}
public List getVegetables(){
List vegetables = new ArrayList();
vegetables.add(new ChineseCabbage());
vegetables.add(new Leek());
vegetables.add(new Chrysanthemum);
return vegetables;
}
public List getOtherIngredients(){
List otherIngredients = new ArrayList();
otherIngredients.add(new Tofu());
return otherIngredients;
}
}
为了和这个类配合,我们把SampleClass类修改成如下的样子。
public class SampleClass{
public static void main(String args[]){
HotPot hotPot = new HotPot();
Factory factory = new MizutakiFactory();
hotPot.addSoup(factory.getSoup());
hotPot.addMain(factory.getMain());
hotPot.addVegetables(factory.getVegetables());
hotPot.addOtherIngredients(factory.getOthreIngredients());
}
}
当我们要制作鸡肉汆锅料理的时候,必须使用MizutakiFactory类,这样不管是什么人总归生成的是同一种鸡肉汆锅。也就是说,变为一种编程人员不易犯错的结构了。让我们来看看这种结构的类图吧。
现在虽然不是AbstractFactory模式,但是却能达到「使被利用对象保持一致」这样的要求。
由于要制作和MizutakiFactory一样的SukiyakiFactory、KimuchiFactory等,我们作成了这些类的父类Factory类。Factory类定义了以下getSoup、getMain、getVegetables、getOtherIngredients四个抽象方法。
public abstract class Factory{
public abstract Soup getSoup();
public abstract Protein getMain();
public abstract List getVegetables();
public abstract List getOtherIngredients();
}
SampleClass类的main方法中,根据赋值给参数的字符的不同,有选择性地创建实际使用的Factory类。
public class SampleClass{
public static void main(String args[]){
HotPot hotPot = new HotPot();
Factory factory = createFactory(args[0]);
hotPot.addSoup(factory.getSoup());
hotPot.addMain(factory.getMain());
hotPot.addVegetables(factory.getVegetables());
hotPot.addOtherIngredients(factory.getOthreIngredients());
}
private Factory createFactory(String str){
if("泡菜汆锅".equals(str)){
return new KimuchiFactory();
}else if("鸡素烧汆锅".equals(str){
return new SukiyakiFactory();
}else{
return new MizutakiFactory();
}
}
}
main方法中,我们虽然不知道Factory实例的具体类型,但下面的处理仍然可以进行。也就是说,我们通过使用抽象的Factory类使得处理能够继续进行。像这样,就能满足「使被利用的对象群体能够自由替换」要求。下面就是这种情况的类图。
8.3 AbstractFactory 模式总结
例子SampleClass的main方法,在没有明确究竟使用了Factory的那一个子类的情况下,我们却得到了能够被HotPot使用的对象。例如addSoup时的factory.getSoup()。
AbstractFactory模式一般的类图如下所示。
[引用] 『Java言語で学ぶ デザインパターン入門』(結城浩 ソフトバンクパブリッシング株式会社出版 2001年)