设计模式 - 工厂模式(通用工厂)

素颜马尾好姑娘i 2023-10-07 15:31 97阅读 0赞
  1. 参考链接: [link](https://www.runoob.com/design-pattern/factory-pattern.html).

什么是工厂模式

  1. 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,
  2. 它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且
  3. 是通过使用一个共同的接口来指向新创建的对象。
  4. 意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
  5. 主要解决:**主要解决接口选择的问题**。
  6. 何时使用:我们明确地计划不同条件下创建不同实例时。
  7. 如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
  8. 关键代码:创建过程在其子类执行。
  9. 优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展
  10. 一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
  11. 缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上
  12. 增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

什么是抽象工厂模式

  1. 抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。
  2. 该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
  3. 在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。
  4. 每个生成的工厂都能按照工厂模式提供对象。
  5. 意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
  6. 主要解决:**主要解决接口选择的问题**。
  7. 何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
  8. 如何解决:在一个产品族里面,定义多个产品。
  9. 关键代码:在一个工厂里聚合多个同类产品。
  10. 优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
  11. 缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

工厂模式解决的是接口选择的问题

实际场景

  1. 顾客在收银端买单时可以选择多种支付方式付款,目前收款包含现金支付,扫码支付,会员余额支付。该接口后期将还会拓展其他支付方式。

如何使用工厂模式解决这一选择支付的问题呢?

1.创建支付常量,不要在代码里出现魔法值
  1. /**
  2. * 支付常量
  3. */
  4. public class PayConstant {
  5. /**
  6. * 支付类型
  7. */
  8. public enum PayTypeEnum {
  9. /**
  10. *现金支付(1)
  11. */
  12. CASH(1, "现金支付"),
  13. /**
  14. * 扫码支付(2)
  15. */
  16. SCAN(2, "扫码支付"),
  17. /**
  18. * 会员余额支付(3)
  19. */
  20. VIP(3, "会员余额支付"),
  21. ;
  22. PayTypeEnum(Integer value, String name) {
  23. this.value = value;
  24. this.name = name;
  25. }
  26. /**
  27. * 通过编号获取支付方式名称值
  28. */
  29. public static String getPayTypeName(Integer payType) {
  30. PayTypeEnum[] payTypes = values();
  31. for (PayTypeEnum payTypeEnum : payTypes) {
  32. if (payTypeEnum.getValue().equals(payType)) {
  33. return payTypeEnum.getName();
  34. }
  35. }
  36. return null;
  37. }
  38. private final Integer value;
  39. private final String name;
  40. public Integer getValue() {
  41. return value;
  42. }
  43. public String getName() {
  44. return name;
  45. }
  46. }
  47. }
2.新建支付接口PayService
  1. /**
  2. * 支付接口
  3. */
  4. public interface PayService {
  5. void pay();
  6. }
3.创建具体的实现类实现支付接口PayService

/**

  1. * 具体实现类:现金支付
  2. */
  3. public class CashPay implements PayService{
  4. @Override
  5. public void pay() {
  6. System.out.println("CashPay实现类:现金支付");
  7. }
  8. }
  9. /**
  10. * 具体实现类:扫码支付
  11. */
  12. public class ScanPay implements PayService{
  13. @Override
  14. public void pay() {
  15. System.out.println("ScanPay实现类:扫码支付");
  16. }
  17. }
  18. /**
  19. * 具体实现类:会员余额支付
  20. */
  21. public class VipPay implements PayService{
  22. @Override
  23. public void pay() {
  24. System.out.println("VipPay实现类:会员余额支付");
  25. }
  26. }
4.静态工厂:用来生产具体的实现类,也就是说通过静态类方法中if或swich 条件判断去生产创建的实现类

优点

  1. 实现简单,只需一个静态类方法即可,所以也称之为静态工厂。
  2. 将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来都相对容易。

缺点

  1. 扩展性差,⼯⼚类的职责相对过重,增加新的产品需要修改⼯⼚类
    的判断逻辑,违背即开闭原则。
  2. 增加了系统的复杂度和理解难度,不利于系统的扩展和维护

    public class PayFactory {

    1. public static PayService getPayFactory(Integer payType){
    2. if(PayConstant.PayTypeEnum.CASH.getValue().equals(payType)){
    3. return new CashPay();
    4. }
    5. if(PayConstant.PayTypeEnum.SCAN.getValue().equals(payType)){
    6. return new ScanPay();
    7. }
    8. if(PayConstant.PayTypeEnum.VIP.getValue().equals(payType)){
    9. return new VipPay();
    10. }
    11. System.out.println("不存在该支付方式");
    12. throw new BusinessRuntimeException(ErrorResultCode.SYSTEM_ERROR);
    13. }

    }

    public class Main {

  1. public static void main(String[] args) {
  2. System.out.println("现金支付=====================");
  3. PayService cashPay = PayFactory.getPayFactory(PayConstant.PayTypeEnum.CASH.getValue());
  4. cashPay.pay();
  5. System.out.println("扫码支付=====================");
  6. PayService scanPay = PayFactory.getPayFactory(PayConstant.PayTypeEnum.SCAN.getValue());
  7. scanPay.pay();
  8. System.out.println("会员余额支付===================");
  9. PayService vipScanPay = PayFactory.getPayFactory(PayConstant.PayTypeEnum.VIP.getValue());
  10. vipScanPay.pay();
  11. }
  12. }
5.spring中结合策略模式来使用简单工厂方法模式

spring中类的创建都不需要我们自己创建了,只要选择的使用它们即可,代码改造一下

  1. /**
  2. * 支付接口
  3. */
  4. public interface PayService {
  5. String pay();
  6. }
  7. /**
  8. * 具体实现类:现金支付
  9. */
  10. @Service("CashPay")
  11. public class CashPay implements PayService {
  12. @Override
  13. public String pay() {
  14. return "CashPay实现类:现金支付";
  15. }
  16. }
  17. /**
  18. * 具体实现类:扫码支付
  19. */
  20. @Service("ScanPay")
  21. public class ScanPay implements PayService {
  22. @Override
  23. public String pay() {
  24. return "ScanPay实现类:扫码支付";
  25. }
  26. }
  27. /**
  28. * 具体实现类:会员余额支付
  29. */
  30. @Service("VipPay")
  31. public class VipPay implements PayService {
  32. @Override
  33. public String pay() {
  34. return "VipPay实现类:会员余额支付";
  35. }
  36. }
  37. /**
  38. * 支付常量
  39. */
  40. public class PayConstant {
  41. /**
  42. * 支付类型
  43. */
  44. public enum PayTypeEnum {
  45. /**
  46. *现金支付(1)
  47. */
  48. CASH(1, "CashPay"),
  49. /**
  50. * 扫码支付(2)
  51. */
  52. SCAN(2, "ScanPay"),
  53. /**
  54. * 会员余额支付(3)
  55. */
  56. VIP(3, "VipPay"),
  57. ;
  58. PayTypeEnum(Integer value, String name) {
  59. this.value = value;
  60. this.name = name;
  61. }
  62. /**
  63. * 通过编号获取支付方式名称值
  64. */
  65. public static String getPayTypeName(Integer payType) {
  66. PayTypeEnum[] payTypes = values();
  67. for (PayTypeEnum payTypeEnum : payTypes) {
  68. if (payTypeEnum.getValue().equals(payType)) {
  69. return payTypeEnum.getName();
  70. }
  71. }
  72. return null;
  73. }
  74. private final Integer value;
  75. private final String name;
  76. public Integer getValue() {
  77. return value;
  78. }
  79. public String getName() {
  80. return name;
  81. }
  82. }
  83. }
  84. /**
  85. * 注意:
  86. * 从ApplicationContextAware获取ApplicationContext上下文的情况,
  87. * 仅仅适用于当前运行的代码和已启动的Spring代码处于同一个Spring上下文,
  88. * 否则获取到的ApplicationContext是空的。
  89. * 定时任务是没办法获取到项目所在Spring容器启动之后的ApplicationContext
  90. */
  91. @Component
  92. public class PayServiceFactory implements ApplicationContextAware {
  93. private static Map<String, PayService> serviceMap;
  94. @Override
  95. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  96. serviceMap = applicationContext.getBeansOfType(PayService.class);
  97. }
  98. /**
  99. * 获取对应serviceImpl实现类
  100. */
  101. public PayService getService(Integer payType) {
  102. PayService payService = serviceMap.get(PayConstant.PayTypeEnum.getPayTypeName(payType));
  103. if(payService == null){
  104. System.out.println("不存在该支付方式");
  105. throw new BusinessRuntimeException(ErrorResultCode.SYSTEM_ERROR);
  106. }
  107. return payService;
  108. }
  109. }
  110. import com.zm.service.PayService;
  111. import com.zm.service.factory.PayServiceFactory;
  112. import io.swagger.annotations.ApiOperation;
  113. import org.hibernate.validator.constraints.Range;
  114. import org.springframework.beans.factory.annotation.Autowired;
  115. import org.springframework.validation.annotation.Validated;
  116. import org.springframework.web.bind.annotation.*;
  117. @RestController
  118. @Validated
  119. public class FactoryController {
  120. @Autowired
  121. private PayServiceFactory payServiceFactory;
  122. @GetMapping("/factory")
  123. @ApiOperation(value = "简单工厂演示" ,httpMethod = "GET")
  124. public String hello(@Range(min = 1,max = 3) @RequestParam("payType") Integer payType) {
  125. PayService service = payServiceFactory.getService(payType);
  126. return service.pay();
  127. }
  128. }

