抽象工厂模式又称工具箱模式。其实抽象工厂模式可以简单的理解为一个工厂生成一个产品族的产品。

  抽象工厂模式可以向客户端提供一个接口,使得客户端在不指定产品的具体类型的情况下,创建多个产品族中的产品对象。这也是抽象工厂模式的用意。

  抽象工厂模式面对的是一个产品等级结构的系统设计。

  抽象工厂模式和工厂模式最大的区别就是:工厂模式针对的是一个产品等级结构,而抽象工厂针对的是多个产品等级结构。

产品族:

  产品族是指位于不同产品等级结构中,功能相关联的产品组成的家族。

    

抽象工厂模式:

  抽象工厂模式最早的应用是用于创建分属于不同操作系统的视窗构建。比如:命令按键(Button)与文字框(Text)都是视窗构件,在Unix和Windows操作系统的视窗环境中,这两个构件有不同的本地实现,它们的细节也有所不同。

其产品如下:

  可以发现在上面图中有两个产品的等级结构,分别是Button等级结构和Text等级结构。同时有两个产品族,也就是Unix产品族和Windows产品族。Unix产品族由UnixButton和UnixText构成,Windows产品族由WinButton和WinText构成,相图描述如下:

  系统对产品对象的创建需求由一个工厂的等级满足,其中有两个具体工厂角色,UnixFactory和WinFactory。UnixFactory对象创建Unix产品族的产品,WinFactory负责Windows产品族的产品。这就是抽象工厂模式的应用。对应图如下:

 

用工厂模式解决上面问题:

  试想如果我们用工厂模式,就需要写多个具体的工厂(UnixButtonFactory、UnixTextFactory、WinButtonFactory、WinTextFactory,如果将来产品等级增多就需要更多的具体工厂),每个工厂生产对应的产品,如果需要切换产品族需要切换整个产品族对应的工厂。比如原来生产的是Unix产品,使用的是UnixButtonFactory、UnixTextFactory,如果现在想切换Windows系列的产品就需要改成WinButtonFactory、WinTextFactory(如果产品等级增加需要切换更多的工厂)。而且增加新的产品族需要编写更多的工厂。

抽象工厂模式涉及到以下角色:

抽象工厂(Abstract Factory)角色:担任这个角色的是工厂方法模式的核心,它是与应用系统的商业逻辑无关的。通常使用Java接口或者类来实现,所有的具体工厂必须实现这个接口或继承这个抽象类。

具体工厂(Concrete Factory)类:这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适产品的逻辑,而这个逻辑是与应用系统的商业逻辑紧密相关的。

抽象产品(Abstract Product):担任这个角色的类是工厂方法模式所创建的对象的父类或它们共同拥有的接口。

具体产品(Concrete Product):由具体工厂创建的具体产品。这是客户端需要的东西,与与应用系统的商业逻辑紧密相关的。

UML类图如下:

源码如下:

package cn.qlq.absfactory;

public interface Button {

    void clickedCutton();
}
package cn.qlq.absfactory;

public class UnixButton implements Button {

    @Override
public void clickedCutton() {
System.out.println("点击 UnixButton ");
} }
package cn.qlq.absfactory;

public class WinButton implements Button {

    @Override
public void clickedCutton() {
System.out.println("点击 WinButton ");
} }
package cn.qlq.absfactory;

public interface Text {

    void clickedText();
}
package cn.qlq.absfactory;

public class UnixText implements Text {

    @Override
public void clickedText() {
System.out.println("点击 UnixText ");
} }
package cn.qlq.absfactory;

public class WinText implements Text {

    @Override
public void clickedText() {
System.out.println("点击 WinText ");
} }
package cn.qlq.absfactory;

public interface AbstractFactory {

    Button createButton();

    Text createText();
}
package cn.qlq.absfactory;

public class UnixFactory implements AbstractFactory {

    @Override
public Button createButton() {
return new UnixButton();
} @Override
public Text createText() {
return new UnixText();
} }
package cn.qlq.absfactory;

public class WinFactory implements AbstractFactory {

    @Override
public Button createButton() {
return new WinButton();
} @Override
public Text createText() {
return new WinText();
} }

测试代码:

package cn.qlq.absfactory;

public class MainClass {

    public static void main(String[] args) {
AbstractFactory unixFactory = new WinFactory(); Button button = unixFactory.createButton();
Text text = unixFactory.createText(); button.clickedCutton();
text.clickedText();
} }

 补充:如果有必要可以增加一个组合器进行组装生产工厂生产的产品,如下:

