工厂设计模式详解

文章目录

  • 工厂模式(创建型模式)
    • 1. 工厂模式介绍
    • 2. 好处
    • 3. 场景案例
    • 4. 工厂方法案例源码
    • 5. 抽象工厂案例源码(适配器、动态代理)
      • 1. 代码结构
      • 2. 老业务,视频流审核服务接口
      • 3. 老业务,视频流审核服务具体实现类
      • 4. 老业务,老的流服务实现类
      • 5. 新加的俩流服务实现类
      • 6. 新加适配器接口,视频流方法差异化解决
      • 7. 新加三个视频流适配器
      • 8. 新加的视频流动态代理实现类
      • 9. 新加的视频聊审核代理抽象工厂类
      • 10. 单元测试

工厂模式(创建型模式)

1. 工厂模式介绍

  • 简单工厂模式介绍
    • 仅提供一个工厂类,类中提供所有需要的方法
    • 选择延迟到实现类,它调用工厂类传入参数、选择具体调用哪个方法
  • 工厂方法模式介绍
    • 提供一个接口统一工厂类,每个方法提供一个工厂类,每个工厂实现自己的方法
    • 选择延迟到实现类、它调用不同工厂上转型成接口,通过动态多态处理具体调用哪个方法
  • 抽象工厂模式介绍
    • 提供组合工厂的方法

2. 好处

  • 每种方法单独一个类,代码层次更加清晰,可扩展性更高
  • 更加符合软件工程的高内聚、低耦合概念
  • 符合单一职责原则,每个类仅包含自己实现,改动方法不会影响其他方法
  • 符合开闭原则,新建一类实现不会修改原有的类

3. 场景案例

  • 简单工厂模式案例
    • 项目 POJO 类中一般不允许直接操作成员变量,而是创建 getter/setter 方法操作,这也是方便如果要全局对成员变量做操作,则直接改动 getter/setter 方法即可(当然一般也不建议这么做)
  • 工厂方法案例
    • 送礼物功能有太多类型通过 if/else 杂糅在一起了,导致每次新需求开发效率低下,因此使用工厂方法模式重构一把
    • 业务是礼物相关处理,不同类型的礼物拥有类似的方法,如:查询礼物信息、送礼物等,但是具体处理细节不同,因此每种礼物创建单独的类处理,使用工厂方法模式对外提供同一个接口,使用 type 字段区分
    • 使用注解的方式,通过 SpringBoot 将继承同一个接口的、多个实现类注入 HashMap,工厂类中通过传入的 key 返回具体的实现类
  • 抽象工厂案例
    • 在原有的功能上进行扩展功能,不改动原有的业务实现,因此使用抽象工程模式
    • 项目以往仅使用 QN 公司的视频流服务,服务端在审核时需要调用三方接口,后来为了灾备与节流,需要新加 UC、X 等公司的视频流服务,如果使用 if/else 判断或者工厂方法模式,需要再原有代码上做大量修改与回归测试
    • 因此采用抽象工厂模式 + 适配器模式 + 动态代理模式,在少量修改原有业务的情况下处理

4. 工厂方法案例源码

  • 代码结构
├── GiftApplication.java
├── GiftFactory.java
├── bean
│   ├── GiftBean.java
│   └── ResultBean.java
├── constant
│   └── GiftConstant.java
└── gift
    ├── IGiftService.java
    └── impl
        ├── GiftLotteryService.java
        └── GiftLuckService.java
  • 礼物业务抽象接口
public interface IGiftService {

    ResultBean sendGift(GiftBean giftBean);
}
  • 抽奖礼物具体实现类
  • 如果接口名或者参数与其他具体实现类不同,则需要再创建一个抽奖礼物工厂类也可传入 Map 参数,使用动态多态
@Component(GiftConstant.GIFT_LOTTERY)
public class GiftLotteryService implements IGiftService {

    @Override
    public ResultBean sendGift(GiftBean giftBean) {
        System.out.println("发送抽奖礼物");
        return new ResultBean(1000, "送抽奖礼物成功!");
    }
}
  • 幸运礼物具体实现类
  • 如果接口名或者参数与其他具体实现类不同,则需要再创建一个抽奖礼物工厂类也可传入 Map 参数,使用动态多态
@Component(GiftConstant.GIFT_LUCK)
public class GiftLuckService implements IGiftService {

    @Override
    public ResultBean sendGift(GiftBean giftBean) {
        System.out.println("发送幸运礼物");
        return new ResultBean(1000, "送幸运礼物成功!");
    }
}
  • 礼物工厂方法类
  • 通过 Spring 注入实现类,通过 key 获取具体实现类
@Service
public class GiftFactory {

