设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结,如果设计模式没学会,抽象能力肯定就不会太强。常见的设计模式有 23 种,今天我们只聊最简单的工厂模式。

工厂模式是属于创建型模式的,通过工厂获取一个一个的新对象。说白了,工厂就是用来 new(创建)对象的,因此把它化分到创建型这一类中。

简单工厂模式

简单工厂模式,正如其名,和名字一样简单,非常简单。下面先看一段代码:

public class FoodFactory {public static Food makeFood(String name) {if ("kuaican".equals(name)) {Food kuaican = new KuaiCanFood();kuaican.setPrice(20.00);return kuaican;} if ("hamburger".equals(name)) {Food hamburger = new HamburgerFood();hamburger.setPrice(22.00);hamburger.setMeat("beef");return hamburger;} // ......return new RuntimeException("not food");}
}

需要注意的是,上面中的 KuaiCanFood 和 HamburgerFood 都继承自 Food。

简单地说,简单工厂模式通常就是这样,一个工厂类 XxxFactory,里面有一个静态方法,根据我们不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象。

我们强调职责单一原则,一个类只提供一种功能,FoodFactory 的功能就是只要负责生产各种 Food。

Java 各种框架中,简单工厂模式是非常常见的,比如JedisConnectionFactory

Redis连接工厂

基本上 Java 中需要创建连接的框架,都是用了工厂模式。

工厂模式

简单工厂模式很简单,在一般情况下,它都能满足我们的需要。但是,在某些特殊场景,它就满足不了我们的需求了。还是拿 Redis 为例,Redis 有单机模式,主从模式,哨兵模式,Cluster 模式。每种模式的连接都不一样,因此我们需要引入多个工厂。

这种情况下,就需要引入多个简单工厂模式。比如:JedisConnectionFactoryJredisConnectionFactoryLettuceConnectionFactorySrpConnectionFactory等等。之所以需要引入工厂模式,是因为我们往往需要使用两个或两个以上的工厂。

还是以 Food 为例。

public interface FoodFactory {Food makeFood(String name);
}
public class ChineseFoodFactory implements FoodFactory {@Overridepublic Food makeFood(String name) {if ("套餐A".equals(name)) {Food kuaican = new KuaiCanFood();kuaican.setPrice(20.00);kuaican.setName("快餐");return kuaican;} if ("套餐B".equals(name)) {Food mifan = new MiFanFood();mifan.setPrice(18.00);mifan.setName("五常大米");return mifan;} return null;}
}
public class AmericanFoodFactory implements FoodFactory {@Overridepublic Food makeFood(String name) {if ("套餐A".equals(name)) {Food hamburger = new HamburgerFood();hamburger.setPrice(22.00);hamburger.setName("hamburger");return hamburger;} if ("套餐B".equals(name)) {Food sandwich = new SandwichFood();sandwich.setPrice(25.00);sandwich.setName("三明治");return sandwich;} return null;}
}

其中,KuaiCanFood、MiFanFood、HamburgerFood、SandwichFood 都派生自 Food。

消费者调用:

public class Consumer {public static void main(String[] args) {// 先选择一个具体的工厂FoodFactory factory = new ChineseFoodFactory();// 由选择的工厂产生具体的对象,不同的工厂造出不一样的对象Food food = factory.makeFood("套餐A");}
}

虽然都是调用 makeFood("套餐A")  制作套餐 A,但是,不同的工厂生产出来的完全不一样。

第一步,我们需要选取合适的工厂,然后第二步基本上和简单工厂一样。

核心在于,我们需要在第一步选好我们需要的工厂。比如,我们有 FileFactory 接口,实现类有 AliyunOssFactory 和 TencentCosFactory,分别对应将文件写入阿里云 OSS 和腾讯云 COS 中。很显然,我们客户端或者消费者第一步就需要决定到底要实例化 AliyunOssFactory 还是 TencentCosFactory,这将决定之后的所有的操作。

工厂模式非常简单,我把他们都画到一张图上,希望大家能够看图就会:

工厂模式

现在会了还不是真会,一定要在实践代码中使用,不然过不了多久就会忘记!

抽象工厂模式

有了简单工厂模式,和工厂模式,为什么还有抽象工厂模式?

这就是当涉及到产品族的时候,就需要引入抽象工厂模式了。当一个产品族,存在多个不同类型的产品(比如我上面列举的 Redis 连接工厂)情况下,不同架构模式,选择不同的连接工厂的问题。而这种场景在业务开发中也是非常多见的,只不过可能有时候没有将它们抽象化出来。

Redis的连接工厂

RedisConnectionFactory的例子,我就不在多说了,建议spring-data-redis中的源码多看几遍。下面我再列举一个生活中的经典的例子,就是造一台手机。我们先不引入抽象工厂模式,看看怎么实现。

因为手机是由许多的构件组成的,我们将处理器 CPU 和主板 Board 进行抽象,然后 CPU 由 CpuFactory 生产,主板由 MainBoardFactory 生产。然后,我们再将 CPU 和主板搭配起来组合在一起,如下图:

抽象工厂模式

这个时候的如果需要手机产品,只需这样调用:

public class Phone {private Cpu cpu;private MainBoard mainBoard;public Phone(Cpu cpu, MainBoard mainBoard){this.cpu = cpu;this.mainBoard = mainBoard;}public static void main(String[] args) {// 得到华为的处理器CpuFactory KirinFactory = new HuaweiCpuFactory();Cpu cpu = KirinFactory.getCpu();// 得到华为的主板MainBoardFactory mainBoardFactory = new HuaweiMainBoardFactory();MainBoard mainBoard = mainBoardFactory.getMainBoard();// 组装手机CPU和主板Phone huaweiMate = new Phone(cpu, mainBoard);}
}

单独看处理器 CPU 工厂和主板工厂,它们分别是前面我们说的工厂模式。这种方式也容易扩展,因为要给手机加配件的话,只需要加一个 XxxFactory 和相应的实现即可,不需要修改现有的工厂。

但是,这种方式有一个问题,那就是如果苹果????家产的 CPU 和华为产的主板不能兼容使用,因此不能出现随意组合。这就是产品族的概念,它代表了组成某个产品的一系列配件的集合。

当涉及到这种产品族的问题的时候,就需要抽象工厂模式来支持了。我们不再定义 CPU 工厂、主板工厂、OS 工厂、显示屏工厂等等,我们直接定义手机工厂,每个手机工厂负责生产所有的设备,这样能保证肯定不存在兼容问题。

public interface PhoneFactory {Cpu getCpu();MainBoard getMainBord();Display getDisplay();
}
public class HuaweiPhoneFactory implements PhoneFactory {@Overridepublic Cpu getCpu() {return new HuaweiCpu();}@Overridepublic MainBoard getMainBord() {return new HuaweiMainBoard();}@Overridepublic Display getDisplay() {return new BoeDisplay();}
}
public class XiaomiPhoneFactory implements PhoneFactory {@Overridepublic Cpu getCpu() {return new HuaweiCpu();}@Overridepublic MainBoard getMainBord() {return new XiaomiMainBoard();}@Overridepublic Display getDisplay() {return new VisionoxDisplay();}
}

这种情况下,对于生产来说,不再需要单独挑选 CPU 厂商、显示屏厂商等,直接选择一家品牌工厂,品牌工厂会负责生产所有的东西,而且能保证肯定是兼容可用的。

public class Test {public static void main(String[] args) {// 第一步就要选定一个“大品牌工厂”PhoneFactory factory = new HuaweiPhoneFactory();// 从这个大厂设计制造CPUCpu cpu = factory.getCpu();// 从这个大厂生产手机主板MainBoard board = factory.getMainBord();// 从这个大厂生产手机显示屏Display boeDisplay = factory.getDisplay();// 生产一个华为 Mete 40 手机Phone huaweiMete40 = new Phone(cpu, board, boeDisplay);}
}

这样一个抽象工厂的代码和案例就讲完了,如果还有不懂的,我下次再拿我们现在生产项目中的案例给大家讲解!

最后总结一下,功能模式根据需求和功能的不同,你可以选择对应的简单工厂,普通工厂,和抽象工厂。抽象工厂看起来比较抽象,它暴露的问题也是显而易见的,比如我们要加个 NFC 模块,就需要修改所有的工厂,给所有的工厂都加上制造显示器的方法。这有点违反了对修改关闭,对扩展开放这个设计原则。但也不要完全照搬设计原则,毕竟有时候是需要打破原则,不是吗?比如在电商系统中的一些反范式设计等。

设计模式之工厂模式,史上最强,不服来辩!相关推荐

