如何将业务代码写得像诗一样(使用注解+单例+工厂去掉一大波if和else判断)
1.订单控制器,提供一个根据商品id和银行渠道id计算商品折后价格的接口:
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; @RestController @RequestMapping("/order") public class OrderController { /** * 根据商品id和银行渠道id计算折扣后的金额 * * @param goodsId 商品id * @param channelId 银行渠道id * @return */ @GetMapping("/calc") @ResponseBody public String calcAmount(Integer goodsId, Integer channelId) { Context context = new Context(); BigDecimal bigDecimal; try { bigDecimal = context.calRecharge(goodsId, channelId); } catch (Exception e) { e.printStackTrace(); return ""; } return bigDecimal.setScale(2) + ""; } }
2.上下文:
import java.math.BigDecimal; public class Context { /** * 根据商品id和银行渠道id计算折扣后的金额 * * @param goodsId 商品id * @param channelId 银行渠道id * @return * @throws Exception */ public BigDecimal calRecharge(Integer goodsId, Integer channelId) throws Exception { StrategyFactory strategyFactory = StrategyFactory.getInstance(); // 根据渠道id查询具体的银行实现类 Strategy strategy = strategyFactory.create(channelId); // 调用具体的实现类进行计算 return strategy.calRecharge(goodsId, channelId); } }
3.折扣计算单例工厂类,内部用一个Map来存储银行渠道id和具体银行实现类之间的映射关系,方便根据渠道id反射获取对应银行具体的实现类:
import org.reflections.Reflections; import java.util.HashMap; import java.util.Set; public class StrategyFactory { private static StrategyFactory strategyFactory = new StrategyFactory(); private StrategyFactory() { } public static StrategyFactory getInstance() { return strategyFactory; } private static HashMap<Integer, String> source_map = new HashMap<>(); static { Reflections reflections = new Reflections("ICBCBankImpl"); Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(Pay.class); for (Class<?> clazz : classSet) { Pay pay = clazz.getAnnotation(Pay.class); source_map.put(pay.channelId(), clazz.getCanonicalName()); } } /** * 根据银行渠道id从Map中获取具体的银行实现类 * * @param channelId * @return * @throws Exception */ public Strategy create(int channelId) throws Exception { String clazz = source_map.get(channelId); Class<?> clazz_ = Class.forName(clazz); return (Strategy) clazz_.newInstance(); } }
4.计算折后价格的接口:
import java.math.BigDecimal; public interface Strategy { BigDecimal calRecharge(Integer goodsId, Integer channelId); }
5.工商银行实现类,类上加上@Pay注解指定工商银行对应的数据库中的渠道id:
import javax.annotation.Resource; import java.math.BigDecimal; /** * 工商银行实现类,对应的数据库中的渠道id为1 */ @Pay(channelId = 1) public class ICBCBankImpl implements Strategy { @Resource private GoodsMapper goodsMapper; @Resource private ChannelMapper channelMapper; /** * 根据商品id和银行渠道id计算优惠后的价格 * * @param goodsId 商品id * @param channelId 银行渠道id * @return */ @Override public BigDecimal calRecharge(Integer goodsId, Integer channelId) { BigDecimal goodsPrice = goodsMapper.getGoodsPriceById(goodsId); BigDecimal discountPrice = channelMapper.getDiscountPriceById(channelId); if (goodsPrice == null || discountPrice == null) { return null; } return goodsPrice.multiply(discountPrice); } }
6.用于标记银行实现类的注解,定义了一个银行渠道id属性:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Pay { int channelId(); }
7.模拟查询商品价格的Dao:
import java.math.BigDecimal; public class GoodsMapper { public BigDecimal getGoodsPriceById(Integer goodsId) { return BigDecimal.valueOf(599); } }
8.模拟查询查询渠道优惠折扣的Dao:
import java.math.BigDecimal; public class ChannelMapper { public BigDecimal getDiscountPriceById(Integer channelId) { return BigDecimal.valueOf(0.5); } }