1、工厂方法模式

1.1、什么是工厂模式

在了解工厂方法模式前,我们先了解一下什么是工厂模式。

  • 定义:工厂模式,是一种创建型设计模式,它提供了在超类中创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类中。

我们把被创建的(目标)对象称为“产品”,把创建产品的对象称为“工厂”。

我们为什么需要工厂模式呢?归根结底就是为了解耦。通俗的说,工厂就是用来封装创建对象的代码,负责处理对象细节的类被称为工厂,工厂模式实现了创建者和调用者的分离。

我们在学习依赖倒置原则的时候其实就提到过简单工厂模式。按照实际业务场景划分,工厂模式有三种不同的实现方式,分别是:简单工厂模式、工厂方法模式和抽象工厂模式。不过,在 GoF 的《设计模式》一书中,它将简单工厂模式看作是工厂方法模式的一种特例,所以工厂模式只被分成了工厂方法和抽象工厂两类。工厂模式在实际项目中还是比较常用的。

要分清楚这三者的关系,首先我们先了解两个概念,什么是产品等级?什么是产品族?

产品等级:即有继承结构的同种类的产品,我们称之为产品等级。比如:苹果手机、小米手机。

产品族:一个具体工厂生产的不同等级的一组产品称为一个产品族。比如:小米手机、小米扫地机等。

如果分别用一句话来概括他们,那就是:

​ 1)简单工厂:生产同一产品等级的任意指定的产品。(不支持增加新产品,增加新产品需要修改代码)

​ 2)工厂方法:生产同一产品等级的固定工厂产品。(支持增加新产品)

​ 3)抽象工厂:生产不同产品族的全部产品。(支持增加产品族,但不支持增加新产品)

1.2、什么是简单工厂模式

既然简单工厂,是工厂方法的一种特例。那我们就来聊聊什么是简单工厂。

  • 定义:简单工厂模式也叫静态工厂模式,就是工厂类(一般使用静态方法)通过接收的参数来区分并返回不同的对象实例。

我们先来看一个案例:比亚迪和特斯拉都是能在路上跑的车。

interface Car{void run();
}
class Byd implements Car{@Overridepublic void run() {System.out.println("比亚迪在路上跑");}
}class Tsl implements Car{@Overridepublic void run() {System.out.println("特斯拉在路上跑");}
}

这样写我们现在的UML图关系是:

在没有工厂的情况下,我们要让比亚迪或者特斯拉出来跑的话。我们会这样写:

public class CarUse {public static void main(String[] args) {Car c1 = new Byd();Car c2 = new Tsl();c1.run();c2.run();}
}

​ 此时的UML关系图:

此时的调用者(CarUse类)和创建者Byd和Tsl都是有关联的,调用者什么都是亲力亲为。

我们知道,工厂模式就是要分离调用者和创建者。现在我们创建一个简单工厂,由简单工厂类去统一管理创建者。

//写法一
public class CarFactory {static final int CAR_BYD = 0;static final int CAR_TSL = 1;public static Car createCar(int type){switch (type) {case 0:return new Byd();case 1:return new Tsl();default:System.out.println("无法识别的品牌");return null;}}
}
//写法二
public class CarFactory {private static final Map<String,Car> createCar = new HashMap<String,Car>();static{createCar.put("byd", new Byd());createCar.put("tsl", new Tsl());}public static Car createCar(String type){if(type==null){return null;}return createCar.get(type);}}

​ 此次的UML关系图:

虽然我们多写了一个CarFactory工厂类,整体上的代码变多了一点点。但是我们的调用者不必依赖我们的创建者,仅仅需要告诉工厂你要什么,无需知道工厂去哪里帮你实现你的需求,这样调用者就省去了很多的事情。

​    后来生意做红火了,来了个有钱的客户,这次要买玛莎拉蒂,也就是新产品了。我们的工厂就不适用了,我们的工厂需要找到玛莎拉蒂的实体,然后在我们的工厂上加入新的品牌,不修改代码是无法扩展了。

​    简单工厂的弊端:每增加一个产品就要增加一个具体产品类和修改工厂类,这增加了系统的复杂度,违背了“开闭原则”。

1.3、工厂方法模式

