[关闭]
@w1992wishes 2017-11-01T17:20:00.000000Z 字数 7654 阅读 1020

设计模式--抽象工厂模式

设计模式 创建型模式 工厂模式


设计原则:

要依赖抽象,不要依赖具体类

目录

本文的结构如下:

一、前言

工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产,这就是我们本文将要学习的抽象工厂模式的基本思想。

二、什么是抽象工厂模式

抽象工厂模式(Abstract Factory Pattern):提供一个接口,用于创建一系列相关或相互依赖对象的家族,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。

三、为什么要用该模式

3.1、官方解释

在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。

为了更清晰地理解工厂方法模式,需要先引入两个概念:

当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。

抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。

抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。

3.2、举个例子

你的蛋糕店卖得非常好,为了更好的提供效率,你决定为每个蛋糕店开设一个原料提供工厂,每个蛋糕店都有专门的原料工厂提供原料,比如水果蛋糕需要的鸡蛋和水果,但CenterCakeStore更多卖的是草莓类水果蛋糕,用的鸡蛋是一般的鸡蛋,而CollegeCakeStore更多卖的是菠萝类水果蛋糕,并且用的鸡蛋都是土鸡蛋,所以原料工厂提供的具体水果和鸡蛋是不一样的,代码怎么设计呢?

这次你没有请我,因为你足够聪明,你知道我肯定会这样写:

为每个原料建立一个抽象工厂类,提供抽象方法用于获取产品:

  1. /**
  2. * Created by w1992wishes on 2017/11/1.
  3. */
  4. public abstract class FruitFactory {
  5. public abstract Fruit provideFruit();
  6. }
  7. /**
  8. * Created by w1992wishes on 2017/11/1.
  9. */
  10. public abstract class EggFactory {
  11. abstract Egg provideEgg();
  12. }

然后为抽象工厂提供多个实现,每个子类工厂提供一种具体的原料:

  1. /**
  2. * Created by w1992wishes on 2017/11/1.
  3. */
  4. public class CenterFruitFactory extends FruitFactory {
  5. @Override
  6. public Fruit provideFruit() {
  7. return new StrawberryFruit();
  8. }
  9. }
  10. /**
  11. * Created by w1992wishes on 2017/11/1.
  12. */
  13. public class CollegeFruitFactory extends FruitFactory{
  14. @Override
  15. public Fruit provideFruit() {
  16. return new MangoFruit();
  17. }
  18. }
  19. /**
  20. * Created by w1992wishes on 2017/11/1.
  21. */
  22. public class CenterEggFactory extends EggFactory {
  23. @Override
  24. Egg provideEgg() {
  25. return new NormalEgg()();
  26. }
  27. }
  28. /**
  29. * Created by w1992wishes on 2017/11/1.
  30. */
  31. public class CollegeEggFactory extends EggFactory{
  32. @Override
  33. Egg provideEgg() {
  34. return new SpecialEgg();
  35. }
  36. }

接着还有一堆抽象原料,Egg,Fruit...
一堆具体原料,SpecialEgg,NormalEgg,MangoFruit...
还要在具体的Cake中用具体的原料:

  1. /**
  2. * Created by w1992wishes on 2017/10/31.
  3. */
  4. public class CenterFruitCake extends Cake {
  5. public CenterFruitCake(){
  6. name = "center fruit cake";
  7. }
  8. @Override
  9. public void prepare(){
  10. FruitFactory fruitFactory = new CenterFruitFactory();
  11. EggFactory eggFactory = new CenterEggFactory();
  12. fruit = fruitFactory.provideFruit();
  13. egg = eggFactory.provideEgg();
  14. }
  15. }
  16. /**
  17. * Created by w1992wishes on 2017/11/1.
  18. */
  19. public class CollegeFruitCake extends Cake {
  20. public CollegeFruitCake(){
  21. name = "college fruit cake";
  22. }
  23. @Override
  24. public void prepare(){
  25. FruitFactory fruitFactory = new CollegeFruitFactory();
  26. EggFactory eggFactory = new CollegeEggFactory();
  27. fruit = fruitFactory.provideFruit();
  28. egg = eggFactory.provideEgg();
  29. }
  30. }

经过一堆复杂的类构建,最后才能订购,而且这还只是一种蛋糕,如果需要增加新的蛋糕时,虽然不要修改现有代码,但是需要增加大量类,针对每一个新增具体组件都需要增加一个具体工厂,类的个数成对增加,这无疑会导致系统越来越庞大,增加系统的维护成本和运行开销;

正是因为每一个具体工厂对应一种具体产品,如果产品族很多,会引入很多类,为了解决这个问题,所以有了抽象工厂模式。

四、模式的结构

抽象工厂模式结构和工厂方法模式结构差不多,只是多了产品族中其他的抽象产品和具体产品。

