本篇Blog继续学习创建型模式,创建型模式的主要关注点是怎样创建对象,它的主要特点是将对象的创建与使用分离,这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。本篇学习的是抽象工厂模式。由于学习的都是设计模式,所有系列文章都遵循如下的目录:

  • 模式档案:包含模式的定义、模式的特点、解决什么问题、优缺点、使用场景等
  • 模式结构:包含模式的结构,包含的角色定义及调用关系
  • 模式实现:包含模式的实现方式代码举例或者生活中简单问题映射代码举例
  • 模式实践:如果工作中或开源项目用到了该模式,就将使用过程贴到这里,并且客观讨论使用的是否恰当
  • 模式对比:如果模式相似或模式有额外的替换方法,有必要体现其相似点及不同点,区分使用,说明哪些场景下使用哪种模式比较好
  • 模式扩展:如果模式有与标准结构定义不同的变体形式,一并体现出其变体结构;对模式的思考需要进行发散等。

接下来所有设计模式的介绍都暂且遵循此基本行文逻辑吗,如果某一条目没有则无需体现,但条目顺序遵循此结构,本文的模式实践案例大多来自极客时间。

模式档案

在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族

模式定义:抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象

模式特点:抽象工厂模式的目的是提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式,可以把抽象工厂比作一个工厂,而工厂方法就是这个抽象工厂中的一条条产品线,负责生产不同维度的产品。

解决什么问题:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。例如对于宝马车来说,宝马5系和宝马3系都需要:座椅、轮胎。但是宝马5系的配置是:真皮座椅、米其林轮胎;而宝马3系的配置是:革皮座椅、普通轮胎。那么创建5系的工厂和创建3系的工厂就要注意了,它们需要生产成套且有约束的产品族,不能把真皮座椅和普通轮胎组合起来,5系就要5系的产品族配置,3系就要3系的产品族配置。

优点: 除了工厂方法具备的解耦性优点外,它还额外具备产品族的约束作用:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点:产品族难扩展,如果产品族中增加了一个创建产品的工厂方法(产品族扩展一个新产品),那么所有具体的工厂类都需要实现这个方法

使用场景: 当要创建的内容为一组相互依赖相互约束的产品族的产品时,使用抽象工厂模式可以方便的实现这个需求,拿上一篇工厂方法模式的形状例子扩展,如果我们想要在抽象工厂中制造红方旗【方形布料+红色染料】就需要布料产品线产出方形布料+染料产品线产出红色染料

模式结构

我们接着按照工厂方法模式中的角色来聊聊抽象工厂中该有哪些工厂以及创建过程,相对于工厂方法模式,它的特点就是:有多个抽象产品类并且每个产品抽象类有多个具体的产品类,有一个抽象工厂类和多个具体的工厂类,每个具体工厂类能创建多个具体的产品类实例

  • 抽象工厂角色: 它是具体工厂角色必须实现的接口或者必须继承的父类,在Java中它由抽象类或接口来实现。它包含创建多个抽象产品的抽象方法。
  • 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的一组具体产品对象
  • 抽象产品角色:它是具体产品继承的父类或者是实现的接口。在Java中一般由抽象类或者接口来实现。
  • 具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在Java中由具体的类来实现。

在以下的例子中我们的抽象工厂是用来生产旗帜的,拥有多条产品线,包括生产布料产品线和生产染料产品线。

模式实现

以下是各个参与其中的角色

抽象产品角色

布料形状抽象产品角色

package com.example.designpattern.factory;/*** The interface Shape.*/
public interface Shape {/*** Draw.*/void draw();
}

染料颜色抽象产品角色

public interface Color {void fill();
}

具体产品角色

布料形状具体产品角色

public class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("Inside Rectangle::draw() method.");}
}public class Square implements Shape {@Overridepublic void draw() {System.out.println("Inside Square::draw() method.");}
}public class Circle implements Shape {@Overridepublic void draw() {System.out.println("Inside Circle::draw() method.");}
}

布料形状具体产品角色

public class Red implements Color {@Overridepublic void fill() {System.out.println("Inside Red::fill() method.");}
}public class Yellow implements Color {@Overridepublic void fill() {System.out.println("Inside Yellow ::fill() method.");}
}public class Blue implements Color {@Overridepublic void fill() {System.out.println("Inside Blue::fill() method.");}
}

抽象工厂角色

