小傅哥《重学JAVA设计模式》学习笔记(一):创建者模型之工厂方法模式

1.工厂方式模式概述

工厂方法模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型。
这种设计模式也是 Java 开发中最常见的一种模式,它的主要意图是定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
简单说就是为了提供代码结构的扩展性,屏蔽每一个功能类中的具体实现逻辑。让外部可以更加简单的,只是知道调用即可。主要解决接口选择的问题。

1.1 工厂方法模式优缺点

优点:

  • 用户只需要知道具体工厂的名称就可以得到所需要的产品,无须知道产品的具体创建过程;
  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
    缺点:
    每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

1.2 工厂方法模式核心结构

工厂方法模式是在简单工厂模式上的改进,主要包含如下几个角色及组件:

  • 抽象工厂(Creator):整个工厂模式的核心角色,它与应用无关,主要在创建模式中规范和产品对应的工厂对象的标准化定义。
  • 具体工厂(ConcreteCreator):实现了抽象工厂的具体工厂类,该类型是和应用直接交互的具体实现类,在应用程序中调用,用于创建产品对象。
  • 抽象产品(Product):工厂方法模式创建的所有类型的超级父类,该类型和具体业务有关,用于规范工厂方法模式中创建的方法对象具备的公共特征行为。
  • 具体产品(Concrete Product):该类型实现了抽象产品 父类,是工厂方法模式中具体创建的实例对象。

1.3 UML 类图

工厂方法模式

2. 案例实现

案例描述: 模拟营销场景下用户通过积分兑换不同类型的商品(如兑换卡、优惠券、实物商品等)

模拟发奖操作
现在有如下三种类型的商品接口:

序号类型接口
1优惠券CouponResult sendCoupon(String uId, String couponNumber, String uuid)
2实物商品Boolean deliverGoods(DeliverReq req)
3第三方爱奇艺兑换卡CouponResult sendCoupon(String uId, String couponNumber, String uuid)

代码实现

2.1 一坨坨if/else实现

2.1.1 工程结构
FACTORY-METHOD-MODE-00
├─src
│  └─main
│      └─java
│          └─org
│              └─itstack
│                  └─factorymethod
│                      └─design
│                          ├─card
│                          │      IQiYiCard.java
│                          │      IQiYiCardService.java
│                          ├─coupon
│                          │      CouponInfo.java
│                          │      CouponResult.java
│                          │      CouponService.java
│                          └─goods
│                                  DeliverReq.java
│                                  GoodsInfo.java
│                                  GoodsService.java
FACTORY-METHOD-MODE-01
├─src
│  ├─main
│  │  └─java
│  │      └─org
│  │          └─itstack
│  │              └─factorymethod
│  │                  └─design
│  │                          AwardReq.java
│  │                          AwardRes.java
│  │                          PrizeController.java
│  └─test
│      └─java
│          └─org
│              └─itstack
│                  └─factorymethod
│                      └─test
│                           └─  ApiTest.java
└─  pom.xml
2.1.2 if/else 代码实现
factory-method-mode-00

模拟爱奇艺会员卡服务

/**
 * 模拟爱奇艺会员卡服务
 */
public class IQiYiCardService {
    /**
     * 生成token
     *
     * @param bindMobileNumber 绑定的手机号码
     * @param cardId           会员卡Id
     */
    public void grantToken(String bindMobileNumber, String cardId) {
        System.out.println("模拟发放爱奇艺会员卡一张:" + bindMobileNumber + "," + cardId);
    }
}

模拟优惠券服务

/**
 * 优惠券返回结果对象
 */
public class CouponResult {

    /**
     * 编码
     */
    private String code;

    /**
     * 描述
     */
    private String info;

    // ...get set 构造函数
}
/**
 * 模拟优惠券服务
 */
public class CouponService {
    /**
     * 模拟发放优惠券
     * @param uId 优惠券Id
     * @param couponNumber 优惠券数量
     * @param uuid uuid
     * @return
     */
    public CouponResult sendCoupon(String uId, String couponNumber, String uuid) {
        System.out.println("模拟发放优惠券一张:" + uId + "," + couponNumber + "," + uuid);
        return new CouponResult("0000", "发放成功");
    }
}