● Factory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。
● ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
● Product(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
● ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。

五、代码示例

先定义一个抽象原料工厂:

  1. /**
  2. * Created by w1992wishes on 2017/11/1.
  3. */
  4. public interface FoodFactory {
  5. Egg provideEgg();
  6. Fruit provideFruit();
  7. }

每个蛋糕店对应的具体工厂:

  1. /**
  2. * Created by w1992wishes on 2017/11/1.
  3. */
  4. public class CenterFoodFactory implements FoodFactory {
  5. @Override
  6. public Egg provideEgg() {
  7. return new NormalEgg();
  8. }
  9. @Override
  10. public Fruit provideFruit() {
  11. return new StrawberryFruit();
  12. }
  13. }
  14. /**
  15. * Created by w1992wishes on 2017/11/1.
  16. */
  17. public class CollegeFoodFactory implements FoodFactory {
  18. @Override
  19. public Egg provideEgg() {
  20. return new SpecialEgg();
  21. }
  22. @Override
  23. public Fruit provideFruit() {
  24. return new MongoFruit();
  25. }
  26. }

具体的水果蛋糕类:

  1. /**
  2. * Created by w1992wishes on 2017/11/01.
  3. */
  4. public abstract class Cake {
  5. String name;
  6. Fruit fruit;
  7. Egg egg;
  8. abstract void prepare();
  9. void bake(){
  10. System.out.println("bake");
  11. }
  12. void box(){
  13. System.out.println("box");
  14. }
  15. public String getName() {
  16. return name;
  17. }
  18. public Fruit getFruit(){
  19. return fruit;
  20. }
  21. public Egg getEgg(){
  22. return egg;
  23. }
  24. }
  1. /**
  2. * Created by w1992wishes on 2017/10/31.
  3. */
  4. public class CenterFruitCake extends Cake {
  5. public CenterFruitCake(){
  6. name = "center fruit cake";
  7. }
  8. @Override
  9. public void prepare(){
  10. FoodFactory foodFactory = new CenterFoodFactory();
  11. fruit = foodFactory.provideFruit();
  12. egg = foodFactory.provideEgg();
  13. }
  14. }
  15. /**
  16. * Created by w1992wishes on 2017/11/1.
  17. */
  18. public class CollegeFruitCake extends Cake {
  19. public CollegeFruitCake(){
  20. name = "college fruit cake";
  21. }
  22. @Override
  23. public void prepare(){
  24. FoodFactory foodFactory = new CollegeFoodFactory();
  25. fruit = foodFactory.provideFruit();
  26. egg = foodFactory.provideEgg();
  27. }
  28. }

蛋糕店的代码不变:

  1. /**
  2. * Created by w1992wishes on 2017/11/1.
  3. */
  4. public class CenterCakeStore extends CakeStore {
  5. @Override
  6. protected Cake createCake(String type) {
  7. Cake cake = null;
  8. if ("cheese".equals(type)) {
  9. cake = new CenterCheeseCake();
  10. } else if ("fruit".equals(type)) {
  11. cake = new CenterFruitCake();
  12. } else if ("cream".equals(type)) {
  13. cake = new CenterCreamCake();
  14. }
  15. return cake;
  16. }
  17. }
  18. /**
  19. * Created by w1992wishes on 2017/11/1.
  20. */
  21. public class CollegeCakeStore extends CakeStore {
  22. @Override
  23. protected Cake createCake(String type) {
  24. Cake cake = null;
  25. if ("cheese".equals(type)) {
  26. cake = new CollegeCheeseCake();
  27. } else if ("fruit".equals(type)) {
  28. cake = new CollegeFruitCake();
  29. } else if ("cream".equals(type)) {
  30. cake = new CollegeCreamCake();
  31. }
  32. return cake;
  33. }
  34. }

客户端代码不变:

  1. /**
  2. * Created by w1992wishes on 2017/11/1.
  3. */
  4. public class Client {
  5. public static void main(String[] args) {
  6. CakeStore cakeStore = new CollegeCakeStore();//这里可通过引入配置文件更改
  7. cakeStore.orderCake("fruit");
  8. }
  9. }

看这段代码可能有点疑惑,建议不用管蛋糕店,直接看制作蛋糕的原料那块,是抽象工厂模式的真正实现代码。

相对于工厂方法模式,抽象工厂方法,即没有破坏“开闭原则”,同时又解决了前者引入大量工厂类的弊端,是前者更高层次的抽象。

六、优点和缺点

6.1、优点

6.2、缺点

正因为抽象工厂模式存在“开闭原则”的倾斜性,它以一种倾斜的方式来满足“开闭原则”,为增加新产品族提供方便,但不能为增加新产品结构提供这样的方便,因此要求设计人员在设计之初就能够全面考虑,不会在设计完成之后向系统中增加新的产品等级结构,也不会删除已有的产品等级结构,否则将会导致系统出现较大的修改,为后续维护工作带来诸多麻烦。

七、适用环境

在以下情况下可以考虑使用抽象工厂模式:

八、模式应用

在很多软件系统中需要更换界面主题,要求界面中的按钮、文本框、背景色等一起发生改变时,可以使用抽象工厂模式进行设计。

九、模式扩展

“开闭原则”的倾斜性

在介绍抽象工厂方法模式的缺点已经提到,这里再重复啰嗦一下。

“开闭原则”要求系统对扩展开放,对修改封闭,通过扩展达到增强其功能的目的。对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面:

工厂模式的退化

当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。

十、总结

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注