抽象工厂角色

  public interface AbstractFactory {    //制造布料 public Shape getShape();  //制造染料   public Color getColor();   }

具体工厂角色

具体工厂角色

public class RedSquareFactory implements AbstractFactory {@Overridepublic Shape getShape() {return new Square ();}@Overridepublic Color getColor() {return new Red();}
}public class CircleBlueFactory implements AbstractFactory {@Overridepublic Shape getShape() {return new Circle();}@Overridepublic Color getColor() {return new Blue();}
}public class YellowRectangleFactory implements AbstractFactory {@Overridepublic Shape getShape() {return new Rectangle();}@Overridepublic Color getColor() {return new Yellow();}
}

客户端调用代码知道它所需要的是哪种旗子,通过具体工厂获取产品并打印结果如下:

public class FactoryPatternDemo {public static void main(String[] args) {//获取 RedSquareFactory工厂并创造产品族实例AbstractFactory redSquareFactory = new RedSquareFactory();redSquareFactory .getShape();redSquareFactory .getColor();//获取 CircleBlueFactory工厂并创造产品族实例AbstractFactory circleBlueFactory = new CircleBlueFactory();circleBlueFactory .getShape();circleBlueFactory .getColor();//获取 YellowRectangleFactory工厂并创造产品族实例AbstractFactory yellowRectangleFactory = new YellowRectangleFactory();yellowRectangleFactory .getShape();yellowRectangleFactory .getColor();}
}

模式实践

接着上篇Blog的文本文件解析,我们再给例子增加一个维度。在简单工厂和工厂方法中,类只有一种分类方式。比如,在规则配置解析那个例子中,解析器类只会根据配置文件格式(Json、Xml、Yaml……)来分类。

设计一个多维可扩展文件解析工厂

如果解析维度更多,比如,我们既可以按照配置文件格式来分类,也可以按照解析的对象(Rule 规则配置还是 System 系统配置)来分类,那就会对应下面这 8 个 parser 类

针对规则配置的解析器:基于接口IRuleConfigParser
JsonRuleConfigParser
XmlRuleConfigParser
YamlRuleConfigParser
PropertiesRuleConfigParser针对系统配置的解析器:基于接口ISystemConfigParser
JsonSystemConfigParser
XmlSystemConfigParser
YamlSystemConfigParser
PropertiesSystemConfigParser

针对这种特殊的场景,如果还是继续用工厂方法来实现的话,我们要针对每个 parser 都编写一个工厂类,也就是要编写 8 个工厂类。如果我们未来还需要增加针对业务配置的解析器(比如 IBizConfigParser),那就要再对应地增加 4 个工厂类。而我们知道,过多的类也会让系统难维护。

抽象工厂就是针对这种非常特殊的场景而诞生的。我们可以让一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser 对象。这样就可以有效地减少工厂类的个数。具体的代码实现如下所示

抽象产品角色

package com.example.designpattern.factory;
public interface IRuleConfigParser{void parse();
}
public interface ISystemConfigParser{void parse();
}

具体产品角色

IRuleConfigParser的具体产品

public class JsonRuleConfigParser implements IRuleConfigParser {@Overridepublic void parse() {System.out.println("IRuleConfigParser  JsonRuleConfigParser parse ");}
}public class XmlRuleConfigParser implements IRuleConfigParser {@Overridepublic void parse() {System.out.println("IRuleConfigParser  XmlRuleConfigParser parse");}
}public class YamlRuleConfigParser implements IRuleConfigParser {@Overridepublic void parse() {System.out.println("IRuleConfigParser  YamlRuleConfigParser parse");}
}public class PropertiesRuleConfigParser implements IRuleConfigParser {@Overridepublic void parse() {System.out.println("IRuleConfigParser  PropertiesRuleConfigParser parse");}
}

ISystemConfigParser 的具体产品

public class JsonSystemConfigParser implements ISystemConfigParser {@Overridepublic void parse() {System.out.println("ISystemConfigParser JsonRuleConfigParser parse ");}
}public class XmlSystemConfigParser implements ISystemConfigParser {@Overridepublic void parse() {System.out.println("ISystemConfigParser XmlRuleConfigParser parse");}
}public class YamlSystemConfigParser implements ISystemConfigParser {@Overridepublic void parse() {System.out.println("ISystemConfigParser YamlRuleConfigParser parse");}
}public class PropertiesSystemConfigParser implements ISystemConfigParser {@Overridepublic void parse() {System.out.println("ISystemConfigParser PropertiesRuleConfigParser parse");}
}

抽象工厂角色

public interface IConfigParserFactory {IRuleConfigParser createRuleParser();ISystemConfigParser createSystemParser();//此处可以扩展新的parser类型,比如IBizConfigParser
}

具体工厂角色

public class JsonConfigParserFactory implements IConfigParserFactory {@Overridepublic IRuleConfigParser createRuleParser() {return new JsonRuleConfigParser();}@Overridepublic ISystemConfigParser createSystemParser() {return new JsonSystemConfigParser();}
}public class XmlConfigParserFactory implements IConfigParserFactory {@Overridepublic IRuleConfigParser createRuleParser() {return new XmlRuleConfigParser();}@Overridepublic ISystemConfigParser createSystemParser() {return new XmlSystemConfigParser();}
}public class YamlConfigParserFactory implements IConfigParserFactory {@Overridepublic IRuleConfigParser createRuleParser() {return new YamlRuleConfigParser ();}@Overridepublic ISystemConfigParser createSystemParser() {return new YamlSystemConfigParser ();}
}public class PropertiesConfigParserFactory implements IConfigParserFactory {@Overridepublic IRuleConfigParser createRuleParser() {return new PropertiesRuleConfigParser ();}@Overridepublic ISystemConfigParser createSystemParser() {return new PropertiesSystemConfigParser ();}
}

客户端调用同工厂方法

模式对比

这里简单对比下工厂方法模式和抽象工厂模式,并整体回顾一下工厂模式的演变。通俗的说这个区别在于产品,如果产品单一,最合适用工厂模式,但是如果有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式

再通俗深化理解下:工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构。抽象工厂在只有一条产品线的时候退化为工厂方法模式。再看一个工厂模式的演进路线:

1 简单工厂模式

简单工厂模式不是 23 种里的一种,简而言之,就是有一个专门生产某个产品的类。比如下图中的鼠标工厂,专业生产鼠标,给参数 0,生产戴尔鼠标,给参数 1,生产惠普鼠标

2 工厂方法模式

工厂方法也就是鼠标工厂是个父类,有生产鼠标这个接口。戴尔鼠标工厂,惠普鼠标工厂继承它,可以分别生产戴尔鼠标,惠普鼠标。生产哪种鼠标不再由参数决定,而是创建鼠标工厂时,由戴尔鼠标工厂创建。后续直接调用鼠标工厂.生产鼠标()即可

3 抽象工厂模式

抽象工厂模式也就是不仅生产鼠标,同时生产键盘。也就是 PC 厂商是个父类,有生产鼠标,生产键盘两个接口。戴尔工厂,惠普工厂继承它,可以分别生产戴尔鼠标+戴尔键盘,和惠普鼠标+惠普键盘。创建工厂时,由戴尔工厂创建。后续工厂.生产鼠标()则生产戴尔鼠标,工厂.生产键盘()则生产戴尔键盘

在抽象工厂模式中,假设我们需要增加一个工厂,假设我们增加华硕工厂并且华硕也有自己的鼠标和键盘,则我们需要增加华硕工厂,和戴尔工厂一样,继承 PC 厂商,之后创建华硕鼠标,继承鼠标类。创建华硕键盘,继承键盘类即可。

在抽象工厂模式中,假设我们需要增加一个产品,假设我们增加耳麦这个产品,则首先我们需要增加耳麦这个父类,再加上戴尔耳麦,惠普耳麦这两个子类。之后在PC厂商这个父类中,增加生产耳麦的接口。最后在戴尔工厂,惠普工厂这两个类中,分别实现生产戴尔耳麦,惠普耳麦的功能。

总结一下

抽象工厂模式其实是工厂方法模式的升级版,其优点其实和工厂方法类似,都是将对象的创建解耦出来,不过抽象工厂模式的工厂能创建的不只是一个产品而是一组产品,这一组产品都有相同的产品类别,同组不同类的产品相互依赖和约束,例如一个工厂要生产红方旗,那么布料类产品必须是方形布料、染料类产品必须为红色染料,方形布料和红色染料在该工厂中就是绑定关系,该工厂也就约束了其生产的具体产品品类。

【Java设计模式 设计模式与范式】创建型模式 四:抽象工厂模式相关推荐

  1. 设计模式系列一创建型之(抽象工厂模式)

    1.抽象工厂简介 在软件系统中,经常面临着"一系列相互依赖的对象"的创建工作:同时由于需求的变化,往往存在着更多系列对象的创建工作. 如何应对这种变化? 如何绕过常规的对象的创建方 ...

  2. C++设计模式 | 四种创建型模式——简单工厂模式、工厂方法模式、抽象工厂模式、单例模式...

    简单工厂模式 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<string> using namespa ...

  3. Java设计模式(工厂模式>抽象工厂模式和原型模式)

    Java设计模式Ⅱ 1.工厂模式 1.1 简单工厂模式 1.2 工厂方法模式 2.抽象工厂模式 3.总结 4.原型模式 4.1 原型模式 4.2 浅拷贝 4.3 深拷贝 5.建造者模式 1.工厂模式 ...

  4. JAVA设计模式——工厂模式【简单工厂模式、工厂方法模式、抽象工厂模式】

    目录 简单工厂模式 传统方式 简单工厂模式 静态工厂模式 工厂方法模式 抽象工厂模式 工厂模式JDK-Calendar源码分析 工厂模式小结 简单工厂模式   看一个具体的需求 看一个披萨的项目:要便 ...

  5. Java设计模式之工厂方法模式与抽象工厂模式

    2019独角兽企业重金招聘Python工程师标准>>> 一.前期回顾 上一篇<Java设计模式之单例模式>详细介绍了单例模式,介绍了单例模式的使用场景,优缺点,同时也写了 ...

  6. 设计模式:工厂方式模式、抽象工厂模式

    前言 在面向对象程序设计中,工厂通常是一个用来创建其他对象的对象.工厂是构造方法的抽象,用来实现不同的分配方案. 工厂方法模式 工厂方法模式(英语:Factory method pattern)是一种 ...

  7. 设计模式——工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)

    声明: 本博客参考C语言中文网:C语言中文网连接 主要记录学习以下内容: 一.简单工厂模式 二.工厂方法模式 三.抽象工厂模式 每个大要点都主要包括以下两部分内容: (1)基本概念和模型结构(包括一些 ...

  8. 021 设计模式之工厂方法模式,抽象工厂模式的区别

    简单工厂模式,工厂方法模式和抽象工厂模式的异同 简单工厂模式,工厂方法模式和抽象工厂模式都是属于创建型设计模式,这三种创建型模式都不需要知道具体类.我们掌握一种思想,就是在创建一个对象时,需要把容易发 ...

  9. 设计模式之工厂方法模式、抽象工厂模式的概念和实现及使用“反射技术+读取配置文件”的方法对工厂模式进行改进(软件工程综合实践课程第三周个人作业)

    文章目录 一.实验目的 二.知识总结 1.工厂方法模式简介 2.抽象工厂模式简介 3.工厂模式小结 4.利用"反射技术+读取配置文件"的方法改进程序 三.实验内容 1. 工厂模式实 ...

  10. 设计模式之——简单(静态)工厂模式和抽象工厂模式

    文章目录 [关于设计模式] 设计模式的诞生 设计模式概念与解读 设计模式的原则 [关于工厂模式] 简单工厂(静态工厂)模式的概念 抽象工厂模式 工厂模式的使用时机和注意事项 给自己的思考 [关于设计模 ...

最新文章

  1. C# 引用类型和值类型
  2. PNAS:微生物组互作塑造宿主适应度
  3. 两个月入门深度学习,全靠动手实践!一位前端小哥的经验分享
  4. socket接收的消息怎么更新到页面_spring boot 集成 websocket 实现消息主动
  5. web 静态页面和动态页面的区别
  6. java修饰类的关键字_JAVA中的修饰关键字
  7. 徐起预热realme Q5系列:骁龙870+80W快充 新一代千元机皇
  8. ld 命令看内存布局 汇编级调试
  9. phpcms文章点击量统计方法
  10. 贝叶斯优化混合核极限学习机回归预测
  11. ifix组态软件研究控制按钮权限
  12. C/C++ 找出最大素数 算法
  13. win8连连看第二季1_第1季
  14. Workbench网格分块分区划分 扫面、节点控制网格数的应用
  15. 点击链接跳转到微信公众号关注页、微信关注链接
  16. 套头文件html重复,关于C++头文件重复包含的问题
  17. 小程序使用 企业微信客户服务插件(联系我) contactPlugin
  18. 电脑知识:电脑老是黑屏怎么解决?
  19. 计算机工程专业院校排名,2017美国计算机工程专业院校排名
  20. 用Python分析《斗破苍穹》

热门文章

  1. 基于angularjs的单页面实例_基于AngularJs的单页面程序
  2. VHDL语言设计8421码加法器(使用quartus)
  3. 推荐国外经典计算机教材9本
  4. STM32F407的USART
  5. leetcode数组相关简单习题,玉米迪迪的刷题之旅(*╹▽╹*)
  6. github搭建自己的博客网站
  7. 视频添加图片背景怎么操作
  8. 腾讯暑期实习面经验1
  9. 3D可视化信息管理平台让运维管理更高效率!
  10. 软件测试线上故障规范及模板