模拟发放实物商品服务

/**
 * 实物商品请求对象
 */
public class DeliverReq {
    /**
     * 用户姓名
     */
    private String userName;

    /**
     * 用户手机
     */
    private String userPhone;

    /**
     * 商品SKU
     */
    private String sku;

    /**
     * 订单ID
     */
    private String orderId;

    /**
     * 收件人姓名
     */
    private String consigneeUserName;

    /**
     * 收件手机
     */
    private String consigneeUserPhone;

    /**
     * 收件人地址
     */
    private String consigneeUserAddress;
    //getter setter
}
/**
 * 模拟实物商品服务
 */
public class GoodsService {
    /**
     * 是否发放实物奖品
     * @param req
     * @return
     */
    public Boolean deliverGoods(DeliverReq req) {
        System.out.println("模拟发货实物商品一个:" + JSON.toJSONString(req));
        return true;
    }
}
factory-method-mode-01
/**
 * 发奖请求对象
 */
public class AwardReq {
    /**
     * 用户唯一ID
     */
    private String uId;
    /**
     * 奖品类型(可以用枚举定义);1优惠券、2实物商品、3第三方兑换卡(爱奇艺)
     */
    private Integer awardType;
    /**
     * 奖品编号;sku、couponNumber、cardId
     */
    private String awardNumber;
    /**
     * 业务ID,防重复
     */
    private String bizId;
    /**
     * 扩展信息
     */
    private Map<String, String> extMap;
    // get set
}
/**
 * 发奖响应对象
 */
public class AwardRes {
    /**
     * 编码
     */
    private String code;

    /**
     * 信息
     */
    private String info;
    // get set
}    
/**
 * 模拟发奖服务
 */
public class PrizeController {
    private final Logger logger = LoggerFactory.getLogger(PrizeController.class);

    /**
     * 模拟给用户发奖
     *
     * @param req
     * @return
     */
    public AwardRes awardToUser(AwardReq req) {
        String reqJson = JSON.toJSONString(req);
        AwardRes awardRes = null;
        try {
            logger.info("奖品发放开始{},req:{}", req.getuId(), reqJson);
            // 按照不同类型发放奖品[1->优惠券,2->实物商品,3->第三方兑换卡(爱奇艺)]
            if (req.getAwardType() == 1) {
                CouponService couponService = new CouponService();
                CouponResult couponResult = couponService.sendCoupon(req.getuId(), req.getAwardNumber(), req.getBizId());
                if ("0000".equals(couponResult.getCode())) {
                    awardRes = new AwardRes("0000", "发放成功");
                } else {
                    awardRes = new AwardRes("0001", couponResult.getInfo());
                }
            } else if (req.getAwardType() == 2) {
                GoodsService goodsService = new GoodsService();
                DeliverReq deliverReq = new DeliverReq();
                deliverReq.setUserName(queryUserName(req.getuId()));
                deliverReq.setUserPhone(queryUserPhoneNumber(req.getuId()));
                deliverReq.setSku(req.getAwardNumber());
                deliverReq.setOrderId(req.getBizId());
                deliverReq.setConsigneeUserName(req.getExtMap().get("consigneeUserName"));
                deliverReq.setConsigneeUserAddress(req.getExtMap().get("consigneeUserAddress"));
                deliverReq.setConsigneeUserPhone(req.getExtMap().get("consigneeUserPhone"));
                Boolean isSuccess = goodsService.deliverGoods(deliverReq);
                if (isSuccess) {
                    awardRes = new AwardRes("0000", "发放成功");
                } else {
                    awardRes = new AwardRes("0001", "发放失败");
                }
            } else if (req.getAwardType() == 3) {
                String bindMobileNumber = queryUserPhoneNumber(req.getuId());
                IQiYiCardService iQiYiCardService = new IQiYiCardService();
                iQiYiCardService.grantToken(bindMobileNumber, req.getAwardNumber());
                awardRes = new AwardRes("0000", "发放成功");
            }
            logger.info("奖品发放完成{}", req.getuId());
        } catch (Exception e) {
            logger.error("奖品发放失败{},req:{}", req.getuId(), reqJson, e);
            awardRes = new AwardRes("0001", e.getMessage());
        }
        return awardRes;
    }

