1.工厂方法

工厂方法

工厂方法,又可以称为虚拟构造函数、Factory Method;其在父类中提供了一个创建对象的方法,允许子类决定实例化对象的类型。

结构实现

如上图所示:

  1. 抽象产品(Product):定义产品的接口。
  2. 具体产品(Concrete):返回具体类型。
  3. 抽象工厂类(Creator):定义一个接口,包含一个抽象的工厂方法
  4. 具体工厂(Concrete Creators):重写工厂方法,返回不同的对应类型

示例说明:

当我们实现跨平台UI组件开发时,需要保证在windows系统下的按钮再Linux系统下保持不变,就需要我们使用到工厂方法。

类图分析:

代码实现:

java 复制代码
package com.example.demo;

import org.junit.jupiter.api.Test;

/**
 * @title: APITest
 * @date: 2024/3/28 16:41
 * @description:
 */

// 抽象产品
interface Button {
    void click();
}

// 具体产品
class WindowsButton implements Button {
    @Override
    public void click() {
        System.out.println("Windows Button");
    }
}

class MacButton implements Button {
    @Override
    public void click() {
        System.out.println("Mac Button");
    }
}

// 抽象工厂
interface GUIFactory {
    Button createButton();
}

// 具体工厂
class WindowsFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }
}

class MacFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new MacButton();
    }
}

public class APITest {
    @Test
    public void test() {
        GUIFactory factory = new WindowsFactory();
        Button button = factory.createButton();
        button.click();
    }
}

这样,我们就简单实现了一个工厂模式。当然,具体的使用中并不会这么简单,接下来我们便以电商系统中常见的积分兑换礼品作为简单分析。

实战分析

假设系统进行礼品兑换时,可分为优惠卷、实物商品、第三方兑换卡三种礼品。

不使用设计模式实现

java 复制代码
 public AwardRes awardToUser(AwardReq req) {
        String reqJson = JSONObject.toJSONString(req);
        AwardRes awardRes = null;
        try {
            logger.info("奖品发放开始{}。req:{}", req.getuId(), reqJson);
            // 按照不同类型方法商品[1优惠券、2实物商品、3第三方兑换卡]
            if (req.getAwardType() == 1) {
                CouponService couponService = new CouponService();
                // 处理优惠卷业务
                awardRes = XXX;
            } else if (req.getAwardType() == 2) {
                GoodsService goodsService = new GoodsService();
                // 处理实物业务
                awardRes = XXX;
            } else if (req.getAwardType() == 3) {
                IQiYiCardService iQiYiCardService = new IQiYiCardService();
                // 处理第三方兑换卡业务
                awardRes = XXX;
            }
            logger.info("奖品发放完成{}。", req.getuId());
        } catch (Exception e) {
            logger.error("奖品发放失败{}。req:{}", req.getuId(), reqJson, e);
            awardRes = new AwardRes("0001", e.getMessage());
        }
        return awardRes;
    }

由以上代码可知,使用 if 判断虽然逻辑明了,并没有什么问题。 但是当奖品种类增加时,if 判断也要对应增加,这样会使得方法体行数增加,维护变得更加麻烦,总不能每次都要一个一个找过去吧。

使用工厂方法

经过以上分析,我们可以抽象产品接口(ISouvenirs ),而具体产品方法(CouponSouvenirsService,GoodsSouvenirsService,CardSouvenirsService)则对应不同的商品实现层。在使用工厂类实现统一返回。 如下:

java 复制代码
// 抽象产品如下:
public interface ISouvenirs{
    
    /**
    * 发送礼品
    */
    void sendSouvenirs(...params);
}

// 工厂类
public class StoreFactory{
    public ISouvenirs getSouvenirsService(Integer commodityType) {
        if (null == commodityType) return null;
        if (1 == commodityType) return new CouponSouvenirsService();
        if (2 == commodityType) return new GoodsSouvenirsService();
        if (3 == commodityType) return new CardSouvenirsService();
        throw new RuntimeException("不存在的礼品类型");
    }
}

// 实现
 public AwardRes awardToUser(AwardReq req) {
     StoreFactory storeFactory = new StoreFactory();
     ISouvenirs souvenirs = storeFactory.getCommodityService();
     souvenirs.sendSouvenirs(...params);
 }

由以上可知,每种奖品的实现都在自己的类中,修改/删除也不影响其他功能的测试。而后续有新增奖品种类时,我们只需要按图索翼,新增对应的Service层,可以减低维护的难度。

总结

经过以上几次总结,我们可以知道工厂方法模式并不复杂。

他避免工厂和产品的耦合,满足了单一原则,每一个业务逻辑实现都在自己所属类中完成。

但是这样子也会出现一个同样的问题,当有巨量的奖品类型时,工厂类中的 if 判断也会对应增加,从而使维护更难。因此这时需要搭配设计模式使用。

参考资料

  1. 设计模式 | 菜鸟教程 (runoob.com)
  2. 《深入设计模式》 PDF版
相关推荐
九圣残炎35 分钟前
【springboot】简易模块化开发项目整合Redis
spring boot·redis·后端
拉里小猪的迷弟1 小时前
设计模式-结构型-常用:代理模式、桥接模式、装饰者模式、适配器模式
设计模式·代理模式·桥接模式·适配器模式·装饰器模式
.生产的驴1 小时前
Electron Vue框架环境搭建 Vue3环境搭建
java·前端·vue.js·spring boot·后端·electron·ecmascript
爱学的小涛1 小时前
【NIO基础】基于 NIO 中的组件实现对文件的操作(文件编程),FileChannel 详解
java·开发语言·笔记·后端·nio
爱学的小涛1 小时前
【NIO基础】NIO(非阻塞 I/O)和 IO(传统 I/O)的区别,以及 NIO 的三大组件详解
java·开发语言·笔记·后端·nio
北极无雪1 小时前
Spring源码学习:SpringMVC(4)DispatcherServlet请求入口分析
java·开发语言·后端·学习·spring
爱码少年1 小时前
springboot工程中使用tcp协议
spring boot·后端·tcp/ip
CocoaAndYy3 小时前
设计模式-单例模式
单例模式·设计模式
bobostudio19955 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
ok!ko9 小时前
设计模式之原型模式(通俗易懂--代码辅助理解【Java版】)
java·设计模式·原型模式