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版
相关推荐
uzong2 小时前
后端线上发布计划模板
后端
uzong3 小时前
软件工程师应该关注的几种 UML 图
后端
上进小菜猪4 小时前
基于 YOLOv8 的 100 类中药材智能识别实战 [目标检测完整源码]
后端
码事漫谈5 小时前
AI 技能工程入门:从独立能力到协作生态
后端
码事漫谈5 小时前
构建高并发AI服务网关:C++与gRPC的工程实践
后端
颜酱7 小时前
前端必备动态规划的10道经典题目
前端·后端·算法
半夏知半秋7 小时前
rust学习-闭包
开发语言·笔记·后端·学习·rust
LucianaiB8 小时前
【保姆级教程】10分钟把手机变成AI Agent:自动刷课、回消息,学不会我“退网”!
后端
Mr -老鬼8 小时前
功能需求对前后端技术选型的横向建议
开发语言·前端·后端·前端框架
IT=>小脑虎8 小时前
Go语言零基础小白学习知识点【基础版详解】
开发语言·后端·学习·golang