  1. 【整理】史上最强的娱乐大餐———九奔、汉澳、器普。。。。。。

    按: 本文内容来源于:http://shouji88.com/msg_list.cgi?bbs_id=000001                               http://shouj ...

  2. 初学Java常用设计模式之——工厂模式

    声明:转载请附上原文链接 提示:标题序号从2开始,是照应不同设计模式笔记发布的顺序而定的,比如,第上一篇文章 初学Java常用设计模式之--单例模式 序号从1开始 2. 工厂模式(常用) ⼯⼚模式介绍 ...

  3. JVM面试题(史上最强、持续更新、吐血推荐)

    JVM面试题(史上最强.持续更新.吐血推荐) 文章很长,建议收藏起来慢慢读!疯狂创客圈总目录 语雀版 | 总目录 码云版| 总目录 博客园版 为您奉上珍贵的学习资源 : <尼恩Java面试宝典& ...

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

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

  5. JavaScript设计模式--简单工厂模式例子---XHR工厂

    JavaScript设计模式--简单工厂模式例子---XHR工厂 第一步,Ajax操作接口(目的是起一个接口检测作用) (1)引入接口文件 //定义一个静态方法来实现接口与实现类的直接检验 //静态方 ...

  6. 三角形圆形创建与擦除java_设计模式---------------简单工厂模式

