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版
相关推荐
程序无bug7 分钟前
手写Spring框架
java·后端
JohnYan9 分钟前
模板+数据的文档生成技术方案设计和实现
javascript·后端·架构
全干engineer20 分钟前
Spring Boot 实现主表+明细表 Excel 导出(EasyPOI 实战)
java·spring boot·后端·excel·easypoi·excel导出
Da_秀24 分钟前
软件工程中耦合度
开发语言·后端·架构·软件工程
GodKeyNet1 小时前
设计模式-责任链模式
java·设计模式·责任链模式
蓝易云1 小时前
Qt框架中connect()方法的ConnectionType参数使用说明 点击改变文章字体大小
linux·前端·后端
a_Dragon11 小时前
Spring Boot多环境开发-Profiles
java·spring boot·后端·intellij-idea
用户8324951417321 小时前
Maven 项目打包:实现业务代码与第三方依赖分离
后端
发仔1231 小时前
解析实时推荐系统的数据流向
后端
ChinaRainbowSea1 小时前
补充:问题:CORS ,前后端访问跨域问题
java·spring boot·后端·spring