动机
在了解动机之前,先回顾一下工厂方法模式:
在工厂方法模式中,工厂类都是对单一产品对象进行操作,每一个具体工厂对应一种具体产品,工厂方法具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。
问题:如果想要一个工厂对多个产品对象进行操作,而不是只对单一产品进行操作,那么工厂方法模式就会变得很复杂,如何解决呢?
-
当系统所提供的工厂所需生产的具体产品并不是一个单一产品,而是多个位于不同继承结构之间的不同类型的产品组成的产品族,这时候就需要使用到抽象工厂模式。
- 继承结构:如一个抽象类是动物,动物中有狗、猫等等,则动物与狗、猫构成了一种继承结构,动物类是父类,狗、猫是子类。
- 产品族:指由同一个工厂生产,位于不同继承结构中的一组产品,如动物类中有狗的名称、叫声、吃东西,狗的名称属于名称一类,狗的叫声属于叫声一类,狗吃东西属于吃东西一类。
- 上图中,有名称、叫声、吃东西三个属性,它们之间都没有联系,是单独的,我们将名称、叫声、吃东西三个属性组成一个产品族,用于区分其他动物的名称、叫声、吃东西。每个产品族又可以划分为狗、猫、猪等不同的动物。
-
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品的继承结构,而抽象工厂模式则需要面对多个产品的继承结构,一个工厂继承结构可以负责多个不同产品继承结构中的产品对象的创建 。当一个工厂继承结构可以创建出分属于不同产品继承结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
概述
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
角色
抽象工厂模式包含如下角色:
- AbstractFactory:抽象工厂
- ConcreteFactory:具体工厂
- AbstractProduct:抽象产品
- Product:具体产品
- UML类图:
案例
- 实质:抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式能够对多个不同继承结构的不同产品族进行操作。
- 实现:对产品族的AnimalName/AnimalEat/AnimalShout类进行抽象,分别使用不同的继承对象(DogName/DogShout/DogEat/CatName/CateShout/CatEat)继承产品族的类,然后创建整个动物类的工厂接口(AnimalFactory),最后使用具体的工厂实现类(DogFactory/CatFactory)来实现动物类的工厂接口,并创建该继承对象对应产品族的实例。
- UML类图:
- 案例代码:
- 抽象产品角色(AnimalName/AnimalShout/AnimalEat)
//AbstractProduct abstract class AnimalName{ public abstract void name(); } abstract class AnimalShout{ public abstract void shout(); } abstract class AnimalEat{ public abstract void eat(); }
- 具体产品角色(DogName/DogShout/DogEat/CatName/CatShout/CatEat)
//Product class DogName extends AnimalName{ @Override public void name() { System.out.println("dog name"); } } class CatName extends AnimalName{ @Override public void name() { System.out.println("cat name"); } } //Product class DogShout extends AnimalShout{ @Override public void shout() { System.out.println("dog shout"); } } class CatShout extends AnimalShout{ @Override public void shout() { System.out.println("cat shout"); } } //Product class DogEat extends AnimalEat{ @Override public void eat() { System.out.println("dog eat"); } } class CatEat extends AnimalEat{ @Override public void eat() { System.out.println("cat eat"); } }
- 抽象工厂角色(AnimalFactory)
//AbstractFactory interface AnimalFactory{ AnimalName animalName(); AnimalShout animalShout(); AnimalEat animalEat(); }
- 具体工厂角色(DogFactory/CatFactory)
//Concrete Factory class DogFactory implements AnimalFactory{ @Override public AnimalName animalName() { return new DogName(); } @Override public AnimalShout animalShout() { return new DogShout(); } @Override public AnimalEat animalEat() { return new DogEat(); } } class CatFactory implements AnimalFactory{ @Override public AnimalName animalName() { return new CatName(); } @Override public AnimalShout animalShout() { return new CatShout(); } @Override public AnimalEat animalEat() { return new CatEat(); } }
- 测试类:
/** * 抽象方法模式-动物 * @author kime * */ public class AbstractFactoryAnimal { public static void main(String[] args) { AnimalFactory dogFactory = new DogFactory(); AnimalFactory catFactory = new CatFactory(); common(dogFactory); common(catFactory); } private static void common(AnimalFactory animalFactory) { animalFactory.animalName().name(); animalFactory.animalShout().shout(); animalFactory.animalEat().eat(); } }
- 结果:
- 抽象产品角色(AnimalName/AnimalShout/AnimalEat)
优点
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点
- 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
- 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。
场景
在以下情况下可以使用抽象工厂模式:
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
- 系统中有多于一个的产品族,而每次只使用其中某一产品族。
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。