赞
踩
设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结,如果设计模式没学会,抽象能力肯定就不会太强。常见的设计模式有 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
。
基本上 Java 中需要创建连接的框架,都是用了工厂模式。
简单工厂模式很简单,在一般情况下,它都能满足我们的需要。但是,在某些特殊场景,它就满足不了我们的需求了。还是拿 Redis 为例,Redis 有单机模式,主从模式,哨兵模式,Cluster 模式。每种模式的连接都不一样,因此我们需要引入多个工厂。
这种情况下,就需要引入多个简单工厂模式。比如:JedisConnectionFactory
、JredisConnectionFactory
、LettuceConnectionFactory
、SrpConnectionFactory
等等。之所以需要引入工厂模式,是因为我们往往需要使用两个或两个以上的工厂。
还是以 Food 为例。
- public interface FoodFactory {
- Food makeFood(String name);
- }
- public class ChineseFoodFactory implements FoodFactory {
- @Override
- public 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 {
- @Override
- public 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 连接工厂)情况下,不同架构模式,选择不同的连接工厂的问题。而这种场景在业务开发中也是非常多见的,只不过可能有时候没有将它们抽象化出来。
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 {
- @Override
- public Cpu getCpu() {
- return new HuaweiCpu();
- }
- @Override
- public MainBoard getMainBord() {
- return new HuaweiMainBoard();
- }
- @Override
- public Display getDisplay() {
- return new BoeDisplay();
- }
- }
- public class XiaomiPhoneFactory implements PhoneFactory {
- @Override
- public Cpu getCpu() {
- return new HuaweiCpu();
- }
- @Override
- public MainBoard getMainBord() {
- return new XiaomiMainBoard();
- }
- @Override
- public Display getDisplay() {
- return new VisionoxDisplay();
- }
- }

这种情况下,对于生产来说,不再需要单独挑选 CPU 厂商、显示屏厂商等,直接选择一家品牌工厂,品牌工厂会负责生产所有的东西,而且能保证肯定是兼容可用的。
- public class Test {
- public static void main(String[] args) {
- // 第一步就要选定一个“大品牌工厂”
- PhoneFactory factory = new HuaweiPhoneFactory();
- // 从这个大厂设计制造CPU
- Cpu cpu = factory.getCpu();
- // 从这个大厂生产手机主板
- MainBoard board = factory.getMainBord();
- // 从这个大厂生产手机显示屏
- Display boeDisplay = factory.getDisplay();
- // 生产一个华为 Mete 40 手机
- Phone huaweiMete40 = new Phone(cpu, board, boeDisplay);
- }
- }
这样一个抽象工厂的代码和案例就讲完了,如果还有不懂的,我下次再拿我们现在生产项目中的案例给大家讲解!
最后总结一下,功能模式根据需求和功能的不同,你可以选择对应的简单工厂,普通工厂,和抽象工厂。抽象工厂看起来比较抽象,它暴露的问题也是显而易见的,比如我们要加个 NFC 模块,就需要修改所有的工厂,给所有的工厂都加上制造显示器的方法。这有点违反了对修改关闭,对扩展开放这个设计原则。但也不要完全照搬设计原则,毕竟有时候是需要打破原则,不是吗?比如在电商系统中的一些反范式设计等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。