深入浅出设计模式-抽象工厂模式

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

这个时候我们就需要使用抽象工厂模式。

在讲解抽象工厂模式之前,我们需要介绍两个概念:

  • 产品等级结构:产品的继承结构。
  • 产品族:在抽象工厂模式中,产品族(Dough、Sauce)是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。

模式定义

提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

设计原则

遵循的原则:

  • 依赖倒置原则(工厂构建产品的方法均返回产品接口而非具体产品,从而使客户端依赖于产品抽象而非具体)
  • 迪米特法则
  • 里氏替换原则
  • 接口隔离原则
  • 单一职责原则(每个工厂只负责创建自己的具体产品族,没有简单工厂中的逻辑判断)
  • 开闭原则(增加新的产品族,不像简单工厂那样需要修改已有的工厂,而只需增加相应的具体工厂类)

未遵循的原则:

  • 开闭原则(虽然对新增产品族符合开-闭原则,但对新增产品种类不符合开-闭原则)

模式类图

抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂模式只是用于创建一个对象,这和抽象工厂模式有很大不同。

抽象工厂模式用到了工厂模式来创建单一对象,在类图左部,AbstractFactory 中的 CreateProductA 和 CreateProductB 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂模式的定义。

至于创建对象的家族这一概念是在 Client 体现,Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象,在这里这两个对象就有很大的相关性,Client 需要同时创建出这两个对象。

从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂模式使用了继承。

抽象工厂模式实例

问题描述:

依然是披萨店。为了要保证每家加盟店都能够生产高质量的披萨,防止使用劣质的原料,我们打算建造一家生产原料的工厂,并将原料运送到各家加盟店。但是加盟店都位于不同的区域,比如纽约、芝加哥。纽约使用一组原料,芝加哥使用另一种原料。在这里我们可以这样理解,这些不同的区域组成了原料家族,每个区域实现了一个完整的原料家族。

问题的解决方案类图

首先定义原料接口-酱汁和面团

package com.wpx.abstractfactory;

/**
 * 原料接口-酱汁
 */
public interface Sauce {
    public String sauceType();
}
package com.wpx.abstractfactory;

/**
 * 原料接口-面团
 */
public interface Dough  {
    public String doughType();
}

再定义具体的原料类-李子番茄酱、纯番茄酱、薄壳面团、厚壳面团

package com.wpx.abstractfactory;

/**
 * 具体原料-李子番茄酱
 */
public class PlumTomatoSauce implements Sauce {
    @Override
    public String sauceType() {
        return "李子番茄酱";
    }
}
package com.wpx.abstractfactory;

/**
 * 具体原料-纯番茄酱
 */
public class MarinaraSauce implements Sauce {
    @Override
    public String sauceType() {
        return "纯番茄酱";
    }
}
package com.wpx.abstractfactory;

/**
 * 薄壳面团
 */
public class ThinCrustDough implements Dough {
    @Override
    public String doughType() {
        return "薄壳面团";
    }
}
package com.wpx.abstractfactory;

/**
 * 厚壳面团
 */
public class ThickCrustDough implements Dough {

    @Override
    public String doughType() {
        return "厚壳面团";
    }
}

有了原料,现在我们来定义一个抽象工厂接口-披萨原料工厂

package com.wpx.abstractfactory;

/**
 * 抽象工厂-披萨原料工厂
 */
public interface PizzaIngredientFactory {
    public Dough createDough();
    public Sauce createSauce();
}

紧接着,我们来找两个加盟商(具体工厂),纽约原料工厂和芝加哥原料工厂

package com.wpx.abstractfactory;

/**
 * 具体原料工厂-纽约原料工厂
 */
public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
    @Override
    public Dough createDough() {
        return new ThickCrustDough();
    }

    @Override
    public Sauce createSauce() {
        return new MarinaraSauce();
    }
}
package com.wpx.abstractfactory;

/**
 * 具体原料工厂-芝加哥原料工厂
 */
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory{
    @Override
    public Dough createDough() {
        return new ThinCrustDough();
    }

    @Override
    public Sauce createSauce() {
        return new PlumTomatoSauce();
    }
}