测试一下
在这里插入图片描述
在这里插入图片描述

通用模板

如果每个工厂都需要去创建这么类,肯定也是不行,那么封装一个通用的工厂模式

  1. package com.chinadci.fj.app.base.factory;
  2. /**
  3. * @author zhou
  4. */
  5. public interface Strategy<T> {
  6. /**
  7. * 获得策略的标识
  8. */
  9. T getId();
  10. }

通用的策略工厂类

  1. import org.springframework.beans.BeansException;
  2. import org.springframework.beans.factory.InitializingBean;
  3. import org.springframework.context.ApplicationContext;
  4. import org.springframework.context.ApplicationContextAware;
  5. import java.util.Collection;
  6. import java.util.HashMap;
  7. import java.util.Map;
  8. /**
  9. * 通用的策略工厂类
  10. * @param <T>
  11. * @param <S>
  12. */
  13. public class StrategyFactory<T, S extends Strategy<T>> implements InitializingBean, ApplicationContextAware {
  14. private final Class<S> strategyType;
  15. private Map<T, S> strategyMap;
  16. private ApplicationContext appContext;
  17. /**
  18. * 创建一个策略工厂
  19. *
  20. * @param strategyType 策略的类型
  21. */
  22. public StrategyFactory(Class<S> strategyType) {
  23. this.strategyType = strategyType;
  24. }
  25. /**
  26. * 根据策略 id 获得对应的策略的 Bean
  27. *
  28. * @param id 策略 id
  29. * @return 策略的 Bean
  30. */
  31. public S getStrategy(T id) {
  32. return strategyMap.get(id);
  33. }
  34. @Override
  35. public void afterPropertiesSet() {
  36. // 获取 Spring 容器中,所有 S 类型的 Bean
  37. Collection<S> strategies = appContext.getBeansOfType(strategyType).values();
  38. strategyMap = new HashMap<>(strategies.size());
  39. // 将 所有 S 类型的 Bean 放入到 strategyMap 中
  40. for (final S strategy : strategies) {
  41. T id = strategy.getId();
  42. strategyMap.put(id, strategy);
  43. }
  44. }
  45. @Override
  46. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  47. appContext = applicationContext;
  48. }
  49. }