    private String queryUserName(String uId) {
        return "花花";
    }

    private String queryUserPhoneNumber(String uId) {
        return "15200101232";
    }
}
  • 如上就是使用 ifelse 非常直接的实现出来业务需求的一坨代码,如果仅从业务角度看,研发如期甚至提前实现了功能。
  • 那这样的代码目前来看并不会有什么问题,但如果在经过几次的迭代和拓展,接手这段代码的研发将十分痛苦。重构成本高需要理清之前每一个接口的使用,测试回归验证时间长,需要全部验证一
    次。这也就是很⼈人并不愿意接手别人的代码,如果接手了又被压榨开发时间。那么可想而知这样的 ifelse 还会继续增加。
2.1.3 测试验证
@Test
    public void test_awardToUser() {
        PrizeController prizeController = new PrizeController();
        System.out.println("\r\n模拟发放优惠券测试\r\n");
        // 模拟发放优惠券测试
        AwardReq couponReq = new AwardReq();
        couponReq.setuId("10001");
        couponReq.setAwardType(1);
        couponReq.setAwardNumber("EGM1023938910232121323432");
        couponReq.setBizId("791098764902132");
        AwardRes couponAwardRes01 = prizeController.awardToUser(couponReq);
        logger.info("请求参数:{}", JSON.toJSON(couponReq));
        logger.info("测试结果:{}", JSON.toJSON(couponAwardRes01));
        System.out.println("---------------------------------------------------------");
        System.out.println("\r\n模拟发放实物商品\r\n");
        AwardReq goodsReq = new AwardReq();
        goodsReq.setuId("10001");
        goodsReq.setAwardType(2);
        goodsReq.setAwardNumber("9820198721311");
        goodsReq.setBizId("1023000020112221113");
        goodsReq.setExtMap(new HashMap<String,String>(){
            {
                put("consigneeUserName","蟹不肉");
                put("consigneeUserPhone","15200292123");
                put("consigneeUserAddress","上海市.浦东新区.高行镇.东靖路393弄.#44号1002");
            }
        });
        AwardRes goodsAwardRes01 = prizeController.awardToUser(goodsReq);
        logger.info("请求参数:{}", JSON.toJSON(goodsReq));
        logger.info("测试结果:{}", JSON.toJSON(goodsAwardRes01));
        System.out.println("---------------------------------------------------------");
        System.out.println("\r\n第三方兑换卡(爱奇艺)\r\n");
        AwardReq iQiYiCardReq = new AwardReq();
        iQiYiCardReq.setuId("10001");
        iQiYiCardReq.setAwardType(3);
        iQiYiCardReq.setAwardNumber("AQY1xjkUodl8LO975GdfrYUio");

        AwardRes iQiYiCardAwardRes = prizeController.awardToUser(iQiYiCardReq);
        logger.info("请求参数:{}", JSON.toJSON(iQiYiCardReq));
        logger.info("测试结果:{}", JSON.toJSON(iQiYiCardAwardRes));
    }

结果

模拟发放优惠券测试

16:18:29.874 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放开始10001,req:{"awardNumber":"EGM1023938910232121323432","awardType":1,"bizId":"791098764902132","uId":"10001"}
模拟发放优惠券一张:10001,EGM1023938910232121323432,791098764902132
16:18:29.883 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放完成10001
16:18:29.884 [main] INFO org.itstack.factorymethod.test.ApiTest - 请求参数:{"uId":"10001","bizId":"791098764902132","awardNumber":"EGM1023938910232121323432","awardType":1}
16:18:29.889 [main] INFO org.itstack.factorymethod.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"}
---------------------------------------------------------

模拟发放实物商品