现在该有一个披萨店去制作披萨

package com.wpx.abstractfactory;

/**
 * 纽约披萨店
 */
public class NYPizzaStore {
    private PizzaIngredientFactory ingredientFactory;

    public NYPizzaStore() {
        // 纽约店会用到纽约披萨原料工厂,由该原料工厂负责生产使用纽约风味披萨所需的原料
        ingredientFactory = new NYPizzaIngredientFactory();
    }

    public void makePizza() {
        Dough dough = ingredientFactory.createDough();
        Sauce sauce = ingredientFactory.createSauce();
        System.out.println(dough.doughType());
        System.out.println(sauce.sauceType());
    }
}

现在我们去纽约的披萨店整一份披萨尝尝

package com.wpx.abstractfactory;

/**
 * 测试抽象工厂模式
 */
public class NYPizzaStoreDemo {
    public static void main(String[] args) {
        NYPizzaStore nyPizzaStore = new NYPizzaStore();
        nyPizzaStore.makePizza();
    }
}

运行结果

厚壳面团
纯番茄酱

Process finished with exit code 0

JDK中的抽象工厂模式

java.util.Calendar#getInstance()
java.util.Arrays#asList()
java.util.ResourceBundle#getBundle()
java.sql.DriverManager#getConnection()
java.sql.Connection#createStatement()
java.sql.Statement#executeQuery()
java.text.NumberFormat#getInstance()
javax.xml.transform.TransformerFactory#newInstance()

总结

优点

  • 抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建。
  • 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
  • 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点

  • 增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。

使用场景

  • 当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
  • 系统中有多于一个的产品族,而每次只使用其中某一产品族。
  • 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。

原文链接:https://group.cnblogs.com/topic/79078.html

原文发布于微信公众号 - java工会(javagonghui)

原文发表时间:2018-04-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏wOw的Android小站

[设计模式]之十三:抽象工厂模式

抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构。

502
来自专栏散尽浮华

Python-面向对象编程

概述: 面向过程:根据业务逻辑从上到下写代码。 函数式:将某功能代码封装到函数中,以后便无需重复编写,进调用函数即可。 面向对象:对函数进行分类和封装,让开发“...

1897
来自专栏python3

python 面向对象之继承实例讲解

        --- info of Student:ChenRonghua ---

802
来自专栏小樱的经验随笔

设计模式六大原则(5):迪米特法则

定义:一个对象应该对其他对象保持最少的了解。 问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。 解决方案:尽量降低类与...

2826
来自专栏菩提树下的杨过

java:集合的自定义多重排序

有一个乱序的对象集合,要求先按对象的属性A排序(排序规则由业务确定,非A-Z或0-9的常规顺序),相同A属性的记录,按根据属性B排序(排序规则,同样由业务确定,...

321
来自专栏有趣的Python

3-Java面向对象-封装综合案例

案例: 学校开设了计算机科学与应用这个专业,专业编号: J0001;学制年限: 4年;

661
来自专栏醒者呆

促和谐好干部——适配器模式

我们的笔记本电脑的充电线上通常都会有一个大砖块似得东西,这个东西是用来将家用额定电压220V转换成笔记本适用的电压,它叫做变压器,也叫做适配器。 今天要研究的是...

3515
来自专栏技术博客

设计模式之十(外观模式)

外观模式:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一系统更加容易使用.

602
来自专栏java一日一条

Java面试题:多继承

招聘和面试对开发经理来说是一个无尽头的工作,虽然有时你可以从HR这边获得一些帮助,但是最后还是得由你来拍板,或者就像另一篇文章“Java 面试题:写一个字符串的...

614
来自专栏nummy

抽象工厂模式

当每个抽象产品都有多于一个的具体子类的时候,工厂角色怎么知道实例化哪一个子类呢?比如每个抽象产品角色都有两个具体产品。抽象工厂模式提供两个具体工厂角色,分别对应...

863

扫码关注云+社区