当前位置:   article > 正文

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

工厂设计模式

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

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

简单工厂模式

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

  1. public class FoodFactory {
  2.     public static Food makeFood(String name) {
  3.         if ("kuaican".equals(name)) {
  4.             Food kuaican = new KuaiCanFood();
  5.             kuaican.setPrice(20.00);
  6.             return kuaican;
  7.         } 
  8.         if ("hamburger".equals(name)) {
  9.             Food hamburger = new HamburgerFood();
  10.             hamburger.setPrice(22.00);
  11.             hamburger.setMeat("beef");
  12.             return hamburger;
  13.         } 
  14.         // ......
  15.         return new RuntimeException("not food");
  16.     }
  17. }

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

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

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

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

Redis连接工厂

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

工厂模式

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

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

还是以 Food 为例。

  1. public interface FoodFactory {
  2.     Food makeFood(String name);
  3. }
  4. public class ChineseFoodFactory implements FoodFactory {
  5.     @Override
  6.     public Food makeFood(String name) {
  7.         if ("套餐A".equals(name)) {
  8.             Food kuaican = new KuaiCanFood();
  9.             kuaican.setPrice(20.00);
  10.             kuaican.setName("快餐");
  11.             return kuaican;
  12.         } 
  13.         
  14.         if ("套餐B".equals(name)) {
  15.             Food mifan = new MiFanFood();
  16.             mifan.setPrice(18.00);
  17.             mifan.setName("五常大米");
  18.             return mifan;
  19.         } 
  20.         
  21.         return null;
  22.     }
  23. }
  24. public class AmericanFoodFactory implements FoodFactory {
  25.     @Override
  26.     public Food makeFood(String name) {
  27.         if ("套餐A".equals(name)) {
  28.             Food hamburger = new HamburgerFood();
  29.             hamburger.setPrice(22.00);
  30.             hamburger.setName("hamburger");
  31.             return hamburger;
  32.         } 
  33.         
  34.         if ("套餐B".equals(name)) {
  35.             Food sandwich = new SandwichFood();
  36.             sandwich.setPrice(25.00);
  37.             sandwich.setName("三明治");
  38.             return sandwich;
  39.         } 
  40.         
  41.         return null;
  42.     }
  43. }

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

消费者调用:

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

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

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

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

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

工厂模式

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

抽象工厂模式

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

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

Redis的连接工厂

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

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

抽象工厂模式

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

  1. public class Phone {
  2.     private Cpu cpu;
  3.     private MainBoard mainBoard;
  4.     public Phone(Cpu cpu, MainBoard mainBoard){
  5.         this.cpu = cpu;
  6.         this.mainBoard = mainBoard;
  7.     }
  8.     public static void main(String[] args) {
  9.         // 得到华为的处理器
  10.         CpuFactory KirinFactory = new HuaweiCpuFactory();
  11.         Cpu cpu = KirinFactory.getCpu();
  12.         // 得到华为的主板
  13.         MainBoardFactory mainBoardFactory = new HuaweiMainBoardFactory();
  14.         MainBoard mainBoard = mainBoardFactory.getMainBoard();
  15.         // 组装手机CPU和主板
  16.         Phone huaweiMate = new Phone(cpu, mainBoard);
  17.     }
  18. }

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

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

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

  1. public interface PhoneFactory {
  2.     Cpu getCpu();
  3.     MainBoard getMainBord();
  4.     Display getDisplay();
  5. }
  6. public class HuaweiPhoneFactory implements PhoneFactory {
  7.     @Override
  8.     public Cpu getCpu() {
  9.         return new HuaweiCpu();
  10.     }
  11.     @Override
  12.     public MainBoard getMainBord() {
  13.         return new HuaweiMainBoard();
  14.     }
  15.     @Override
  16.     public Display getDisplay() {
  17.         return new BoeDisplay();
  18.     }
  19. }
  20. public class XiaomiPhoneFactory implements PhoneFactory {
  21.     @Override
  22.     public Cpu getCpu() {
  23.         return new HuaweiCpu();
  24.     }
  25.     @Override
  26.     public MainBoard getMainBord() {
  27.         return new XiaomiMainBoard();
  28.     }
  29.     @Override
  30.     public Display getDisplay() {
  31.         return new VisionoxDisplay();
  32.     }
  33. }

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

  1. public class Test {
  2.     public static void main(String[] args) {
  3.         // 第一步就要选定一个“大品牌工厂”
  4.         PhoneFactory factory = new HuaweiPhoneFactory();
  5.         // 从这个大厂设计制造CPU
  6.         Cpu cpu = factory.getCpu();
  7.         // 从这个大厂生产手机主板
  8.         MainBoard board = factory.getMainBord();
  9.         // 从这个大厂生产手机显示屏
  10.         Display boeDisplay = factory.getDisplay();
  11.         // 生产一个华为 Mete 40 手机
  12.         Phone huaweiMete40 = new Phone(cpu, board, boeDisplay);
  13.     }
  14. }

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

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

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
  

闽ICP备14008679号