16:18:29.890 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放开始10001,req:{"awardNumber":"9820198721311","awardType":2,"bizId":"1023000020112221113","extMap":{"consigneeUserName":"蟹不肉","consigneeUserPhone":"15200292123","consigneeUserAddress":"上海市.浦东新区.高行镇.东靖路393弄.#44号1002"},"uId":"10001"}
模拟发货实物商品一个:{"consigneeUserAddress":"上海市.浦东新区.高行镇.东靖路393弄.#44号1002","consigneeUserName":"蟹不肉","consigneeUserPhone":"15200292123","orderId":"1023000020112221113","sku":"9820198721311","userName":"花花","userPhone":"15200101232"}
16:18:29.897 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放完成10001
16:18:29.897 [main] INFO org.itstack.factorymethod.test.ApiTest - 请求参数:{"extMap":{"consigneeUserName":"蟹不肉","consigneeUserAddress":"上海市.浦东新区.高行镇.东靖路393弄.#44号1002","consigneeUserPhone":"15200292123"},"uId":"10001","bizId":"1023000020112221113","awardNumber":"9820198721311","awardType":2}
16:18:29.897 [main] INFO org.itstack.factorymethod.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"}
---------------------------------------------------------

第三方兑换卡(爱奇艺)

16:18:29.898 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放开始10001,req:{"awardNumber":"AQY1xjkUodl8LO975GdfrYUio","awardType":3,"uId":"10001"}
模拟发放爱奇艺会员卡一张:15200101232,AQY1xjkUodl8LO975GdfrYUio
16:18:29.899 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放完成10001
16:18:29.899 [main] INFO org.itstack.factorymethod.test.ApiTest - 请求参数:{"uId":"10001","awardNumber":"AQY1xjkUodl8LO975GdfrYUio","awardType":3}
16:18:29.899 [main] INFO org.itstack.factorymethod.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"}

Process finished with exit code 0
  • 运行结果正常,满足当前所有业务产品需求,但是难以维护。

2.2 工厂方法模式代码优化

2.2.1 UML类图

工厂方法模式

2.2.2 工程结构
FACTORY-METHOD-MODE-02
├─src
│  ├─main
│  │  ├─java
│  │  │  └─org
│  │  │      └─itstack
│  │  │          └─factorymethod
│  │  │              └─design
│  │  │                  │  StoreFactory.java
│  │  │                  │
│  │  │                  └─store
│  │  │                      │  ICommodity.java
│  │  │                      │
│  │  │                      └─impl
│  │  │                              CardCommodityService.java
│  │  │                              CouponCommodityService.java
│  │  │                              GoodsCommodityService.java
│  │  │
│  │  └─resources
│  └─test
│      └─java
│          └─org
│              └─itstack
│                  └─factorymethod
│                      └─test
│                              ApiTest.java

2.2.3 代码实现
2.2.3.1 定义发奖接口
public interface ICommodity {
    /**
     * 发奖
     *
     * @param uId 用户Id
     * @param commodityId 奖品Id
     * @param bizId 业务Id
     * @param extMap 扩展字段
     * @throws Exception e
     */
    void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
}
  • 所有的奖品无论是实物、虚拟还是第三方,都需要通过我们的程序实现此接口进行处理,以保证最终入参出参的统⼀性。
  • 接口的入参包括; 用户ID 、 奖品ID 、 业务ID 以及 扩展字段用于处理发放实物商品时的收货地址。
2.2.3.2 实现奖品发放接口
优惠券
/**
 * 优惠券奖品服务
 */
public class CouponCommodityService implements ICommodity {
    private final Logger logger = LoggerFactory.getLogger(CouponCommodityService.class);
    
    private final CouponService couponService = new CouponService();
    @Override
    public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
        CouponResult couponResult = couponService.sendCoupon(uId, commodityId, bizId);
        logger.info("请求参数[优惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
        logger.info("测试结果[优惠券]:{}", JSON.toJSON(couponResult));
        if (!"0000".equals(couponResult.getCode())) throw new RuntimeException(couponResult.getInfo());
    }
}
实物商品
/**
 * 实物商品服务
 */
public class GoodsCommodityService implements ICommodity {
    private final Logger logger = LoggerFactory.getLogger(GoodsCommodityService.class);

    /**
     * 模拟注入商品服务
     */
    private final GoodsService goodsService = new GoodsService();

