工厂方法
工厂方法,又可以称为虚拟构造函数、Factory Method;其在父类中提供了一个创建对象的方法,允许子类决定实例化对象的类型。
结构实现
如上图所示:
- 抽象产品(Product):定义产品的接口。
- 具体产品(Concrete):返回具体类型。
- 抽象工厂类(Creator):定义一个接口,包含一个抽象的工厂方法
- 具体工厂(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 判断也会对应增加,从而使维护更难。因此这时需要搭配设计模式使用。