​    虽然简单工厂模式解决了调用者和创建者之间的耦合,但是工厂和创建者之间依然存在着耦合。这样的设计违背了我们的“开闭原则”,未来新产品的扩展并不灵活。而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码(比如工厂类)的情况下引进新的产品,即满足开闭原则。我们的工厂方法模式就是做这个的。

//声明一个抽象工厂
interface CarFactory {Car createCar();
}
//比亚迪工厂
public class BydFactory implements CarFactory{@Overridepublic Car createCar() {return new Byd();}
}
//特斯拉工厂
public class TslFactory implements CarFactory{@Overridepublic Car createCar() {return new Tsl();}
}
//调用者
public class CarUse {public static void main(String[] args) {Car c1 = new BydFactory().createCar();Car c2 = new TslFactory().createCar();c1.run();c2.run();}
}
//工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式(对于一个项目或者一个独立的模块而言)只有一个工厂类,而工厂方法模式有一组实现了相同接口的工厂类。调用者不是什么都找一个工厂,而是需要什么就找什么工厂提供,这样可选择的范围更大。

​ 此时的UML关系图:

​ 现在,我们的新客户要玛莎拉蒂,就可以直接找我们的玛莎拉蒂工厂。我们只需要有新建的玛莎拉蒂工厂即可满足客户的要求。

//新建玛莎拉蒂工厂
public class MsldFactory implements CarFactory{@Overridepublic Car createCar() {return new Msld();}}class Msld implements Car{@Overridepublic void run(){System.out.println("玛莎拉蒂在路上跑");}
}

我们的客户,只需要找到这家玛莎拉蒂工厂即可。

public class CarUse {public static void main(String[] args) {Car c1 = new BydFactory().createCar();Car c2 = new TslFactory().createCar();c1.run();c2.run();Car c3 = new MsldFactory().createCar();c3.run();}
}

我们增加新的产品,不需要去修改我们的工厂类,工厂方法模式不违背开闭原则,扩展性非常良好。

1.4、工厂方法模式的优缺点

  • 优点

​    1)用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;

​ 2)对于新产品的创建,只需多写一个相应的工厂类,不会影响已有的代码,后期维护容易,增强系统的扩展性。

  • 缺点

类的个数容易过多,增加系统的复杂度和理解难度。

注:从结构的复杂度、代码的复杂度以及客户端调用者的编程

1.5、总结及建议

​    简单工厂模式和工厂方法模式的比较:

比较维度 简单工厂模式 工厂方法模式 说明
结构、代码复杂度 简单工厂模式占优,只需要一个工厂类,而工厂方法模式会随着工厂的数量增加而增加
调用者使用难度 简单工厂模式的工厂类是静态类,调用者无需实例化
扩展性 工厂方法模式满足OCP原则,具有非常良好的扩展性。简单工厂的扩展性稍弱,但是只要改动不大产品不是特别复杂,简单工厂模式无疑是更优的选择。

​ 注:事实上我们有时并不需要像一些框架一样做到那么灵活,反而一般都用简单工厂模式,这样代码简洁逻辑也清晰,可以使用

​ 应用场景:

​ 1)对于产品种类相对较少的情况,考虑使用简单工厂模式;

​ 2)当您想为您的库或框架的用户提供一种扩展其内部组件的方法时,请使用工厂方法模式。

​ DJK中工厂模式的使用:

​ 1)java.util.Calendar中的getInstance()方法使用了工厂模式。

2、抽象工厂模式

​    我们之前的工厂方法中,针对的是一种产品等级——汽车。我们都知道,现实生活中的场景比我们想象的要更复杂?为了产品细分和精准的客户定位,比亚迪工厂要给汽车装配不同的配饰,来做产品细分。比如:在高配版的比亚迪中装配高级座椅、高级轮胎和高级发动机;在低配版本的比亚迪中装配低级座椅、低级轮胎和低级发动机;这就形成了产品族的概念,装配有座椅、轮胎和发动机,并不是一种产品了。

​    针对不同的产品族,如果还想沿用继承思想的话,那会使得整体的结构变得非常复杂。且工厂个数非常庞大。

​ 这种玩法是不是有点熟悉,我们在讲合成复用原则的时候就遇到过这种问题。这里的抽象工厂模式,就是用来解决这个问题的。