package cn.qlq.absfactory;

public class Combiner {

    public Combiner(AbstractFactory factory) {
// 生产产品
factory.createButton();
factory.createText(); // 用产品做后续处理
}
}

适用场景:

1.一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。

2.这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

3.同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。

4.系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

优缺点:

优点:

分离接口和实现:

  客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。

使切换产品族和增加产品组变得容易:

  因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从windows系列产品到unix系列产品只需要切换一下具体工厂。增加一系列产品也容易,比如增加一个mac系列产品则需要增加对应的mac产品与对应的macfactory即可。

缺点:

 增加和减少产品等级结构不容易:

  如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。比如所有的产品都需要增加一个label,则需要增加一个label产品等级并修改工厂接口与修改所有的具体工厂。这也违反了开闭原则。

  综合起来,抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供便利,而不能为产品等级结构的增加提供便利。

利用简单工厂改造上面的抽象工厂:

  我们尝试利用简单工厂改造上面的抽象工厂。UML类图如下:

产品类代码同上,工厂类代码如下:

package cn.qlq.simplefactory;

public class ProductFactory {

    private String productName;

    public ProductFactory(String productName) {
this.productName = productName;
} public Button createButton() {
Button button = null;
switch (productName) {
case "Win":
button = new WinButton();
break;
case "Unix":
button = new UnixButton();
break;
default:
break;
}
return button;
} public Text createText() {
Text text = null;
switch (productName) {
case "Win":
text = new WinText();
break;
case "Unix":
text = new UnixText();
break;
default:
break;
}
return text;
} public String getProductName() {
return productName;
} public void setProductName(String productName) {
this.productName = productName;
} }

测试代码:

package cn.qlq.simplefactory;

public class MainClass {

    public static void main(String[] args) {
String productName = "Win";
ProductFactory productFactory = new ProductFactory(productName); Button button = productFactory.createButton();
Text text = productFactory.createText(); button.clickedCutton();
text.clickedText();
} }

  上面有个缺点就是增加产品族的时候需要修改工厂

 利用反射+简单工厂改造:

修改工厂类采用反射创建对象:

  这里需要遵循一个约定:产品族的名称 + 抽象产品类名称构成具体类名称,比如产品族为Win,抽象产品为Button,则具体类为WinButton。

package cn.qlq.simplefactory;

public class ProductFactory {

    private String productName;

    public ProductFactory(String productName) {
this.productName = productName;
} public Button createButton() {
String packageName = "cn.qlq.simplefactory";
Button button = null;
try {
Class clazz = Class.forName(packageName + "." + productName + "Button");
button = (Button) clazz.newInstance();
} catch (Exception e) {
// 记录日志
throw new RuntimeException("非法参数异常");
} return button;
} public Text createText() {
String packageName = "cn.qlq.simplefactory";
Text text = null;
try {
Class clazz = Class.forName(packageName + "." + productName + "Text");
text = (Text) clazz.newInstance();
} catch (Exception e) {
// 记录日志
throw new RuntimeException("非法参数异常");
} return text;
} public String getProductName() {
return productName;
} public void setProductName(String productName) {
this.productName = productName;
} }

  这种方式就比较灵活了,将来扩展新的产品族的时候也比较方便,无需工厂类。

  一般最常用的就是反射 + 简单工厂。