    // 通过 Spring 注入(结果为 HashMap)
    @Autowired
    private Map<String, IGiftService> giftServiceMap;

    public IGiftService getGiftService(String component) {
//        System.out.println(giftServiceMap instanceof HashMap);
        IGiftService giftService = giftServiceMap.get(component);
        if (giftService == null) {
            throw new RuntimeException("礼物类型未定义");
        }
        return giftService;
    }
}
  • 单元测试方法
@RunWith(SpringRunner.class)
@SpringBootTest
public class GiftFactoryTest {

    @Autowired
    GiftFactory giftFactory;

    @Test
    public void test() {
        giftFactory.getGiftService(GiftConstant.GIFT_LUCK).sendGift(new GiftBean(1, "幸运礼物1", 100));
        giftFactory.getGiftService(GiftConstant.GIFT_LOTTERY).sendGift(new GiftBean(2, "抽奖礼物2", 200));
    }
}

5. 抽象工厂案例源码(适配器、动态代理)

1. 代码结构

├── VideoApplication.java
├── bean
│   └── VideoStreamAuditInfo.java
└── video
    ├── IVideoAuditService.java
    ├── factory
    │   ├── IVideoAdapter.java
    │   └── impl
    │       ├── QNStreamAdapter.java
    │       ├── UCStreamAdapter.java
    │       └── XStreamAdapter.java
    ├── impl
    │   └── VideoAuditServiceImpl.java
    ├── proxy
    │   ├── VideoInvocationHandler.java
    │   └── VideoProxy.java
    └── stream
        ├── QNStream.java
        ├── UCStream.java
        └── XStream.java
  • 视频聊审核抽象工厂 + 适配器 + 动态代理、测试类
  • 不用改动原有的 VideoAuditServiceImpl 实现类,通过动态代理的方式创建 IVideoAuditService 接口的代理工厂、
  • 传入最新的实现类(xxStreamAdapter),然后正常调用 IVideoAuditService 接口执行

2. 老业务,视频流审核服务接口

public interface IVideoAuditService {

    /**
     * 获取视频流审核结果
     */
    VideoStreamAuditInfo getStreamAuditInfo(Integer sId);
}

3. 老业务,视频流审核服务具体实现类

@Component
public class VideoAuditServiceImpl implements IVideoAuditService {

    @Autowired
//    private QNStream qnStream;

    private QNStream qnStream = new QNStream();

    /**
     * 返回视频流审核信息,如果不存在返回 null
     */
    @Override
    public VideoStreamAuditInfo getStreamAuditInfo(Integer sId) {
        return qnStream.getQNStreamAuditInfo(sId);
    }
}

4. 老业务,老的流服务实现类

@Component
public class QNStream {

    private Map<Integer, VideoStreamAuditInfo> streamMap = new ConcurrentHashMap<>();

    {
        streamMap.put(100, new VideoStreamAuditInfo(100, "", 1));
    }

    /**
     * 返回 QN 流审核信息,不存在返回 null
     */
    public VideoStreamAuditInfo getQNStreamAuditInfo(int sId) {
        return streamMap.get(sId);
    }

}

5. 新加的俩流服务实现类

@Component
public class UCStream {

    private Map<Integer, VideoStreamAuditInfo> streamMap = new ConcurrentHashMap<>();

    {
        streamMap.put(200, new VideoStreamAuditInfo(200, "", 1));
    }

    /**
     * 返回 UC 流审核信息,不存在返回 null
     */
    public VideoStreamAuditInfo getUCStreamAuditInfo(int sId) {
        return streamMap.get(sId);
    }
}
@Component
public class XStream {

    private Map<Integer, VideoStreamAuditInfo> streamMap = new ConcurrentHashMap<>();

    {
        streamMap.put(300, new VideoStreamAuditInfo(300, "", 0));
    }

    /**
     * 返回 X 流审核信息,不存在返回 null
     */
    public VideoStreamAuditInfo getXStreamAuditInfo(int sId) {
        return streamMap.get(sId);
    }

}

6. 新加适配器接口,视频流方法差异化解决

public interface IVideoAdapter {

    VideoStreamAuditInfo getStreamAuditInfo(Integer sId);
}

7. 新加三个视频流适配器

@Component
public class QNStreamAdapter implements IVideoAdapter {

    @Autowired
//    private QNStream qnStream;

    private QNStream qnStream = new QNStream();

    @Override
    public VideoStreamAuditInfo getStreamAuditInfo(Integer sId) {
        return qnStream.getQNStreamAuditInfo(sId);
    }
}
@Component
public class UCStreamAdapter implements IVideoAdapter {

    @Autowired
//    private UCStream ucStream;

