抽象工厂模式

avatar
Android @奇舞团Android团队
原文链接: mp.weixin.qq.com

内容简介:简单的案例实现抽象工厂模式,分析抽象工厂的优势与存在的问题。

系列文章
单例模式 Build 模式
前言

简单工厂根据对象类型信息创建对象,但责任太重,不符合单一职责原则;工厂模式引入等级结构,解决了简单工厂责任太重的问题,那抽象工厂的主要作用是什么呢?

产品等级结构和产品簇

如果要区分工厂和抽象工厂,理解产品等级结构和产品簇是必要条件。 

图片引自:https://www.cnblogs.com/lfxiao/p/6811820.html

上图展示了产品等级和产品簇之间的区别。

在横坐标方向,图形分正方形,原型,椭圆;在纵坐标方向上,为每种图形的不同颜色。产品等级结构是相同产品的不同表现形式,比如车->黑色的车->白色的车,在产品等级结构中,所有的产品都有共同的基类;产品簇是具有某种相同属性的不同产品,比如黑色的宝马车->黑色的自行车->黑色的手扶拖拉机,在产品簇中,所有的产品拥有相同的约束--黑色的车。

定义

为创建一组或具有相同约束的对象提供一个接口。

适用范围

一个产品由相同的约束。比如都是黑色的交通工具,都是黑色的图形......

实现

在介绍工厂模式时,使用的示例如下: 

工厂的基类 Factory 定义了createAnimal 方法,具体的工厂根据待生产的产品的不同分 CatFactory 和 DogFactory ,这两个 Factory 的子类分别用于新建 Cat 对象和 Dog 对象。

假设现在对猫 Animal 进行具体化,分为黑颜色的猫 BlackCat ,白颜色的猫 WhiteCat ,黑颜色的狗 BlackDog ,白颜色的狗 WhiteDog ,如果使用工厂模式,结构图如下: 

可以发现系统中的类会成对增加,造成这种情况的原因在于每个工厂只能生产一种具体的产品,每增加一个产品均需要新建一个工厂,那有没有什么方法能够避免这种情况的出现呢?

在以上的示例中,白色的狗 WhiteDog 和白色的猫 WhiteCat 都是相同颜色的动物,相同颜色的狗和猫是相同颜色的小动物,将他们放在同一个产品簇中,修改后的结构如下: 

如果继续添加黑色的猪 BlackPig ,白色的猪 WhitePig ,则只需要增加新的 Animal 类型,在 Factory 中添加对应的 create 方法即可,不会出现类急剧增多的情况。

具体实现如下:

    public interface Animal {

       void eat();

    }

    public class Cat implements Animal {

       private String color;

       public Cat(String color) {

           // TODO Auto-generated constructor stub

           this.color = color;

       }

       @Override

       public void eat() {

           // TODO Auto-generated method stub

           System.out.println(color + " cat eats");

       }

    }

    public class Dog implements Animal {

       private String color;

       public Dog(String color) {

           // TODO Auto-generated constructor stub

           this.color = color;

       }

       @Override

       public void eat() {

           // TODO Auto-generated method stub

           System.out.println(color + " dog eats");

       }

    }

    public abstract class AbstractAnimalFactory {

       abstract Animal makeCat();

       abstract Animal makeDog();

    }

    public class BlackAnimalFactory extends AbstractAnimalFactory {

       @Override

       Animal makeCat() {

           // TODO Auto-generated method stub

           return new Cat("black");

       }

       @Override

       Animal makeDog() {

           // TODO Auto-generated method stub

           return new Dog("black");

       }

    }

    public class WhiteAnimalFactory extends AbstractAnimalFactory {

       @Override

       Animal makeCat() {

           // TODO Auto-generated method stub

           return new Cat("white");

       }

       @Override

       Animal makeDog() {

           // TODO Auto-generated method stub

           return new Dog("white");

       }

    }

测试代码如下:

    public static void main(String [] args) {

           AbstractAnimalFactory whiteAnimalFactory = new WhiteAnimalFactory();

           Cat whiteCat = (Cat) whiteAnimalFactory.makeCat();

           Dog whiteDog = (Dog) whiteAnimalFactory.makeDog();

           whiteCat.eat();

           whiteDog.eat();

           AbstractAnimalFactory blackAnimalFactory = new BlackAnimalFactory();

           Cat blackCat = (Cat) blackAnimalFactory.makeCat();

           Dog blackDog = (Dog) blackAnimalFactory.makeDog();

           blackCat.eat();

           blackDog.eat();

       }

运行结果如下:

white cat eats white dog eats black cat eats black dog eats

问题

似乎抽象工厂很完美,现在有一个新的需求过来了,需要在系统中添加白色的猪 WhitePig 和黑色的猪 BlackPig 。突然发现在原有的抽象工厂结构中,如果需要添加这两种 Animal ,需要对 Factory 进行修改,添加 makePig 方法,同时 WhiteAnimalFactory 和 BlackAnimalFactory 都需要实现 makePig 方法。

这样的设计违反了开闭原则(一个软件实体应当对扩展开放,对修改关闭),有没有什么办法解决这个问题呢?抽象工厂无法解决这样的问题。可以发现,使用抽象工厂时,增加产品簇(在本文中可以理解为为现有动物添加新的颜色分类)可以满足开闭原则,但是增加产品等级结构(在本文中可以理解为增加新的动物),就需要对原结构进行大量的修改。

总结
  • 简单工厂侧重根据类型信息创建对象,但是不符合单一职责原则;工厂模式中每个工厂只生产一种具体的产品,如果产品过多,可能会导致系统中存在大量的工厂类;抽象工厂将工厂模式中相关的产品组成产品簇,由一个工厂生产,减少了系统中类的数量。

  • 使用抽象工厂增加产品簇符合开闭原则,但增加产品等级结构不满足开闭原则。

  • 在使用抽象工厂模式之前,需要设计好产品簇和产品等级结构,防止由于增加产品等级结构而造成系统大幅度的改动。

--END--

识别二维码,关注我们