假如做一个购物车下单,计算商品,优惠卷、配送费价格功能,普通用户收取费用、运费跟vip、内部用户计算价格肯定不一样的。
一般代码写法:
String userType="normal"; if(userType.equals("normal")){ //普通用户执行价格逻辑 }else if(userType.equals("internalUserCart")){ //内部用户执行 }else { //其他用户执行 }
将业务逻辑集中写在请求方法中,好点的话,可以分开创建不同类执行(用户类、vip、内部用户),这样的话有一些重复性代码,比如三个角色商品价格获取,只是返回的时候,根据业务规定计算出价格。
其实,三种购物车计算价格,可以把重复的代码放在抽象类中,不同的价格计算由子类去实现。实现方法如下:
1、创建抽象类
这里只做演示,参数、方法逻辑根据实际情况定义。
public abstract class AbstractCar { public void process(long userId, Map<String,Object> item){ //大量逻辑处理代码 log.info("计算购物车 userId:{},{}",userId,item); } //优惠卷 protected abstract void processCouponPrice(long userId, Map<String,Object> item); //配送费价格 protected abstract void processDeliveryPrice(long userId, Map<String,Object> item); }
2、创建普通用户,继承抽象类
@Service("normalUserCart") public class NormalUserCart extends AbstractCar{ // 普通用户的购物车 NormalUserCart,实现的是 0 优惠和 10% 运费的逻辑: @Override protected void processCouponPrice(long userId, Map<String, Object> item) { log.info("普通用户,计算优惠卷价格{}",userId); } @Override protected void processDeliveryPrice(long userId, Map<String, Object> item) { log.info("普通用户,计算配送费价格{}",userId); } }
注意:添加@Service注解,后面需要通过spring容器管理,获取该对象。
3、vip用户,继承普通用户
@Slf4j @Service("vipUserCart") public class VipUserCart extends NormalUserCart{ // VipUserCart,直接继承了 NormalUserCart,只需要修改多买优惠策 @Override protected void processCouponPrice(long userId, Map<String, Object> item) { log.info("VipUserCart processCouponPrice:{}",userId); } }
vip用户跟普通用户,除了优惠卷外,其他计算方式一样,所以直接继承。
4、内部用户
@Slf4j @Service("internalUserCart") public class InternalUserCart extends AbstractCar{ // 内部用户 @Override protected void processCouponPrice(long userId, Map<String, Object> item) { log.info("InternalUserCart processCouponPrice:{}",userId); } @Override protected void processDeliveryPrice(long userId, Map<String, Object> item) { log.info("InternalUserCart processDeliveryPrice:{}",userId); } }
5、请求方法测试
// 模拟用户类型 String userCategory = "internalUserCart"; AbstractCar car = applicationContext.getBean(userCategory, AbstractCar.class); Map<String,Object> item = new HashMap<>(); item.put("name","car"); item.put("price",1000); car.process(1,item);
这里只做模拟演示,用户类型一般通过请求参数来判断,你会发现请求类型变量值,跟@Service注解值一致。这里的方法删除了if else大量逻辑代码,直接由AbstractCar类调用,具体逻辑交给其子类实现。
这就是设计模式中的开闭原则:对修改关闭,对扩展开放。
如果一个类的属性多达数十个,但是需要DTO转换为类似的DO操作,那么出现大量的以下代码:
ComplicatedOrderDTO orderDTO = new ComplicatedOrderDTO(); ComplicatedOrderDO orderDO = new ComplicatedOrderDO(); orderDO.setAcceptDate(orderDTO.getAcceptDate()); orderDO.setAddress(orderDTO.getAddress()); orderDO.setAddressId(orderDTO.getAddressId()); orderDO.setCancelable(orderDTO.isCancelable()); orderDO.setCommentable(orderDTO.isComplainable()); //属性错误 orderDO.setComplainable(orderDTO.isCommentable()); //属性错误 orderDO.setCancelable(orderDTO.isCancelable()); orderDO.setCouponAmount(orderDTO.getCouponAmount()); orderDO.setCouponId(orderDTO.getCouponId()); orderDO.setCreateDate(orderDTO.getCreateDate()); orderDO.setDirectCancelable(orderDTO.isDirectCancelable()); orderDO.setDeliverDate(orderDTO.getDeliverDate()); orderDO.setDeliverGroup(orderDTO.getDeliverGroup()); orderDO.setDeliverGroupOrderStatus(orderDTO.getDeliverGroupOrderStatus()); orderDO.setDeliverMethod(orderDTO.getDeliverMethod()); orderDO.setDeliverPrice(orderDTO.getDeliverPrice()); orderDO.setDeliveryManId(orderDTO.getDeliveryManId()); orderDO.setDeliveryManMobile(orderDO.getDeliveryManMobile()); //对象错误 orderDO.setDeliveryManName(orderDTO.getDeliveryManName()); orderDO.setDistance(orderDTO.getDistance()); orderDO.setExpectDate(orderDTO.getExpectDate()); orderDO.setFirstDeal(orderDTO.isFirstDeal());
在赋值过程中,很容易写错、漏写造成数据不准确。可以使用BeanUtils这样Mapping工具来转换。copyProperties还可以忽略某些属性。
ComplicatedOrderDTO orderDTO = new ComplicatedOrderDTO(); ComplicatedOrderDO orderDO = new ComplicatedOrderDO(); BeanUtils.copyProperties(orderDTO, orderDO, "id"); return orderDO;
第一种消除重复代码,可以考虑将相同逻辑提取到父类中,差异的逻辑通过抽象方法留给子类实现。通过类似的模板方法把相同的逻辑做成固定模板,差异的同时尽可能避免重复代码。同时使用Spring的IOC特性注入子类,来避免实例化子类时大量的if else代码。
第二种重复代码,业务中常见的DO、DTO、VO转换时大量手动赋值,容易出错。通过映射工具BeanUtils实现。
联系客服