    @Override
    public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
        DeliverReq req = new DeliverReq();
        req.setUserPhone(queryUserPhoneNumber(uId));
        req.setUserName(queryUserName(uId));
        req.setSku(commodityId);
        req.setOrderId(bizId);
        req.setConsigneeUserName(extMap.get("consigneeUserName"));
        req.setConsigneeUserPhone(extMap.get("consigneeUserPhone"));
        req.setConsigneeUserAddress(extMap.get("consigneeUserAddress"));
        Boolean isSuccess = goodsService.deliverGoods(req);
        logger.info("请求参数[优惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
        logger.info("测试结果[优惠券]:{}", isSuccess);
        if (!isSuccess) throw new RuntimeException("实物商品发送失败");
    }

    private String queryUserName(String uId) {
        return "花花";
    }

    private String queryUserPhoneNumber(String uId) {
        return "15200101232";
    }
}
第三方兑换卡
/**
 * 爱奇艺兑换卡服务实现
 */
public class CardCommodityService implements ICommodity {
    private final Logger logger = LoggerFactory.getLogger(CardCommodityService.class);
    /**
     * 模拟注入
     */
    private final IQiYiCardService iQiYiCardService = new IQiYiCardService();
    @Override
    public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
        String mobileNumber = queryUserMobile(uId);
        iQiYiCardService.grantToken(mobileNumber,commodityId);
        logger.info("请求参数[爱奇艺兑换卡] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
        logger.info("测试结果[爱奇艺兑换卡]:success");
    }

    private String queryUserMobile(String uId) {
        return "15200101232";
    }
}
  • 从上面可以看到每一种奖品的实现都包括在自己的类中,新增、修改或者删除都不会影响其他奖品功能的测试,降低回归测试的可能。
  • 后续在新增的奖品只需要按照此结构进行填充即可,非常易于维护和扩展。
  • 在统一了入参以及出参后,调用方不在需要关心奖品发放的内部逻辑,按照统一的方式即可处理。
2.2.3.3 创建商店工厂
/**
 * 商店工厂类
 */
public class StoreFactory {

    private static final Map<Integer, ICommodity> commodityServiceMap = new HashMap<>();

    static {
        commodityServiceMap.put(1, new CouponCommodityService());
        commodityServiceMap.put(2, new GoodsCommodityService());
        commodityServiceMap.put(3, new CardCommodityService());
    }

    /**
     * 工厂模式根据商品类型获取对应service(map配置结构)
     *
     * @param commodityType 商品类型
     * @return ICommodity
     */
    public ICommodity getCommodityServiceByMap(Integer commodityType) {
        return Optional.ofNullable(commodityType)
                .map(commodityServiceMap::get)
                .orElseThrow(() -> new RuntimeException("不存在的商品服务类型"));
//        if (null == commodityType) return null;
//        ICommodity iCommodity = commodityServiceMap.get(commodityType);
//        if (null == iCommodity) {
//            throw new RuntimeException("不存在的商品服务类型");
//        } else {
//            return iCommodity;
//        }
    }
    
    /**
     * 工厂模式根据商品类型获取对应service(switch判断)
     *
     * @param commodityType 商品类型
     * @return ICommodity
     */
    public ICommodity getCommodityService(Integer commodityType) {
        if (null == commodityType) return null;
        switch (commodityType) {
            case 1:
                return new CouponCommodityService();
            case 2:
                return new GoodsCommodityService();
            case 3:
                return new CardCommodityService();
            default:
                throw new RuntimeException("不存在的商品服务类型");
        }
    }
}
  • 定义一个商店的⼯厂类,在⾥面按照类型实现各种商品的服务。可以⾮非常干净整洁的处理我们的代码,后续新增的商品在这里扩展即可。
  • 这里定义了两种不同的处理方式,getCommodityService 通过if判断获取接口,getCommodityServiceByMap通过map方式获取相对应的接口。
2.2.4 测试验证

**getCommodityService **