接口去继承Strategy类

  1. /**
  2. * 支付接口
  3. * @author zhou
  4. */
  5. public interface PayService extends Strategy<String>{
  6. String pay();
  7. }
  8. import org.springframework.stereotype.Service;
  9. /**
  10. * 具体实现类:现金支付
  11. */
  12. @Service("CashPay")
  13. public class CashPay implements PayService {
  14. @Override
  15. public String pay() {
  16. return "CashPay实现类:现金支付";
  17. }
  18. @Override
  19. public String getId() {
  20. return "1";
  21. }
  22. }
  23. import org.springframework.stereotype.Service;
  24. /**
  25. * 具体实现类:扫码支付
  26. */
  27. @Service("ScanPay")
  28. public class ScanPay implements PayService {
  29. @Override
  30. public String pay() {
  31. return "ScanPay实现类:扫码支付";
  32. }
  33. @Override
  34. public String getId() {
  35. return "2";
  36. }
  37. }
  38. import org.springframework.stereotype.Service;
  39. /**
  40. * 具体实现类:会员余额支付
  41. */
  42. @Service("VipPay")
  43. public class VipPay implements PayService {
  44. @Override
  45. public String pay() {
  46. return "VipPay实现类:会员余额支付";
  47. }
  48. @Override
  49. public String getId() {
  50. return "3";
  51. }
  52. }

注册bean

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. /**
  4. * 注册bean工厂
  5. * @author zhou
  6. */
  7. @Configuration
  8. public class FactoryConfig {
  9. @Bean(name = "payServiceFactory")
  10. public StrategyFactory<String, PayService> payServiceFactory() {
  11. return new StrategyFactory<>(PayService.class);
  12. }
  13. }
  14. import com.zm.factory.PayService;
  15. import com.zm.factory.StrategyFactory;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import org.springframework.validation.annotation.Validated;
  18. import org.springframework.web.bind.annotation.*;
  19. @RestController
  20. @Validated
  21. public class FactoryController {
  22. @Autowired
  23. private StrategyFactory<String, PayService> payServiceFactory;
  24. // http://localhost:8080/factory?payType=1
  25. @GetMapping("/factory")
  26. public String hello(@RequestParam("payType") String payType) {
  27. PayService service = payServiceFactory.getStrategy(payType);
  28. return service.pay();
  29. }
  30. }

发表评论

表情:
评论列表 (有 0 条评论,97人围观)

还没有评论,来说两句吧...

相关阅读