一个营销场景:用户通过打卡、分享、邀请新用户注册、浏览商品信息等操作获取积分,这些积分可以兑换一定的福利,比如赠送咪咕视频会员卡、发放实物商品、赠送优惠券等。
先创建一个单独的module,包含三种福利实现的接口。
| 序号 | 类型 | 接口 |
|---|---|---|
| 1 | 咪咕会员卡 | grantToken(String bindMobileNumber, String cardId) |
| 2 | 实物商品 | Boolean deliverGoods(DeliverReq req) |
| 3 | 优惠券 | CouponResult sendCoupon(String uId, String couponNumber, String uuid) |
| 这是这个module的pom文件,它有一个上级cn.xiaobaisai.design,做配置依赖用 |
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>design</artifactId>
<groupId>cn.xiaobaisai.design</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cn-xiaobaisai-design-4.0-0</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
cn.xiaobaisai.design的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.xiaobaisai.design</groupId>
<artifactId>design</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>cn-xiaobaisai-design-4.0-0</module>
<module>cn-xiaobaisai-design-4.0-1</module>
<module>cn-xiaobaisai-design-4.0-2</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- LOGGING begin -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.9</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
这个module的结构:

简单模拟发放咪咕会员卡,不做具体实现
package cn.xiaobaisai.design.card;
/**
* 模拟咪咕会员卡服务
*/
public class MiguCardService {
public void grantToken(String bindMobileNumber, String cardId) {
System.out.println("模拟发放咪咕会员卡一张:" + bindMobileNumber + "," + cardId);
}
}
简单模拟发放实物商品服务,不做具体实现
package cn.xiaobaisai.design.goods;
import com.alibaba.fastjson.JSON;
/**
* 模拟发放实物商品服务
*/
public class GoodsService {
public Boolean deliverGoods(DeliverReq req) {
System.out.println("模拟发货实物商品一个:" + JSON.toJSONString(req));
return true;
}
}
实物需要邮寄,准备一个邮寄信息类,包含邮寄单需要填的字段
package cn.xiaobaisai.design.goods;
public class DeliverReq {
private String userName; // 用户姓名
private String userPhone; // 用户手机
private String sku; // 商品SKU
private String orderId; // 订单ID
private String consigneeUserName; // 收货人姓名
private String consigneeUserPhone; // 收货人手机
private String consigneeUserAddress; // 收获人地址
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPhone() {
return userPhone;
}
public void setUserPhone(String userPhone) {
this.userPhone = userPhone;
}
public String getSku() {
return sku;
}
public void setSku(String sku) {
this.sku = sku;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getConsigneeUserName() {
return consigneeUserName;
}
public void setConsigneeUserName(String consigneeUserName) {
this.consigneeUserName = consigneeUserName;
}
public String getConsigneeUserPhone() {
return consigneeUserPhone;
}
public void setConsigneeUserPhone(String consigneeUserPhone) {
this.consigneeUserPhone = consigneeUserPhone;
}
public String getConsigneeUserAddress() {
return consigneeUserAddress;
}
public void setConsigneeUserAddress(String consigneeUserAddress) {
this.consigneeUserAddress = consigneeUserAddress;
}
}
简单模拟优惠券服务,不做具体实现
package cn.xiaobaisai.design.coupon;
/**
* 模拟优惠券服务
*/
public class CouponService {
public CouponResult sendCoupon(String uId, String couponNumber, String uuid) {
System.out.println("模拟发放优惠券一张:" + uId + "," + couponNumber + "," + uuid);
return new CouponResult("0000", "发放成功");
}
}
优惠券发送过程中有可能因为网络故障等原因出现发送不成功的情况,准备一个优惠券发送结果的类
package cn.xiaobaisai.design.coupon;
/**
* 优惠券返回结果类
*/
public class CouponResult {
private String code; // 编码
private String info; // 描述
public CouponResult(String code, String info) {
this.code = code;
this.info = info;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
至此,三种福利的接口准备好了,如果仅仅是写一个根据奖品类型执行对应操作的过程,直接if……else就行,尤其是目前这个逻辑也只有三种情况,要考虑的情况不算多,先用这种做法实现一版,再用工厂模式实现做对比。
先创建一个mavan的module,在pom里加上三个福利实现的module的依赖,这是pom:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>design</artifactId>
<groupId>cn.xiaobaisai.design</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cn-xiaobaisai-design-4.0-1</artifactId>
<dependencies>
<dependency>
<groupId>cn.xiaobaisai.design</groupId>
<artifactId>cn-xiaobaisai-design-4.0-0</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
先把三种福利要用到的所有参数放一起,做一个奖品请求的类
package cn.xiaobaisai.design;
import java.util.Map;
/**
* 发奖请求对象
*/
public class AwardReq {
private String uId; // 用户唯一ID
private Integer awardType; // 奖品类型(可以用枚举定义);1优惠券、2实物商品、3第三方兑换卡(爱奇艺)
private String awardNumber; // 奖品编号;sku、couponNumber、cardId
private String bizId; // 业务ID,防重复
private Map<String, String> extMap; // 扩展信息
public String getuId() {
return uId;
}
public void setuId(String uId) {
this.uId = uId;
}
public Integer getAwardType() {
return awardType;
}
public void setAwardType(Integer awardType) {
this.awardType = awardType;
}
public String getAwardNumber() {
return awardNumber;
}
public void setAwardNumber(String awardNumber) {
this.awardNumber = awardNumber;
}
public String getBizId() {
return bizId;
}
public void setBizId(String bizId) {
this.bizId = bizId;
}
public Map<String, String> getExtMap() {
return extMap;
}
public void setExtMap(Map<String, String> extMap) {
this.extMap = extMap;
}
}
对应地,准备一个奖励反馈结果的类
package cn.xiaobaisai.design;
/**
* 发奖结果反馈对象
*/
public class AwardRes {
private String code; // 编码
private String info; // 描述
public AwardRes() {
}
public AwardRes(String code, String info) {
this.code = code;
this.info = info;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
if……else方式的具体实现
package cn.xiaobaisai.design;
import cn.xiaobaisai.design.card.MiguCardService;
import cn.xiaobaisai.design.coupon.CouponResult;
import cn.xiaobaisai.design.coupon.CouponService;
import cn.xiaobaisai.design.goods.DeliverReq;
import cn.xiaobaisai.design.goods.GoodsService;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Description: 模拟发奖服务
* @author: sai
* @date: 2023年04月24日 17:41
*/
public class PrizeController {
private Logger logger = LoggerFactory.getLogger(PrizeController.class);
public AwardRes awardUser(AwardReq req){
String reqJson = JSON.toJSONString(req);
AwardRes awardRes = new AwardRes();
try {
logger.info("奖品发放开始{}。req:{}", req.getuId(), reqJson);
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(queryUserPhone(req.getuId()));
deliverReq.setSku(req.getAwardNumber());
deliverReq.setOrderId(req.getBizId());
deliverReq.setConsigneeUserName(req.getExtMap().get("consigneeUserName"));
deliverReq.setConsigneeUserPhone(req.getExtMap().get("consigneeUserPhone"));
deliverReq.setConsigneeUserAddress(req.getExtMap().get("consigneeUserAddress"));
Boolean isSuccess = goodsService.deliverGoods(deliverReq);
if(isSuccess){
awardRes = new AwardRes("0000","发放成功");
}else{
awardRes = new AwardRes("0001","发放失败");
}
}else if (req.getAwardType() == 3) {
String userPhone = queryUserPhone(req.getuId());
MiguCardService miguCardService = new MiguCardService();
miguCardService.grantToken(userPhone,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 queryUserPhone(String uId){
return "18888888888";
}
}
单元测试
package cn.xiaobaisai.design.test;
import cn.xiaobaisai.design.AwardReq;
import cn.xiaobaisai.design.AwardRes;
import cn.xiaobaisai.design.PrizeController;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
/**
* @Description: if……else实现的单元测试
* @author: sai
* @date: 2023年04月24日 20:08
*/
public class ApiTest {
private Logger logger = LoggerFactory.getLogger(ApiTest.class);
@Test
public void testAwardUser(){
PrizeController prizeController = new PrizeController();
System.out.println("\r\n模拟发放优惠券测试\r\n");
AwardReq req01 = new AwardReq();
req01.setuId("10001");
req01.setAwardType(1);
req01.setAwardNumber("YHQ21398412934");
req01.setBizId("27348292348");
AwardRes awardRes01 = prizeController.awardUser(req01);
logger.info("请求参数:{}", JSON.toJSON(req01));
logger.info("测试结果:{}", JSON.toJSON(awardRes01));
System.out.println("\r\n模拟发放实物商品\r\n");
AwardReq req02 = new AwardReq();
req02.setuId("10001");
req02.setAwardType(2);
req02.setAwardNumber("9820198721311");
req02.setBizId("1023000020112221113");
req02.setExtMap(new HashMap<String, String>() {{
put("consigneeUserName", "张伟");
put("consigneeUserPhone", "16666666666");
put("consigneeUserAddress", "吉林省.吉林市.朝阳区.XX街道.帝豪小区.#18-2109");
}});
AwardRes awardRes02 = prizeController.awardUser(req02);
logger.info("请求参数:{}", JSON.toJSON(req02));
logger.info("测试结果:{}", JSON.toJSON(awardRes02));
System.out.println("\r\n第三方兑换会员卡(咪咕)\r\n");
AwardReq req03 = new AwardReq();
req03.setuId("10001");
req03.setAwardType(3);
req03.setAwardNumber("AQY1xjkUodl8LO975GdfrYUio");
AwardRes awardRes03 = prizeController.awardUser(req03);
logger.info("请求参数:{}", JSON.toJSON(req03));
logger.info("测试结果:{}", JSON.toJSON(awardRes03));
}
}
这种方式虽然好写,顺着写if……else就ok了,但是不利于扩展(预感这个话要重复好多遍,因为大多数设计模式的目的就是改if……else,变得更有扩展性一点)
工厂模式的实现
同样先创建一个mavan的module,要加入三种福利服务的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>design</artifactId>
<groupId>cn.xiaobaisai.design</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cn-xiaobaisai-design-4.0-2</artifactId>
<dependencies>
<dependency>
<groupId>cn.xiaobaisai.design</groupId>
<artifactId>cn-xiaobaisai-design-4.0-0</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
创建一个接口,发送福利定义在这个接口里
package cn.xiaobaisai.design.store;
import java.util.Map;
public interface ICommodity {
void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
}
发放咪咕会员卡的具体实现,要实现发放福利接口
package cn.xiaobaisai.design.store.impl;
import cn.xiaobaisai.design.card.MiguCardService;
import cn.xiaobaisai.design.store.ICommodity;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* @Description: 发放咪咕会员卡的服务
* @author: sai
* @date: 2023年04月24日 20:34
*/
public class CardCommodityService implements ICommodity {
private Logger logger = LoggerFactory.getLogger(CardCommodityService.class);
// 模拟注入
private MiguCardService miguCardService = new MiguCardService();
@Override
public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
String userPhone = queryUserPhone(uId);
miguCardService.grantToken(userPhone, bizId);
logger.info("请求参数[咪咕兑换卡] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
logger.info("测试结果[咪咕兑换卡]:success");
}
private String queryUserPhone(String uId){
return "16666666666";
}
}
发放优惠券的具体实现,也要实现发放福利接口
package cn.xiaobaisai.design.store.impl;
import cn.xiaobaisai.design.coupon.CouponResult;
import cn.xiaobaisai.design.coupon.CouponService;
import cn.xiaobaisai.design.store.ICommodity;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* @Description: 发放优惠券的服务
* @author: sai
* @date: 2023年04月24日 20:57
*/
public class CouponCommodityService implements ICommodity {
private Logger logger = LoggerFactory.getLogger(CouponCommodityService.class);
private 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());
}
}
}
发放实物商品的具体实现,也要实现发放福利接口
package cn.xiaobaisai.design.store.impl;
import cn.xiaobaisai.design.goods.DeliverReq;
import cn.xiaobaisai.design.goods.GoodsService;
import cn.xiaobaisai.design.store.ICommodity;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* @Description: 模拟发放实物商品服务
* @author: sai
* @date: 2023年04月24日 21:00
*/
public class GoodsCommodityService implements ICommodity {
private Logger logger = LoggerFactory.getLogger(GoodsCommodityService.class);
private GoodsService goodsService = new GoodsService();
@Override
public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
DeliverReq deliverReq = new DeliverReq();
deliverReq.setUserName(queryUserName(uId));
deliverReq.setUserPhone(queryUserPhone(uId));
deliverReq.setSku(commodityId);
deliverReq.setOrderId(bizId);
deliverReq.setConsigneeUserName(extMap.get("consigneeUserName"));
deliverReq.setConsigneeUserPhone(extMap.get("consigneeUserPhone"));
deliverReq.setConsigneeUserAddress(extMap.get("consigneeUserAddress"));
Boolean isSuccess = goodsService.deliverGoods(deliverReq);
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 queryUserPhone(String uId){
return "18888888888";
}
}
这样把本来写在PrizeController里的根据奖品类型的逻辑分开写具体的类来实现
工厂类,有两种发放福利方式,一种是按照奖品类型实例化对应的发送奖品的服务,另一种是可以传入发送奖品的服务类做对应的实例化。
package cn.xiaobaisai.design;
import cn.xiaobaisai.design.store.ICommodity;
import cn.xiaobaisai.design.store.impl.CardCommodityService;
import cn.xiaobaisai.design.store.impl.CouponCommodityService;
import cn.xiaobaisai.design.store.impl.GoodsCommodityService;
/**
* @Description: 工厂类
* @author: sai
* @date: 2023年04月24日 21:03
*/
public class StoreFactory {
/**
* 按奖品类型实例化
* @param commodityType 奖品类型
* @return 发放奖品服务的实例化对象
*/
public ICommodity getCommodityService(Integer commodityType){
if(1 == commodityType){
return new CouponCommodityService();
}else if (2 == commodityType) {
return new GoodsCommodityService();
}else if (3 == commodityType) {
return new CardCommodityService();
}else{
throw new RuntimeException("奖品类型不存在");
}
}
/**
* 按奖品类信息实例化
* @param clazz 奖品类
* @return
* @throws IllegalAccessException
* @throws InstantiationException
*/
public ICommodity getCommodityService(Class<? extends ICommodity> clazz) throws IllegalAccessException, InstantiationException {
if(null == clazz){
return null;
}
return clazz.newInstance();
}
}
对应的单元测试:
package cn.xiaobaisai.design.test;
import cn.xiaobaisai.design.StoreFactory;
import cn.xiaobaisai.design.store.ICommodity;
import cn.xiaobaisai.design.store.impl.CardCommodityService;
import cn.xiaobaisai.design.store.impl.CouponCommodityService;
import cn.xiaobaisai.design.store.impl.GoodsCommodityService;
import org.junit.Test;
import java.util.HashMap;
/**
* @Description: 工厂模式实现的单元测试
* @author: sai
* @date: 2023年04月24日 21:18
*/
public class ApiTest {
StoreFactory storeFactory = new StoreFactory();
@Test
public void testStoreFactory01() throws Exception {
// 1. 优惠券
ICommodity commodityService_1 = storeFactory.getCommodityService(1);
commodityService_1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);
// 2. 实物商品
ICommodity commodityService_2 = storeFactory.getCommodityService(2);
commodityService_2.sendCommodity("10001", "9820198721311", "1023000020112221113", new HashMap<String, String>() {{
put("consigneeUserName", "张伟");
put("consigneeUserPhone", "16666666666");
put("consigneeUserAddress", "吉林省.吉林市.朝阳区.XX街道.帝豪小区.#18-2109");
}});
// 3. 第三方兑换卡(模拟咪咕)
ICommodity commodityService_3 = storeFactory.getCommodityService(3);
commodityService_3.sendCommodity("10001", "AQY1xjkUodl8LO975GdfrYUio", null, null);
}
@Test
public void testStoreFactory02() throws Exception {
// 1. 优惠券
ICommodity commodityService = storeFactory.getCommodityService(CouponCommodityService.class);
commodityService.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);
// 2. 实物商品
ICommodity commodityService_2 = storeFactory.getCommodityService(GoodsCommodityService.class);
commodityService_2.sendCommodity("10001", "9820198721311", "1023000020112221113", new HashMap<String, String>() {{
put("consigneeUserName", "张伟");
put("consigneeUserPhone", "16666666666");
put("consigneeUserAddress", "吉林省.吉林市.朝阳区.XX街道.帝豪小区.#18-2109");
}});
// 3. 第三方兑换卡(模拟咪咕)
ICommodity commodityService_3 = storeFactory.getCommodityService(CardCommodityService.class);
commodityService_3.sendCommodity("10001", "AQY1xjkUodl8LO975GdfrYUio", null, null);
}
}