2.1、什么是抽象工厂模式

  • 定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

    简单的说:就是用来生产不同产品族的全部产品的,抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。

2.2、模式的优缺点

  • 优点

​ 抽象工厂模式是“工厂的工厂”,可以轻松扩展以容纳更多产品。

  • 缺点

​ 代码会变得比应有的更复杂,因为随着模式引入了许多新的接口和类。

2.3、创建的方式

​    首先分析问题,产品族不是同一类型的产品我们使用工厂方法模式的继承思想并不能很好的解决问题。前面我们也学到了合成复用原则。对于产品族而言,更适合使用组合去实现对工厂的进一步抽象。

//----首先写我们的产品族,轮胎、座椅、发动机
interface Tyre{//轮胎接口void rotate(); //抓地摩擦力
}class HighTyre implements Tyre{//高端轮胎@Overridepublic void rotate() {System.out.println("高端轮胎抓地摩擦力好");}
} class LowTyre implements Tyre{//低端轮胎@Overridepublic void rotate() {System.out.println("低端轮胎抓地摩擦力一般");}
}interface Seat{//座椅接口void material(); //原材料
}class HighSeat implements Seat{//高端座椅@Overridepublic void material() {System.out.println("高端座椅真皮柔软");}
} class LowSeat implements Seat{//低端座椅@Overridepublic void material() {System.out.println("低端座椅假皮不柔软");}
}interface Engine{//发动机void speed(); //转速
}class HighEngine implements Engine{//高端发动机@Overridepublic void speed() {System.out.println("高端发动机转速快");}
} class LowEngine implements Engine{//低端座椅@Overridepublic void speed() {System.out.println("低端发动机转速一般");}
}//----在构造组合这些产品的工厂
interface BydFactory{ //比亚迪工厂Tyre addTyre();  //装配轮胎Seat addSeat(); //装配座椅Engine addEngine(); //装配发动机
}//高端比亚迪工厂
class HighBydFactory implements BydFactory{@Overridepublic Engine addEngine() {return new HighEngine();}@Overridepublic Seat addSeat() {return new HighSeat();}@Overridepublic Tyre addTyre() {return new HighTyre();}
}//低端比亚迪工厂
class LowBydFactory implements BydFactory{@Overridepublic Engine addEngine() {return new LowEngine();}@Overridepublic Seat addSeat() {return new LowSeat();}@Overridepublic Tyre addTyre() {return new LowTyre();}
}
  • 测试

//--测试
public class BydAbstractFactoryDemo {public static void main(String[] args) {System.out.println("高端比亚迪工厂----------------");BydFactory high = new HighBydFactory();Engine e = high.addEngine();e.speed();Seat s = high.addSeat();s.material();Tyre t = high.addTyre();t.rotate();System.out.println("低端比亚迪工厂----------------");BydFactory low = new LowBydFactory();low.addEngine().speed();low.addSeat().material();low.addTyre().rotate();}
}
  • 案例效果

​ 此时的UML关系图:

2.4、总结及建议

​    抽象工厂模式,适合比较复杂的情形,一般我们用的比较少。

应用场景

  • 抽象工厂模式是“工厂的工厂”,抽象工厂设计模式为接口而不是实现提供了代码方法,抽象工厂模式是健壮的,避免了简单工厂模式的条件逻辑;

  • 当您的代码需要与各种相关产品系列一起使用时,请使用抽象工厂;

DJK中抽象工厂模式的使用:

  • javax.xml.parsers.DocumentBuilderFactory#newInstance()

  • javax.xml.transform.TransformerFactory#newInstance()

  • javax.xml.xpath.XPathFactory#newInstance()

