设计模式之抽象工厂模式(4)

                             工厂模式三兄弟之工厂方法模式

 

1.什么是抽象工厂?

上一篇博文介绍了工厂方法模式,这里来说说比他更高级一点的抽象工厂模式,附上官方说法:

    抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角
色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况
下,创建多个产品族中的产品对象。根据里氏替换原则,任何接受父类型的地方,都应当能够接受子类型。因此,
实际上系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。换言之,也
就是这些抽象产品的具体子类的实例。工厂类负责创建抽象产品的具体子类的实例。

这个看着可能有点蒙蔽,什么是抽象工厂?可以理解为工厂方法模式的抽象化,那又是怎么抽象的呢?我们就来探讨一下这个问题,在工厂方法模式中有几个角色的概念,分别是抽象工厂,具体工厂,抽象产品,具体产品,我们从上一篇知道,每一个具体产品都是由他对应的抽象工厂生产出来的,这里的抽象工厂并不是现在我们要说的抽象工厂模式,简单点的个人理解,抽象工厂其实就是在工厂方法模式上再抽象出来一层,而抽象出来的这一层被赋予了一个官方的名字,那就是产品族,产品族是什么呢?产品族其实就是工厂方法模式中具体产品的父类(更高一级的抽象),举个例子,比如说饭菜(抽象工厂),肉菜有很多子类,比如说:红烧肉,水煮肉等等,素菜也一样,比如炒鸡蛋和炒白菜,而在工厂方法模式中,红烧肉这个就属于具体产品,它是由具体工厂(肉菜工厂)生产出来的,具体产品的进一步抽象(荤素套餐)也就是产品族,而抽象工厂模式简单点说就是将一些相关的具体产品组成一个产品族,然后交给同一个具体工厂来统一生产 

 

2.为什么用抽象工厂?

从上边我们可能已经了解了一些概念,但是为什么用这个模式,我们再来探讨一下,其实工厂方法模式已经解决了很多问题了,但是抽象工厂的出现完全就是基于工厂方法模式进行的改造,工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销和代码量,如果代码量逐渐庞大起来,维护起来也是很大的一个成本.还有一个就是,工厂方法模式具有唯一性,在工厂方法模式中具体工厂只能生产具体产品,而无法做到生产多种类相近的产品,就像上边说的,一个肉菜工厂只能生产红烧肉,如果我想让这个工厂也能生产炒鸡蛋怎么办,就要新建工厂,可是如果我把红烧肉工厂抽象成荤素套餐工厂,把红烧肉和炒鸡蛋抽象成荤素套餐(产品族),让荤素套餐工厂可以生产产品族不就更节省的代码,也更高效和灵活了么,而根据里氏代换原则,再去看官方的这一句:任何接受父类型的地方,都应当能够接受子类型。因此,实际上系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。换言之,也就是这些抽象产品的具体子类的实例。这就很好理解了,我们需要的其实还是具体产品,也就是红烧肉,但是我们生成出来的产品族(也就是父类型荤素套餐)要完全可以接受子类型(红烧肉,炒鸡蛋),也就是一个向上转型而已,最终得到的也就是红烧肉而不是产品族了.

 

3.抽象工厂怎么用?

先来看看抽象工厂模式的UML模型:

 设计模式之抽象工厂模式(4)

 上代码:

抽象工厂:

/**
 * @author zhangcq
 * @Description: 抽象工厂
 * @Time: 2019/3/4 14:04
 * @Version 1.0
 */
public interface LogsFactory {

    public AbstractOtherProduct createProductOne();
    public AbstractSpecifcProduct createProductTwo();

}

具体工厂:

/**
 * @author zhangcq
 * @Description: 具体工厂
 * @Time: 2019/3/8 13:36
 * @Version 1.0
 */
public class SpecificFactory implements LogsFactory {

    @Override
    public AbstractOtherProduct createProductOne() {
        return new SpecifcProductOne();
    }

    @Override
    public AbstractSpecifcProduct createProductTwo() {
        return new SpecifcProductTwo();
    }
}

其他工厂:

/**
 * @author zhangcq
 * @Description: 其他工厂
 * @Time: 2019/3/8 13:36
 * @Version 1.0
 */
public class OtherFactory implements LogsFactory {

    @Override
    public AbstractOtherProduct createProductOne() {
        return new OtherProductOne();
    }

    @Override
    public AbstractSpecifcProduct createProductTwo() {
        return new OtherProductTwo();
    }
}

抽象产品1:

/**
 * @author zhangcq
 * @Description: 抽象产品1
 * @Time: 2019/3/4 14:04
 * @Version 1.0
 */
public interface AbstractOtherProduct {

    void print();
}

具体产品1:

/**
 * @Description: 具体产品1
 * @author: zhangcq
 * @Time: 2019/3/15 17:29
 * @Version 1.0
 */
public class SpecifcProductOne implements AbstractOtherProduct {
    @Override
    public void print() {
        System.out.println("具体产品1");
    }
}

其他产品1:

/**
 * @Description: 其他产品1
 * @author: zhangcq
 * @Time: 2019/3/15 17:29
 * @Version 1.0
 */
public class OtherProductOne implements AbstractOtherProduct {
    @Override
    public void print() {
        System.out.println("其他产品1");
    }
}

抽象产品2:

/**
 * @author zhangcq
 * @Description: 抽象产品2
 * @Time: 2019/3/4 14:04
 * @Version 1.0
 */
public interface AbstractSpecifcProduct {

    void print();
}

具体产品2:

/**
 * @Description: 具体产品2
 * @author: zhangcq
 * @Time: 2019/3/15 17:29
 * @Version 1.0
 */
public class SpecifcProductTwo implements AbstractSpecifcProduct {
    @Override
    public void print() {
        System.out.println("具体产品2");
    }
}

其他产品2:

/**
 * @Description: 其他产品2
 * @author: zhangcq
 * @Time: 2019/3/15 17:29
 * @Version 1.0
 */
public class OtherProductTwo implements AbstractSpecifcProduct {
    @Override
    public void print() {
        System.out.println("其他产品2");
    }
}

客户端:

/**
 * @author zhangcq
 * @Description: 客户端
 * @Time: 2019/3/4 14:18
 * @Version 1.0
 */
public class LogsClient {

    public static void main(String[] args) {
        //抽象工厂
        LogsFactory logsFactory;
        //抽象产品1,产品族1
        AbstractOtherProduct abstractProductOne;
        //抽象产品2,产品族2
        AbstractSpecifcProduct abstractProductTwo;
        //其他工厂
        logsFactory = new OtherFactory();
        abstractProductOne = logsFactory.createProductOne();
        abstractProductOne.print();
        abstractProductTwo = logsFactory.createProductTwo();
        abstractProductTwo.print();

        //具体工厂
        logsFactory = new SpecificFactory();
        abstractProductOne = logsFactory.createProductOne();
        abstractProductOne.print();
        abstractProductTwo = logsFactory.createProductTwo();
        abstractProductTwo.print();

    }
}

 

4.抽象工厂的总结

     1. 主要优点

       抽象工厂模式的主要优点如下:

       (1) 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。

       (2) 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。

       (3) 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。

 

       2. 主要缺点

       抽象工厂模式的主要缺点如下:

       增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。

 

       3. 适用场景

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

       (1) 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。

       (2) 系统中有多于一个的产品族,而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。

       (3) 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束,如同一操作系统下的按钮和文本框,按钮与文本框之间没有直接关系,但它们都是属于某一操作系统的,此时具有一个共同的约束条件:操作系统的类型。

       (4) 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。
_____________________________________________________________________________________________________代码地址:github