简单工厂设计模式 示例
说明:参照小傅哥的《重学JAVA设计模式》
-
模仿实际业务 发放礼品
- 简单工厂模式是创建型设计模式的一种,这种设计模式提供了按需创建对象的最佳方式。
- 在本示例中使用了最接近的实现 模拟了网络请求
- 在发放奖品的时候根据奖品类型去实现对奖品的一系列处理,减少了if…else if…esle的判断
- 使用容器化的实例 构建Icommodity工厂,在servic层直接调用 Icommodity接口的实现,
- 就算以后扩展别的奖品类型 也是自己在store.impl下实现Icommodity接口,在枚举中添加实现类就可以。无需改变其他的层面的处理。
- 业务及部分代码来着 重学JAVA设计模式-小傅哥一书中,其中缺少了部分业务实现,简化了部分实现。
构造entity
@Data
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; // 扩展信息
}
接下来 咱们声明个枚举 用来作为商品类型。其中有个属性 一个是type 另外一个是Class<?> 这里可能有朋友不大了解为啥使用Class<?> 别着急 后续 会给出解释的 先记住!
@Getter
@AllArgsConstructor
public enum CommodityType {
CARD(1, CardCommodityService.class),
COUpon(2, CouponCommodityService.class),
goods(3, GoodsCommodityService.class);
private int type;
private Class<?> clazz;
public static Class<?> macth(Integer type) {
if (type == null || type <= 0) {
return null;
}
CommodityType commodityType = Stream.of(values()).filter(e -> e.getType() == type).findFirst().orElse(null);
return commodityType != null ? commodityType.clazz : null;
}
}
声明枚举后 我们也看到了三个service 分别是兑换卡、优惠券、实物商品,三个Service,接下来 咱们看看着三个Service实现的业务
/**
*实物商品处理
*/
@Slf4j
@Component
public class GoodsCommodityService implements ICommodity {
/**
* 发放的实物商品处理
* @param req
* @return
*/
@Override
public Map<String, Object> sendCommodity(AwardReq req) {
Map<String, Object> map = new HashMap<>();
User user = new User(req.getUId(),"王五","13678948545");
map.put("user", user);
//处理业务
// 比如获取商品信息,通知服务部进行发放礼品,
// mq消息队列可实现,发放的结果应该是发放中,礼品邮寄出去后,服务部进行通知给用户
String comodityName ="笔记本";
String result ="发放成功,您的商品:"+comodityName+"已通知客服人员进行邮寄发放,具体进度请留意短信或进入XXAPP查看进度";
map.put("result",result);
return map;
}
}
-----------------------------------------------------------------------
@Slf4j
@Component
public class CouponCommodityService implements ICommodity {
//小傅哥还有一个卡片的service但是 这里为了方便 简化其代码
@Override
public Map<String, Object> sendCommodity(AwardReq req) {
Map<String, Object> map = new HashMap<>();
User user = queryUserMobile(req.getUId());
map.put("user", user);
//模拟发放,将获取优惠券信息,进行业务处理,
//模拟获取礼品,此处应该查询的是兑换卡的数据
String commodityId = getCommodity(req.getAwardNumber());
//方式1.存储在卡包
String desc = "发放成功,已存储在您的卡包中,可前往查看";
map.put("result", desc);
log.info("请求参数[优惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", req.getUId(), commodityId, req.getBizId(), JSONObject.toJSONString(req.getExtMap()));
log.info("测试结果[优惠券]:success");
return map;
}
/**
* 这个应该是个获取优惠券的方法,此处省略
*
* @param awardNumber
* @return
*/
private String getCommodity(String awardNumber) {
return "20211113-145184";
}
private User queryUserMobile(String uId) {
return new User(uId,"李四","13355696984");
}
}
---------------------------------------------------------------------------
@Slf4j
@Component
public class CardCommodityService implements ICommodity {
//小傅哥还有一个卡片的service但是 这里为了方便 简化其代码
@Override
public Map<String, Object> sendCommodity(AwardReq req) {
Map<String, Object> map = new HashMap<>();
User user = queryUserMobile(req.getUId());
map.put("user", user);
//模拟获取礼品,此处应该查询的是兑换卡的数据
String commodityId = getCommodity(req.getAwardNumber());
//模拟发放,将获取的兑换卡信息,进行业务处理,
//方式1.存储在卡包
//方式2.直接进行兑换 比如兑换成会员。就进行开通会员业务,然后返回开通是否成功,也可以进行异步,或者消息队列等各种方式
String desc = "兑换成功,已为您开通爱奇艺会员一个月";
map.put("result", desc);
log.info("请求参数[爱奇艺兑换卡] => uId:{} commodityId:{} bizId:{} extMap:{}", req.getUId(), commodityId, req.getBizId(), JSONObject.toJSONString(req.getExtMap()));
log.info("测试结果[爱奇艺兑换卡]:success");
return map;
}
private String getCommodity(String awardNumber) {
return "IQIYI-121313";
}
private User queryUserMobile(String uId) {
return new User(uId, "张三", "15200101232");
}
}
细心的朋友 已经发现了 这三个Service都实现了ICommodity 接口,其实呢ICommodity 接口中的sendCommodity函数就是一个发送商品的抽象,有的朋友肯定想到了之前我们定义的枚举,没错我们接下来就是通过spring的ApplicationContext和枚举中的type属性获取对应的发送商品实现,代码如下
@Component
public class StoreFactory {
/**
* 奖品类型方式实例化
*
* @param commodityType 奖品类型
* @return 实例化对象
*/
public ICommodity getCommodityService(Integer commodityType) {
Class<?> bean = CommodityType.macth(commodityType);
if (bean == null) {
throw new RuntimeException("不存在的奖品服务类型");
}
ICommodity iCommodity = (ICommodity) SpringUtil.getBean(bean);
return iCommodity;
}
/**
* 奖品类型方式实例化
* @param commodityType 奖品类型
* @return 实例化对象
*/
public ICommodity getInstance(Integer commodityType) throws IllegalAccessException, InstantiationException {
Class<?> bean = CommodityType.macth(commodityType);
if (bean == null) {
throw new RuntimeException("不存在的奖品服务类型");
}
return (ICommodity) bean.newInstance();
}
}
-------------------------------------附带个简单的Spring工具类---------------------
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringUtil.applicationContext==null){
SpringUtil.applicationContext=applicationContext;
}
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
有的朋友可能不了解 还是简单的说下 想要获取ApplicationContext容器 就得先实现ApplicationContextAware 其中有个方法setApplicationContext();需重写 这个是给当前对象设置获取ApplicationContext用的 通过这个我们可以根据类型,或者beanName的方式获取Spring容器中的bean。
到了这其实我们的简单工厂已经完成了只需再结合实际业务就可以了 业务代码我也做了模拟分别从controller向下层进行实现
Controller
@RestController
@RequestMapping("/award")
public class PrizeController {
@Autowired
private PrizeService prizeService;
@PostMapping
public R awardToUser(@RequestBody AwardReq req){
return R.success(prizeService.sendAward(req));
}
}
service及其实现
public interface PrizeService {
/**
* 发放奖励
* @param req
* @return
*/
Map<String,Object> sendAward(AwardReq req);
}
Impl
@Service
public class PrizeServiceImpl implements PrizeService {
/**
* 注入工厂
*/
@Autowired
private StoreFactory storeFactory;
@Override
public Map<String,Object> sendAward(AwardReq req) {
ICommodity commodityService = storeFactory.getCommodityService(req.getAwardType());
return commodityService.sendCommodity(req);
}
}
总结:总的来说简单工厂就是将同一属性下的不同的业务实现 进行工厂化, 外层只需关注我想得到什么,工厂内部与外层无关,当然如果你只是单一的实现或者小部分的且确定没有业务扩展,为了项目进度需要 也是可以写个if else if 如果有扩展或者多个实现, 建议开发之前进行设计模式的使用或实现, 为后续扩展做准备!
万一用到了呢?那你岂不是装了一波?