@Test
    public void test_commodity() throws Exception {
        StoreFactory storeFactory = new StoreFactory();

        // 1. 优惠券
        ICommodity commodityService_1 = storeFactory.getCommodityService(1);
        commodityService_1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);

        // 2. 实物商品
        ICommodity commodityService_2 = storeFactory.getCommodityService(2);
        Map<String,String> extMap = new HashMap<String,String>();
        extMap.put("consigneeUserName", "谢飞机");
        extMap.put("consigneeUserPhone", "15200292123");
        extMap.put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");

        commodityService_2.sendCommodity("10001","9820198721311","1023000020112221113",new HashMap<String, String>() {{
            put("consigneeUserName", "谢飞机");
            put("consigneeUserPhone", "15200292123");
            put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");
        }});

        // 3. 第三方兑换卡(爱奇艺)
        ICommodity commodityService_3 = storeFactory.getCommodityService(3);
        commodityService_3.sendCommodity("10001","AQY1xjkUodl8LO975GdfrYUio",null,null);
    }

**getCommodityServiceByMap **

@Test
    public void test_commodity2() throws Exception {
        StoreFactory storeFactory = new StoreFactory();

        // 1. 优惠券
        ICommodity commodityService_1 = storeFactory.getCommodityServiceByMap(1);
        commodityService_1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);

        // 2. 实物商品
        ICommodity commodityService_2 = storeFactory.getCommodityServiceByMap(2);
        Map<String,String> extMap = new HashMap<String,String>();
        extMap.put("consigneeUserName", "谢飞机");
        extMap.put("consigneeUserPhone", "15200292123");
        extMap.put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");

        commodityService_2.sendCommodity("10001","9820198721311","1023000020112221113",new HashMap<String, String>() {{
            put("consigneeUserName", "谢飞机");
            put("consigneeUserPhone", "15200292123");
            put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");
        }});

        // 3. 第三方兑换卡(爱奇艺)
        ICommodity commodityService_3 = storeFactory.getCommodityServiceByMap(3);
        commodityService_3.sendCommodity("10001","AQY1xjkUodl8LO975GdfrYUio",null,null);
    }

测试结果

模拟发放优惠券一张:10001,EGM1023938910232121323432,791098764902132
09:54:47.287 [main] INFO org.itstack.factorymethod.design.store.impl.CouponCommodityService - 请求参数[优惠券] => uId:10001 commodityId:EGM1023938910232121323432 bizId:791098764902132 extMap:null
09:54:47.328 [main] INFO org.itstack.factorymethod.design.store.impl.CouponCommodityService - 测试结果[优惠券]{"code":"0000","info":"发放成功"}
模拟发货实物商品一个:{"consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserName":"谢飞机","consigneeUserPhone":"15200292123","orderId":"1023000020112221113","sku":"9820198721311","userName":"花花","userPhone":"15200101232"}
09:54:47.338 [main] INFO org.itstack.factorymethod.design.store.impl.GoodsCommodityService - 请求参数[优惠券] => uId:10001 commodityId:9820198721311 bizId:1023000020112221113 extMap:{"consigneeUserName":"谢飞机","consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserPhone":"15200292123"}
09:54:47.338 [main] INFO org.itstack.factorymethod.design.store.impl.GoodsCommodityService - 测试结果[优惠券]:true
模拟发放爱奇艺会员卡一张:15200101232,AQY1xjkUodl8LO975GdfrYUio
09:54:47.338 [main] INFO org.itstack.factorymethod.design.store.impl.CardCommodityService - 请求参数[爱奇艺兑换卡] => uId:10001 commodityId:AQY1xjkUodl8LO975GdfrYUio bizId:null extMap:null
09:54:47.339 [main] INFO org.itstack.factorymethod.design.store.impl.CardCommodityService - 测试结果[爱奇艺兑换卡]:success

Process finished with exit code 0
  • 运行结果正常

3.总结

工厂方法模式的主要思想是继承,修改符合开闭原则;但每个工厂只能创建一种类型的产品。
通过上面的实战我们可以总结出它的一些优点:避免创建者与具体的产品逻辑耦合、满足单一职责,每一个业务逻辑实现都在所属自己的类中完成、满足开闭原则,无需更改使用调用方就可以在程序中引入新的产品类型。但这样也会带来一些问题,如果有非常多的奖品类型,那么实现的子类会极速扩张。

更多精彩内容,请关注微信公众号:bugstack虫洞栈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值