抽象工厂(AbstractFactory)模式的更多相关文章

  1. 抽象工厂(AbstractFactory)模式-创建型模式

    1.new 的问题 常见的对象创建方法: //创建一个Road对象 Road road=new Road(); new的问题:实现依赖,不能应对具体实例的变化 怎么理解上面这句话呢? 可以这样理解:我 ...

  2. Android设计模式——抽象工厂方法模式

    1.抽象工厂方法模式:为了创建一组或相互依赖的对象提供一个接口,而不需要指定它们是具体类……无语 2.找个代码看看: 抽象工厂类<成员为接口> /** * 抽象工厂 * */ public ...

  3. factory工厂模式之抽象工厂AbstractFactory

    * 抽象工厂: 意图在于创建一系列互相关联或互相依赖的对象. * 每个工厂都会创建一个或多个一系列的产品 * 适用于:产品不会变动,开始时所有产品都创建好,然后根据分类获取想要的 某一类产品(很像sp ...

  4. Abstract Factory(抽象工厂)模式

    1.意图 提供一个创建一系列相关或相互依赖对象的接口,而无需制定它们具体的类. 2.适用性 一个系统要独立于它的产品创建.组合和表示时. 一个系统要由多个产品系列中的一个来配置时. 当你强调一系列相关 ...

  5. Java设计模式从精通到入门五 抽象工厂方法模式

    定义 抽象工厂类为创建一组相关和相互依赖的对象提供一组接口,而无需指定一个具体的类. ​ 这里我得把工厂方法模式得定义拿出来做一下比较:定义一个创建对象的接口,由子类决定实例化哪一个类.工厂方法是一个 ...

  6. 简单工厂,Factory Method(工厂方法)和Abstract Factory(抽象工厂)模式

    对于简单工厂来说,它的工厂只能是这个样子的 public class SimplyFactory {  /** * 静态工厂方法 */ public static Prouct factory(Str ...

  7. 抽象工厂方法模式(Abstract Factory Pattern)

    Provide an interface for creating families of related or dependent objects without specifying their ...

  8. java23种设计模式之九: 抽象工厂方法模式

    一.抽象工厂定义 上一讲我们说了一下工厂方法,那么我们如何对工厂进行抽象. 因为工厂是生产产品的,现在我们需要工厂抽象,只生产抽象产品,不生产具体的产品,这同时也体现了java的多态. 现在有2个抽象 ...

  9. FactoryMethod工厂方法模式升级成AbstractFactory抽象工厂模式

    具体参考抽象工厂(AbstractFactory)模式-创建型模式,本文在FactoryMethod工厂方法模式(创建型模式)的基础上进行业务进一步抽象,不做详细原理介绍. 1.在FactoryMet ...

随机推荐

  1. ava 类似jest snapshot 功能试用

    ava也提供了类似jest 的snapshot 测试,可以用来方便的测试web 组件,以下是一个简单的试用, 同时包含了自己碰到问题,以及解决方法,以及一些参考链接 使用typescript 以及ts ...

  2. SpringBoot要点之使用Actuator监控

    Actuator是Springboot提供的用来对应用系统进行自省和监控的功能模块,借助于Actuator开发者可以很方便地对应用系统某些监控指标进行查看.统计等. 在pom文件中加入spring-b ...

  3. jupyter的补充

    目录 jupyter 的使用 常用命令模式快捷键: 常用编辑模式快捷键: jupyter 的使用 Cells状态分为命令模式和编辑模式,Enter进入编辑模式,ESC进入命令模式,命令模式和编辑模式下 ...

  4. 第09组 Alpha冲刺(1/6)

    队名:观光队 组长博客 作业博客 组员实践情况 王耀鑫 过去两天完成了哪些任务 文字/口头描述 完成服务器连接数据库部分代码 展示GitHub当日代码/文档签入记录 接下来的计划 与服务器连接,配合前 ...

  5. 报表导出之easypoi的应用

    报表导出有很多种方法,像之前我有写过的jxl,poi,jasperreport又或者各种商业软件,这次来简单介绍下用了许久的开源轮子easypoi. easypoi的底层原理就不介绍了.因为官方文档的 ...

  6. mybatis多参数查询问题:org.apache.ibatis.binding.BindingException: Parameter 'name' not found. Available par

    错误如下:  这个是由于在执行sql的时候无法匹配sql语句的通配符造成的,有两种方式可以解决 第一种:在sql语句中通配符这样写,用0,1匹配:  第二种:在dao接口的方法中的参数前面加param ...

  7. mac mysql 使用注意事项

    mac mysql 使用注意事项 .安装 直接通过下载官网上的dmg安装包进行安装,mysql--osx10.-x86_64(我的安装文件) ,安装完成后在系统偏好设置里面有mysql选项,我们可以通 ...

  8. Android Studio 之 数据存活【2】,返回桌面切换回来,内容还保存着

    使用ViewMode SavedState来使得ViewModel在系统杀死后台进程的时候得以存活. 本集使用到的库,需要在gradle中添加: implementation 'androidx.li ...

  9. 出现 java.lang.OutOfMemoryError: PermGen space 错误的原因及解决方法

    一.原因及解决方法[1] 1.原因:堆内存的永久保存去区内存分配不足(缺省默认为64M),导致内存溢出错误. 2.解决方法:重新分配内存大小,-Xms1024M -Xmx2048M -XX:PermS ...

  10. 【mybatis源码学习】缓存机制

    一.mybatis的缓存 一级缓存:sqlsession级别,默认开启(一个事务内有效)二级缓存:  sqlsessionFactory级别,需要手动开启,在xml配置cache节点(依赖事务的执行结 ...