抽象工厂模式是所有工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式的简略类图如下:
抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象。这就是抽象工厂模式的用意。
为了说明抽象工厂模式的用意,不妨将它分成两段来理解。
第一段
一个系统需要消费多个抽象产品角色,这些抽象产品角色可以用java接口或者java抽象类来实现。然后通过工厂类来实现。如下图:
第二段
依照上面的模式设计系统,已经是抽象工厂模式的基本意义了,接下来的问题是,如果每个抽象产品都有多于一个具体的产品子类,工厂角色怎么知道实例化哪一个产品子类呢,如下图:
抽象工厂提供了两个具体工厂角色,分别对应两个具体的产品角色。每一个具体的工厂角色色仅负责某一个具体产品角色的实例化。每一个具体的工厂类负责创建抽象产品的某一个具体子类的实例。
理解以上两个步骤,就不难理解“抽象工厂”。“抽象”来自抽象产品角色,而“抽象工厂”就是抽象产品角色的工厂。
产品族
为了方便引进抽象工厂模式,特地引进一个新的概念:产品族(Product Family)。所谓产品族,是指位于不同产品等级结构中,功能相关联的产品组成的家族。每个产品族含有产品的数目,与产品等级结构的数目是相等的。
抽象工厂模式的结构
抽象工厂模式是对象的创建模式,它是工厂方法模式的进一步推广。
假设一个系统需要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和创建这些产品对象的责任分割开来。可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要想一个公用的工厂接口请求所需的产品。
产品对象的创建问题
通过使用抽象工厂模式,可以处理具有相同(或相似)等级结构的多个产品族中的产品对象的创建问题。如下图:
如果使用工厂方法模式处理的话,就必须要有两个独立的工厂族。由于这两个产品族的等级结构相同,因此使用同一个工厂族也能处理这两个产品族的创建问题。
如果使用抽象工厂模式,这样根据产品觉得的结构图,就不难给出工厂角色的设计图,如下:
由于每个具体工厂角色都需要负责两个不同等级结构的产品对象的创建,因此每个工厂角色都需要提供两个工厂方法,分别用于创建两个等级结构的产品。既然每个具体工厂角色都需要实现这两个工厂方法,所以这种情况就具有一般性,可以抽象出来,放入抽象工厂角色中加以声明。
系统的设计
采用抽象工厂模式设计出的系统类图如下:
从上图可以看出抽象工厂涉及以下几种角色:
- 抽象工厂(AbstractFactory)角色:担任这个角色的是抽象工厂模式的核心,它是与应用系统的商业逻辑无关的,通常使用java接口或者java抽象类来实现。所有具体工厂类必须实现这个java接口或者继承java抽象类。
- 具体工厂类(Concrete Factory)角色:这个角色直接在客户端俄调用下创建产品的实例,这个角色含有选择合适的产品对象的逻辑,这个逻辑与应用系统的商业逻辑是密切相关的。通常用java类来实现。
- 抽象产品(Abstract Product)角色: 担任这个角色的类是抽象工厂模式所创建的对象的父类,或者他们共同拥有的接口,通常使用java抽象类或者接口来实现。
- 具体产品(Concrete Product)角色:抽象工厂模式所创建的任何产品对象都是具体产品绝对的一个实例。这是客户端最终需要的东西,其内部包含了应用系统的商业逻辑,有java类实现。
抽象工厂模式在农场系统中的实现
农场公司面临新的发展,引进朔料大棚技术,在大棚里种植热带和亚热带的水果和蔬菜,因此在这个系统中产品分两个等级结构,类图如下:
这个系统可以分成两个等级结构:Fruit 和 Veggie,已经两个产品族:Tropical和Northern。
系统设计
与抽象工厂模式各个角色想比较,就不难发现,园丁就是工厂角色,水果就是产品角色。系统设计如下图:
源代码
抽象产品: Fruit.java
package com.model.builder.abstractfactory;
public interface Fruit {
void grow();
void harvest();
void plant();
}
NorthernFruit.java
package com.model.builder.abstractfactory;
public class NorthernFruit implements Fruit {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public NorthernFruit(String name){
this.name = name;
}
@Override
public void grow() {
// TODO Auto-generated method stub
System.out.println("NorthernFruit is growing");
}
@Override
public void harvest() {
// TODO Auto-generated method stub
System.out.println("NorthernFruit is harvest");
}
@Override
public void plant() {
// TODO Auto-generated method stub
System.out.println("NorthernFruit is planting");
}
}
TropicalFruit.java
package com.model.builder.abstractfactory;
public class TropicalFruit implements Fruit {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TropicalFruit(String name){
this.name = name;
}
@Override
public void grow() {
// TODO Auto-generated method stub
System.out.println("TropicalFruit is growing");
}
@Override
public void harvest() {
// TODO Auto-generated method stub
System.out.println("TropicalFruit is harvest");
}
@Override
public void plant() {
// TODO Auto-generated method stub
System.out.println("TropicalFruit is planting");
}
}
抽象产品:Veggie.java
package com.model.builder.abstractfactory;
public interface Veggie {
void grow();
void harvest();
}
NorthernVeggie.java
package com.model.builder.abstractfactory;
public class NorthernVeggie implements Veggie {
private String name ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public NorthernVeggie(String name){
this.name =name;
}
@Override
public void grow() {
System.out.println("NorthernVeggie is growing");
}
@Override
public void harvest() {
System.out.println("NorthernVeggie is harvest");
}
}
TropicalVeggie.java
package com.model.builder.abstractfactory;
public class TropicalVeggie implements Veggie {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TropicalVeggie(String name){
this.name = name;
}
@Override
public void grow() {
// TODO Auto-generated method stub
System.out.println("TropicalVeggie is growing");
}
@Override
public void harvest() {
// TODO Auto-generated method stub
System.out.println("TropicalVeggie is harvest");
}
}
抽象工厂:Gardener.java
package com.model.builder.abstractfactory;
public interface Gardener {
public Fruit createFruit(String name);
public Veggie createVeggie(String name);
}
NorthernGardener.java
package com.model.builder.abstractfactory;
public class NorthernGardener implements Gardener {
@Override
public Fruit createFruit(String name) {
return new NorthernFruit(name);
}
@Override
public Veggie createVeggie(String name) {
return new NorthernVeggie(name);
}
}
TropicalGardener.java
package com.model.builder.abstractfactory;
public class TropicalGardener implements Gardener {
@Override
public Fruit createFruit(String name) {
return new TropicalFruit(name) ;
}
@Override
public Veggie createVeggie(String name) {
return new TropicalVeggie(name);
}
}
在什么情况下应该使用抽象工厂模式
以下情况下,可以考试使用抽象工厂模式 :
- 一个系统不应该依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是很重要的。
- 这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
- 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中提醒出来。
- 提醒提供一个产品类的库,所有的产品以同样的接口出现,从而是客户端不依赖与实现。
“开闭”原则要求一个软件系统可以再不修改原有代码的情况下,通过扩展达到新增功能的目的。对于一个涉及多个单品等级结构和多个产品族的系统,其功能增强不外乎两个方面:增加新的产品族和增加新的产品等级结构。
当产品等级结构有所调整时, 由于工厂等级而机构和产品等级结构是平行的登记机构,需要将工厂等级结构调整。现在产品等级结构中出现了新的元素,因此需要向工厂等级结构中加入相应的新元素即可。换而言之,设计师只用新增产品类,不用修改已有的工厂类和抽象工厂角色,因此在增加产品族时,抽象工厂是支持“开闭”原则。
在产品族的数目不变的情况下,增效新的产品等级结构,要做到这一点,就需要修改所有的工厂角色,给每一个工厂类都新增一个工厂方法,这明显违背了“开闭”原则。
综合起来,抽象工厂模式以一种倾斜的方式支持增加新的产品,能为新的产品族的增加提供方便,但是不能为新增的产品等级结构提供方便。