【设计模式 - 1】之工厂模式(Factory)
1、模式简介
工厂模式的作用:
- 工厂模式解决的是“使用new关键字获取对象造成松耦合”的问题。
- 工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
工厂模式可以分为三类:
- 简单工厂模式(SimpleFactory)
- 工厂方法模式(FactoryMethod)
- 抽象工厂方法(AbstractFactory)
- 注:这里将简单工厂模式看作工厂方法模式的一个特例
工厂方法模式:
- 一个抽象产品类,可以派生出多个具体产品类;
- 一个抽象工厂类,可以派生出多个具体工厂类;
- 每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:
- 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类;
- 一个抽象工厂类,可以派生出多个具体工厂类;
- 每个具体工厂类可以创建多个具体产品类的实例。
模式区别:
- 工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个;
- 工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。
2、简单工厂模式
2.1、简介
简单工厂模式的工作流程如上图所示:汽车工厂中有创建汽车的方法,用户只需要将汽车的型号作为参数传到工厂中(告诉工厂自己想要造哪种型号的汽车),工厂会根据参数创建不同的汽车对象后返回给用户。
可见,在工厂模式中,用户不需要知道汽车具体是如何制造的,他们只需要告诉工厂自己想要哪个型号的汽车,其他的工作交给工厂去做即可。
简单工厂模式主要包括三部分:
- 工厂角色:相当于上图中的FactoryBMW,具有一定的判断逻辑;
- 抽象产品角色:相当于上图中的BMW,一般是具体商品继承的父类后实现的接口;
- 具体产品角色:相当于上图中的BMW532和BMW320,是抽象产品类的子类或实现类。
2.2、分析
设计模式的最终目标:让程序“对扩展开放,对修改关闭(开闭原则)”。
当有新的车型加入工厂生产线后,不仅需要新加一个新的车型类,还需要在工厂中改变车型的判断逻辑,这是不符合开闭原则的。
要解决这个问题,需要工厂方法模式。
2.3、代码
程序目录架构:
抽象汽车产品类BMW.java类中的代码:
public abstract class BMW {
public abstract void introduce();
}
具体汽车产品类BMW523.java和BMW320.java中的代码(以BMW523.java为例):
public class BMW523 extends BMW {
public BMW523() {
}
@Override
public void introduce() {
System.out.println("这是一辆BMW523型号车");
}
}
汽车工厂类FactoryBMW.java中的代码:
public class FactoryBMW {
public static final int TYPE_BMW523 = 1;
public static final int TYPE_BMW320 = 2;
public static BMW createBMW(int type) {
switch (type) {
case TYPE_BMW523:
return new BMW523();
case TYPE_BMW320:
return new BMW320();
}
return null;
}
}
测试类Test.java中的代码:
public class Test {
private static BMW bmw;
public static void main(String[] args) {
Scannerscanner = new Scanner(System.in);
System.out.println("请选择要生产的汽车的型号:");
System.out.println("1、BMW 523");
System.out.println("2、BMW 320");
int type = scanner.nextInt();
bmw = FactoryBMW.createBMW(type);
if (bmw != null) {
bmw.introduce();
}else {
System.out.println("没有这个车型!");
}
scanner.close();
}
}
运行结果:
3、工厂方法模式
3.1、简介
工厂方法模式的工作流程如上图所示:将简单工厂模式中的工厂类FactoryBMW定义成接口,每种车型都定义一个新的工厂类,用来专门生产BMW类的某个车型子类。这样,工厂可以扩展,就不需要修改原来的代码了。
工厂方法模式主要包括四部分:
- 抽象工厂角色:相当于上图中的FactoryBMW,是所有具体工厂必须实现或继承的接口或父类;
- 具体工厂角色:相当于上图中的FactoryBMW320和FactoryBMW523,它们含有具体的与业务逻辑有关的代码,用来创建具体的产品对象;
- 抽象产品角色:相当于上图中的BMW,是具体产品的接口或父类;
- 具体产品角色:相当于上图中的BMW523和BMW320,是具体的产品类。
3.2、分析
相比于简单工厂模式用一个工厂来创建所有车型,工厂方法模式将工厂生产汽车的方法下放到其子类中,分担了工厂类承担的压力。
当有新的车型加入工厂生产线时,只需要创建具体的工厂类和车型类即可,不需要修改现有的代码,让程序变得更加灵活,可见,工厂方法模式是符合开闭原则的。
但是工厂方法模式也有一定的弊端:这种模式使得类的数量成倍增长,当有很多车型时,就会有很多工厂类来生产这些车型,这不是我们所希望的。这个问题的一个解决方案是将工厂方法模式与简单工厂模式结合使用,创建方法比较相似的车型合并到一个简单工厂中创建,创建方法差别较大的车型使用工厂方法模式分到不同的工厂中创建。
工厂方法模式已经比较完美的对对象进行了封装,但也不是意味着我们要对程序中的所有对象都采用工厂方法模式。
在以下情况下可以考虑使用工厂方法模式:
- 当客户不需要知道要使用的对象的创建过程时;
- 当客户要使用的对象存在变动的可能,或者根本就不知道要使用哪个对象时。
3.3、代码
程序目录框架:
抽象的工厂类FactoryBMW.java中的代码:
public abstract class FactoryBMW {
public abstract BMW createBMW();
}
具体的工厂类FactoryBMW320.java和FactoryBMW523.java中的代码(这里以FactoryBMW523.java为例):
public class FactoryBMW523 extendsFactoryBMW {
@Override
public BMW createBMW() {
return new BMW523();
}
}
测试类Test.java中的代码:
public class Test {
private static BMW bmw;
private static FactoryBMW factory;
public static void main(String[] args) {
Scannerscanner = new Scanner(System.in);
System.out.println("请选择要生产的汽车的型号:");
System.out.println("1、BMW 523");
System.out.println("2、BMW 320");
int type = scanner.nextInt();
switch (type) {
case 1:
factory = new FactoryBMW523();
break;
case 2:
factory = new FactoryBMW320();
break;
default:
factory = null;
break;
}
if (factory != null) {
bmw = factory.createBMW();
bmw.introduce();
}else {
System.out.println("没有这个车型!");
}
scanner.close();
}
}
运行结果如下图:
3.4、扩展
从上面的代码我们可以看到:工厂方法模式其实并没有做到避免代码的修改,而是将原来需要在工厂中进行的逻辑判断拿到了测试类中进行,这是因为我们仍然无法判断具体需要调用哪个工厂。面对这种情况,我们可以使用反射机制来在一定程度上进行解决。具体代码如下:
public class Test {
private static BMW bmw;
private static final String[] classes = {
"com.itgungnir.designpattern.factory.factorymethod.FactoryBMW523",
"com.itgungnir.designpattern.factory.factorymethod.FactoryBMW320" };
public static void main(String[] args) {
Scannerscanner = new Scanner(System.in);
System.out.println("请选择要生产的汽车的型号:");
System.out.println("1、BMW 523");
System.out.println("2、BMW 320");
int type = scanner.nextInt();
try {
Class<?>factory = Class.forName(classes[type - 1]);
MethodcreateBMW = factory.getMethod("createBMW");
bmw = (BMW) createBMW.invoke(factory.newInstance());
}catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (NoSuchMethodException e) {
e.printStackTrace();
}catch (SecurityException e) {
e.printStackTrace();
}catch (IllegalAccessException e) {
e.printStackTrace();
}catch (IllegalArgumentException e) {
e.printStackTrace();
}catch (InvocationTargetException e) {
e.printStackTrace();
}catch (InstantiationException e) {
e.printStackTrace();
}
if (bmw != null) {
bmw.introduce();
}else {
System.out.println("没有这个车型!");
}
scanner.close();
}
}
这样,当有新的车型和工厂加入到生产线之后,除了创建具体工厂类和具体车型类,只需要在字符串数组classes中加入新创建的具体工厂类的包地址即可。这样可以将代码的改动降到最小。
注:关于反射机制的介绍可以参考:【JAVA - 基础】之反射的原理与应用
4、抽象工厂模式
4.1、简介
抽象工厂模式的工作流程和工厂方法模式的工作流程基本相似,唯一的不同是一个工厂生产的是一个产品的不同组件。下图中,一个具体工厂除了可以生产汽车之外,还可以生产与该型号汽车对应的空调。(本DEMO的前提是每个车型的汽车都有一款对应的空调,一个车型的汽车不能搭载另一个车型的汽车的空调)
抽象工厂模式的组成部分和工厂方法模式的组成部分相同。
4.2、分析
从上图中可以看到,抽象工厂模式的适用场景是系统中存在多个系列的产品(如汽车和空调)。实际上,抽象工厂模式和工厂方法模式的区别就在于需要创建的对象的复杂程度。用这个例子来说,就是当我们只需要生产汽车的时候,就可以选择工厂方法模式;当我们既需要生产汽车又需要生产空调时,就可以选择抽象工厂模式。
用一句话概括抽象工厂方法:给客户端提供一个接口,可以创建多个产品族中的产品对象。
4.3、代码
程序目录框架:
空调抽象类AC.java中的代码:
public abstract class AC {
public abstract void introduce();
}
具体的空调类AC523.java和AC320.java中的代码(这里以AC523.java为例):
public class AC523 extends AC {
@Override
public void introduce() {
System.out.println("BMW523型号的汽车成功搭载了AC523型号的空调");
}
}
抽象的工厂接口Factory.java中的代码:
public interface Factory {
BMWcreateBMW();
ACcreateAC();
}
具体的工厂类Factory523.java和Factory320.java中的代码(这里以Factory523.java为例):
public class Factory523 implements Factory {
@Override
public BMW createBMW() {
return new BMW523();
}
@Override
public AC createAC() {
return new AC523();
}
}
测试类Test.java中的代码:
public class Test {
private static BMW bmw;
private static AC ac;
private static final String[] classes = {
"com.itgungnir.designpattern.factory.abstractfactory.Factory523",
"com.itgungnir.designpattern.factory.abstractfactory.Factory320" };
public static void main(String[] args) {
Scannerscanner = new Scanner(System.in);
System.out.println("请选择要生产的产品的型号:");
System.out.println("1、523型号");
System.out.println("2、320型号");
int type = scanner.nextInt();
try {
Class<?>factory = Class.forName(classes[type - 1]);
MethodcreateBMW = factory.getMethod("createBMW");
MethodcreateAC = factory.getMethod("createAC");
bmw = (BMW) createBMW.invoke(factory.newInstance());
ac = (AC) createAC.invoke(factory.newInstance());
}catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (NoSuchMethodException e) {
e.printStackTrace();
}catch (SecurityException e) {
e.printStackTrace();
}catch (IllegalAccessException e) {
e.printStackTrace();
}catch (IllegalArgumentException e) {
e.printStackTrace();
}catch (InvocationTargetException e) {
e.printStackTrace();
}catch (InstantiationException e) {
e.printStackTrace();
}
if (bmw != null && ac!=null) {
bmw.introduce();
ac.introduce();
}else {
System.out.println("没有这个型号的产品!");
}
scanner.close();
}
}
运行结果如下图所示:
5、后记
这个帖子中的所有例子都很简单,都是直接创建的,这样还不能把工厂模式淋漓尽致的体现出来。设想,如果一个产品的创建需要很多步骤,中间会产生很多很多的对象,而这些对象最终都没有返回(即这些对象对我们最终想要获得的对象只是辅助作用,我们不需要获取这些对象)。在这种情况下,我们如果每获取一个对象都调用这一套方法的话无疑是非常耗时耗力的。
因此,工厂模式不仅对“new”进行了封装,也在一定程度上对对象的创建过程进行了封装。使用工厂模式,可以得到更松耦合、更有弹性的设计。
最后总结一下工厂模式不同类别的定义:
- 工厂方法模式:定义一个创建对象的接口或抽象类,由实现类或子类决定要实例化的类是哪一个。工厂方法模式让类把实例化的代码推迟到子类中进行;
- 抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
最后贴出工厂模式的GitHub地址:【GitHub - Factory】。
【设计模式 - 1】之工厂模式(Factory)的更多相关文章
- 设计模式(一)工厂模式Factory(创建型)
设计模式一 工厂模式Factory 在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的.可是在一些情况下, new操作符直接生成对象会带来一些问题. ...
- 设计模式(一)工厂模式Factory(创建类型)
设计模式一 工厂模式Factory 在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的.可是在一些情况下, new操作符直接生成对象会带来一些问题. ...
- 乐在其中设计模式(C#) - 抽象工厂模式(Abstract Factory Pattern)
原文:乐在其中设计模式(C#) - 抽象工厂模式(Abstract Factory Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 抽象工厂模式(Abstract Factor ...
- 设计模式(一)工厂模式Factory(创建型)(转)
原文链接:http://blog.csdn.net/hguisu/article/details/7505909 设计模式一 工厂模式Factory 在面向对象编程中, 最通常的方法是一个new操作符 ...
- 设计模式之简单工厂模式(Simple Factory)
原文地址:http://www.cnblogs.com/BeyondAnyTime/archive/2012/07/06/2579100.html 今天呢,要学习的设计模式是“简单工厂模式”,这是一个 ...
- 设计模式系列之工厂模式三兄弟(Factory Pattern)
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- 设计模式 - 工厂模式(factory pattern) 具体解释
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u012515223/article/details/27081511 工厂模式(factory pa ...
- 桥接模式及C++实现 C++设计模式-AbstractFactory抽象工厂模式
桥接模式及C++实现 桥接模式 先说说桥接模式的定义:将抽象化(Abstraction)与实现化(Implementation)分离,使得二者可以独立地变化. 桥接模式号称设计模式中最难理解的模式之一 ...
- java设计模式---三种工厂模式
工厂模式提供创建对象的接口. 工厂模式分为三类:简单工厂模式(Simple Factory), 工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory).GOF在 ...
- Jquery如何序列化form表单数据为JSON对象 C# ADO.NET中设置Like模糊查询的参数 从客户端出现小于等于公式符号引发检测到有潜在危险的Request.Form 值 jquery调用iframe里面的方法 Js根据Ip地址自动判断是哪个城市 【我们一起写框架】MVVM的WPF框架(三)—数据控件 设计模式之简单工厂模式(C#语言描述)
jquery提供的serialize方法能够实现. $("#searchForm").serialize();但是,观察输出的信息,发现serialize()方法做的是将表单中的数 ...
随机推荐
- Frameset布局
<FRAMESET> <FRAME> <NOFRAMES> <IFRAME> ■ 框架概念 : 所谓框架便是网页画面分成几个框窗,同时取得多个 URL. ...
- 读书笔记之 - javascript 设计模式 - 代理模式
代理(proxy)是一个对象,它可以用来控制对另一对象的访问.它与另外那个对象实现了同样的接口,并且会把任何方法调用传递给那个对象.另外那个对象通常称为本体.代理可以代替本体被实例化,并使其可被远程访 ...
- 基于管道通知的百万并发长连接server模型
0.前言 最近突然想了解怎样设计一个支持百万连接的后台server架构. 要设计一个支持百万连接的后台server,我们首先要知道会有哪些因素限制后台server的高并发连接,这里想到的因素有以下几点 ...
- socket+网路编程
1.网络编程: 通过某种计算机语言来实现不同设备间的资源共享和信息传递.计算机网络的创造可能比计算机本身意义更重大!!!(否则,你只能玩单机版游戏 OSI模型 OSI模型定义了不同计算机互联的标准,是 ...
- 《C和指针》章节后编程练习解答参考——第8章
8.1 #include <stdio.h> int main (void) { int a, b, c, d; // 不使用嵌套花括号初始化 unsigned ][][][] = { , ...
- 在oj平台上练习的一些总结【转】
程序书写过程中的一些小技巧:1. freopen(“1.txt”,”r”,stdin); //程序运行后系统自动输入此文档里面的内容(不需要进行手动输入)freopen(“1.txt”,”w”,std ...
- jackson学习----解析豆瓣的图书信息
异常一. org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple ...
- Greg and Array
Codeforces Round #179 (Div. 2) C:http://codeforces.com/problemset/problem/296/C 题意:给你一个序列,然后有两种操作,第一 ...
- Class.forName()的理解
转自:http://blog.csdn.net/yanwushu/article/details/7574713 使用jdbc方式连接数据库时会使用一句代码Class.forName(String c ...
- latch free
latch free 等待事件: latch: cache buffers chains 这个等待事件其实还有另外一个重要的原因,那么就是逻辑读太高,SQL执行计划走错了导致的. 当进程想要获取锁存器 ...