    设计模式---------------简单工厂模式 一.题目(Question) 使用简单工厂模式设计一个可以创建不同几何形状(如圆形.方形和三角形等)的绘图工具,每个几何图形都要有绘制draw()和 ...

  7. 【译】史上最强的vimrc文件

    史上最强的vimrc文件http://amix.dk/vim/vimrc.html, 据说有800行,还是作者精简后的结果. """"""& ...

  8. 策略模式和工厂模式的区别_设计模式之工厂模式-工厂方法模式

    设计模式之工厂模式-工厂方法模式 大家好,欢迎来到污污弹公司,今天司小司又接到了一个新活-披萨项目. 来源:凯哥Java(kaigejava) 需求: 披萨项目: 要方便披萨品种的扩展.要便于维护.要 ...

  9. Java 设计模式之工厂模式(二)

    原文地址:Java 设计模式之工厂模式(二) 博客地址:http://www.extlight.com 一.背景 本篇内容是 Java 设计模式创建型模式的第二篇.上一篇主题为 <Java 设计 ...

  10. 【插件】史上最强编辑器通用ctags插件OpenCTags使用指南v1.2--开发者必备

    Changes Log: v1.2.0 2011-12-4 支持Java编写的可以设置用户配置的编辑工具 增加当前文件类查找Tags功能 增加设置当前目录参数 完善多种主流轻量级编辑器的配置 v1.1 ...

最新文章

  1. android之descendantFocusability用法简析
  2. MongoDB增加用户认证:增加用户、删除用户、修改用户密码、读写权限、只读权限...
  3. 3.7 为什么需要非线性激活函数-深度学习-Stanford吴恩达教授
  4. 飞鸽 bind()错误=10048
  5. 如何用C语言清空特定文件夹中的所有文件
  6. 笔记-项目范围管理-需求工程-需求分析的三个阶段-需求提出-需求描述-需求评审...
  7. Learning python学习总结之字符串方法
  8. 黑客必须了解的网络知识
  9. SpringBoot : Consider defining a bean of type xxx in your configuration.
  10. JavaScript数组合并
  11. Keras:我的第一个LSTM二分类网络模型
  12. Unlocker-最好的顽固文件删除工具使用说明
  13. html自动写对联,html左右对联代码
  14. 猿人学爬虫攻防大赛 | 第五题: js混淆 乱码增强
  15. 制订项目进度计划的讨论
  16. note4-WEB源码拓展
  17. 关于微信小程序中的.eslintrc.js
  18. oracle中字段类型为date存储数据精确到时分秒的问题
  19. random是python的第三方库吗_random库是Python的()
  20. linux字符型驱动开发体验01

热门文章

  1. 简述计算机视觉中的单眼线索,单眼线索
  2. 产品经理与项目经理的区别?
  3. 全面理解ES6模块化编程
  4. 四种常见的浏览器内核简介----JS城市选择控件
  5. 紧跟百度爱采购:搜狗上线b2b商城“搜狗招商”
  6. 华为服务器Linux启动过程,华为RH2288H服务器引导ServiceCD安装Windows Server操作系统...
  7. vm12创建虚拟机(nat模式)
  8. MySQL基础篇-MySQL 命令大全
  9. HTML JS获取当前页面URL
  10. horspool算法C语言代码,sfa3