    private UCStream ucStream = new UCStream();

    @Override
    public VideoStreamAuditInfo getStreamAuditInfo(Integer sId) {
        return ucStream.getUCStreamAuditInfo(sId);
    }
}
@Component
public class XStreamAdapter implements IVideoAdapter {

    @Autowired
//    private XStream xStream;

    private XStream xStream = new XStream();

    @Override
    public VideoStreamAuditInfo getStreamAuditInfo(Integer sId) {
        return xStream.getXStreamAuditInfo(sId);
    }
}

8. 新加的视频流动态代理实现类

public class VideoInvocationHandler implements InvocationHandler {

    private IVideoAdapter videoAdapter;

    public VideoInvocationHandler(IVideoAdapter videoAdapter) {
        this.videoAdapter = videoAdapter;
    }

    /**
     * 调用传入的适配器 + method
     * @param proxy 新建的代理类本身
     * @param method 通过代理类调用的方法本身
     * @param args 通过代理类调用的方法中的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 需调用方法的类与代理实现类不同
        return IVideoAdapter.class.getMethod(method.getName(), getClazzByArgs(args)).invoke(videoAdapter, args);
    }

    private static Class<?>[] getClazzByArgs(Object[] args) {
        Class<?>[] parameterTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof ArrayList) {
                parameterTypes[i] = List.class;
                continue;
            }
            if (args[i] instanceof LinkedList) {
                parameterTypes[i] = List.class;
                continue;
            }
            if (args[i] instanceof HashMap) {
                parameterTypes[i] = Map.class;
                continue;
            }
            if (args[i] instanceof TimeUnit){
                parameterTypes[i] = TimeUnit.class;
                continue;
            }
            parameterTypes[i] = args[i].getClass();
        }
        return parameterTypes;
    }
}

9. 新加的视频聊审核代理抽象工厂类

public class VideoProxy {

    /**
     * 获取动态代理
     * 可通过反编译看出:其实是重建一个继承了 Proxy 类的 $Proxy0 类,同时实现了传入的接口
     * @param serviceClass 被代理类的 class 类
     * @param videoAdapter 具体使用的类
     * @return 动态代理对象
     */
    public static <T> T getProxy(Class<T> serviceClass, IVideoAdapter videoAdapter) throws Exception {
        /**
         * 通过类加载器和接口确认代理哪个接口
         * interfaces 被代理的类,它必须至少实现一个接口
         * classLoader 类加载器,可用被代理类接口的类加载器(参考 MyBatis 源码 MapperProxyFactory)
         */
        ClassLoader classLoader = serviceClass.getClassLoader();
        Class<?>[] interfaces = serviceClass.getInterfaces();
        // 具体实现代理方案
        InvocationHandler invocationHandler = new VideoInvocationHandler(videoAdapter);

        return (T)Proxy.newProxyInstance(classLoader, new Class[]{interfaces[0]}, invocationHandler);
    }
}

10. 单元测试

//@RunWith(SpringRunner.class)
//@SpringBootTest
public class VideoApplicationTest {

    @Autowired
//    private static QNStreamAdapter qnStreamAdapter;

    private static QNStreamAdapter qnStreamAdapter = new QNStreamAdapter();
    private static UCStreamAdapter ucStreamAdapter = new UCStreamAdapter();
    private static XStreamAdapter xStreamAdapter = new XStreamAdapter();

//    @Test
    public static void main(String[] args) throws Exception {

        // 让代理对象的class文件写入到磁盘(必须是标准的 main 方法才行)
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        IVideoAuditService qnProxy = VideoProxy.getProxy(VideoAuditServiceImpl.class, qnStreamAdapter);
        VideoStreamAuditInfo qnInfo = qnProxy.getStreamAuditInfo(100);
        System.out.println("QN 视频流审核结果:" + qnInfo);
//
        IVideoAuditService ucProxy = VideoProxy.getProxy(VideoAuditServiceImpl.class, ucStreamAdapter);
        VideoStreamAuditInfo ucInfo = ucProxy.getStreamAuditInfo(200);
        System.out.println("UC 视频流审核结果:" + ucInfo);

        IVideoAuditService xProxy = VideoProxy.getProxy(VideoAuditServiceImpl.class, xStreamAdapter);
        VideoStreamAuditInfo xInfo = xProxy.getStreamAuditInfo(300);
        System.out.println("X 视频流审核结果:" + xInfo);

    }
}

参考:
https://bugstack.cn/md/develop/design-pattern/2020-05-24-%E9%87%8D%E5%AD%A6Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%8A%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%E3%80%8B.html

你可能感兴趣的:(设计模式,设计模式,java,抽象工厂模式,工厂方法模式)