重学设计模式(三、设计模式-工厂模式)相关推荐

  1. 设计模式三—抽象工厂模式

    设计模式三-抽象工厂模式 一.定义 抽象工厂模式是工厂方法模式的进一步抽象.如果产品簇中只有一种产品,则退化为工厂方法模式. 二.原理图 三.代码实例 * 苹果和土豆是园丁1的杰作 * 葡萄和西红柿是 ...

  2. java设计模式---三种工厂模式

    工厂模式提供创建对象的接口. 工厂模式分为三类:简单工厂模式(Simple Factory), 工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory). GOF ...

  3. java设计模式---三种工厂模式之间的区别

    简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式.其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性. 本文是本人对这三种模式学习后的一个小结以及对他 ...

  4. 浅析设计模式(三)——抽象工厂模式

    抽象工厂模式(Abstract-Factory,创建型模式) 本文的结构: 一.抽象工厂模式的定义 二.抽象工厂模式的参与者及其角色 三.抽象工厂模式的类图 四.抽象工厂模式的示例 五.参考 一.抽象 ...

  5. java 用映射写工厂类_java23种设计模式——三、工厂模式

    目录 工厂模式 工厂模式介绍 工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式.著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见.因为 ...

  6. mysql工厂模式_设计模式-三种工厂模式实例

    1.简单工厂模式:代替new产生对象,产品的类型比较少时. 我们要获得三种不同的数据库对象,如Mysql,SQLserver,Oracle,它们拥有共同的特征,即可以进行抽象,简单工厂目的是将获得具体 ...

  7. 设计模式学习笔记(三)工厂模式中的简单工厂、工厂方法和抽象工厂模式之间的区别

    设计模式中的工厂模式(Factory Design pattern)是一个比较常用的创建型设计模式,其中可以细分为三种:简单工厂(Simple Factory).工厂方法(Factory Method ...

  8. 设计模式之四(抽象工厂模式第三回合)

    原文:设计模式之四(抽象工厂模式第三回合) 前言 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 抽象工厂模式最大的好处便是易于交换产品系列,由于具体工厂类,例如I ...

  9. 设计模式系列——三个工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)...

    转自:http://www.cnblogs.com/stonehat/archive/2012/04/16/2451891.html 设计模式系列--三个工厂模式(简单工厂模式,工厂方法模式,抽象工厂 ...

  10. 从零开始学设计模式(四):工厂模式(Factory Pattern)

    作者平台: | CSDN:blog.csdn.net/qq\_4115394- | 掘金:juejin.cn/user/651387- | 知乎:www.zhihu.com/people/1024- ...

最新文章

  1. 罗格斯大学电气与计算机工程专业怎么样,美国电子工程排名 - 电子计算机工程的研究生教育,特别是偏向电路设计方向,请问是美国罗格斯大学新布朗斯维克校区好还是清华...
  2. 基于OpenCV的图像阴影去除
  3. OpenGL Distance Field文本的实例
  4. linux syslog 删除文件_Linux没有当心删除日记文件syslog怎样办
  5. 使用函数求余弦函数的近似值_WPS-Excel表格实用技巧-排名函数RANK函数的使用
  6. iOS 设计模式浅析 0 - 前言
  7. MySQL 数据类型转化
  8. raptor累乘流程图_程序设计基础
  9. 安卓adb是什么?ADB命令大全及使用教程
  10. 操作系统笔记 第二章
  11. Html5 Egret游戏开发 成语大挑战(一)开篇
  12. 叶念琛告诉你什么是爱情。。。
  13. SpringBoot整合邮件发送功能
  14. 互联网企业数据应用BI建设全流程解读!
  15. 哪种性格的人,更适合转管理?
  16. 【bzoj1003】[ZJOI2006]物流运输trans 最短路+dp
  17. MySQL数据库对象管理
  18. 在云边公益——互联网金融与公益的完美结合
  19. windows下使用pycharm+pyinstaller生成可执行文件
  20. gwr模型用什么做_为什么我的 CV 模型不好用?没想到原因竟如此简单……

热门文章

  1. JS实现的淘宝购物车
  2. [求助贴]python图片全屏截取+定位坐标+图片识别
  3. 对称加密和非对称加密总结
  4. Unity 触摸屏旋转和缩放
  5. 走进MSTP -- 3. Optix OSN 500/550/580硬件概览
  6. 将普通图片转化为字符画(Python)
  7. vue中 关于$emit的用法
  8. 5G和高性能电子产品需求带动,我国高频高速覆铜板市场快速发展
  9. 中兴通讯总裁现身央视《对话》 5G的全面爆发需要满足什么条件?
  10. 在matlab中以图像中心为旋转轴逆时针旋转30度自编程序,MATLAB数学建模习题