之前写过的一个老项目中,有这样一个业务场景,比喻:一个外卖系统须要接入多家餐馆,在外卖系统中返回每一个餐馆的菜单列表 ,每一个餐馆的菜单价格都须要不一样的算法计算。算法
代码中使用了大量的if else嵌套链接,一个类中数千行代码(眼睛快看瞎...),并且随着业务的扩展,接入的餐馆会愈来愈多,每接入一个餐馆都要增长一个 if else,满屏幕密密麻麻的逻辑代码,毫无可读性。而后前段时间进行了代码重构,使用了策略模式+工厂模式+反射代替了这整片的臃肿代码,瞬间神清气爽。后端
原代码的简单模拟实现,根据传入的不一样餐馆编码获取对应的餐馆类集合,每一个餐馆菜单价格的算法都不一样。每当须要新接入一家餐馆时,都须要在此增长一个if else,中间加入一大长串的处理逻辑,当餐馆愈来愈多的时候,代码就变得愈来愈沉重,维护成本高。app
public List server(String hotelCode) { if ("HotelA".equals(hotelCode)) { //获取数据 List<HotelA> hotelList = new ArrayList<HotelA>() { { add(new HotelA("爆炒腰子", 100d, 0.8, null)); add(new HotelA("红烧腰子", 200d, 0.8, null)); add(new HotelA("腰子刺身", 300d, 0.8, null)); } }; //逻辑计算 最终价格 = 原价 * 折扣 hotelList.parallelStream().forEach(v -> v.setFinalPrice(v.getPrice() * v.getDiscount())); return hotelList; } else if ("HotelB".equals(hotelCode)) { //获取数据 List<HotelB> hotelList = new ArrayList<HotelB>() { { add(new HotelB("兰州拉面", 100d, 10d, null)); add(new HotelB("落魄后端在线炒粉", 200d, 20d, null)); } }; //逻辑计算 最终价格 = 原价 - 优惠 hotelList.parallelStream().forEach(v -> v.setFinalPrice(v.getPrice() - v.getPreferential())); return hotelList; } else if ("HotelC".equals(hotelCode)) { //获取数据 List<HotelC> hotelList = new ArrayList<HotelC>() { { add(new HotelC("秘制奥利给", 1000d, 0.6, 20d, null)); add(new HotelC("老八辣酱", 2000d, 0.6, 10d, null)); } }; //逻辑计算 最终价格 = 原价 * 折扣 - 服务费 hotelList.parallelStream().forEach(v -> v.setFinalPrice(v.getPrice() * v.getDiscount() - v.getTip())); return hotelList; } return new ArrayList(); }
@Data @NoArgsConstructor @AllArgsConstructor public class HotelA { //菜品名 private String menu; //原价 private Double price; //折扣 private Double discount; //最终价格 = 原价 * 折扣 private Double finalPrice; }
@Data @NoArgsConstructor @AllArgsConstructor public class HotelB { //菜品名 private String menu; //原价 private Double price; //优惠 private Double preferential; //最终价格 = 原价 - 优惠 private Double finalPrice; }
@Data @NoArgsConstructor @AllArgsConstructor public class HotelC { //菜品名 private String menu; //原价 private Double price; //折扣 private Double discount; //服务费 private Double tip; //最终价格 = 原价 * 折扣 - 服务费 private Double finalPrice; }
由上述代码首先抽离出一个接口,if else中的业务逻辑最终都是返回一个列表ide
/** * 餐馆服务接口 */ public interface HotelService { /** * 获取餐馆菜单列表 * @return */ List getMenuList(); }
把每一个分支的业务逻辑封装成实现类,实现HotelService接口this
public class HotelAServiceImpl implements HotelService { /** * 逻辑计算 返回集合 * @return */ @Override public List getMenuList() { return initList().parallelStream() .peek(v -> v.setFinalPrice(v.getPrice() * v.getDiscount())) .collect(Collectors.toList()); } /** * 获取数据 * @return */ public List<HotelA> initList() { return new ArrayList<HotelA>() { { add(new HotelA("爆炒腰子", 100d, 0.8, null)); add(new HotelA("红烧腰子", 200d, 0.8, null)); add(new HotelA("腰子刺身", 300d, 0.8, null)); } }; } }
public class HotelBServiceImpl implements HotelService { /** * 逻辑计算 返回集合 * @return */ @Override public List getMenuList() { return initList().parallelStream() .peek(v -> v.setFinalPrice(v.getPrice() - v.getPreferential())) .collect(Collectors.toList()); } /** * 获取数据 * @return */ public List<HotelB> initList() { return new ArrayList<HotelB>() { { add(new HotelB("兰州拉面", 100d, 10d, null)); add(new HotelB("落魄后端在线炒粉", 200d, 20d, null)); } }; } }
public class HotelCServiceImpl implements HotelService { /** * 逻辑计算 返回集合 * @return */ @Override public List getMenuList() { return initList().parallelStream() .peek(v -> v.setFinalPrice(v.getPrice() * v.getDiscount() - v.getTip())) .collect(Collectors.toList()); } /** * 获取数据 * @return */ public List<HotelC> initList() { return new ArrayList<HotelC>() { { add(new HotelC("秘制奥利给", 1000d, 0.6, 20d, null)); add(new HotelC("老八辣酱", 2000d, 0.6, 10d, null)); } }; } }
这样就是一个简单的策略模式了,可是如今要调用不一样的实现类中的getMenuList方法,好像仍是离不开if else,那么如今就须要用工厂模式把全部实现类包装起来。编码
先定义一个枚举类,里面是各餐馆的codespa
public enum HotelEnum { HOTEL_A("HotelA"), HOTEL_B("HotelB"), HOTEL_C("HotelC"),; private String hotelCode; /** * 返回全部餐馆编码的集合 * @return */ public static List<String> getList() { return Arrays.asList(HotelEnum.values()) .stream() .map(HotelEnum::getHotelCode) .collect(Collectors.toList()); } HotelEnum(String hotelCode) { this.hotelCode = hotelCode; } public String getHotelCode() { return hotelCode; } }
接下来定义一个服务工厂,在静态块中利用反射机制把全部服务实现类动态加载到HOTEL_SERVER_MAP中,而后提供一个对外的获取对应服务的方法code
这里有几个须要注意的地方:server
1.因为包名是写死的,那么全部实现HotelService的实现类都须要放在固定的包下对象
2.类名的格式也是固定的,即枚举类中的hotelCode + "ServiceImpl"
/** * 服务工厂类 */ public class HotelServerFactory { /** * 类路径目录 */ private static final String CLASS_PATH = "com.tactics.service.impl."; /** * 服务实现后缀 */ private static final String GAME_SERVICE_SUFFIX = "ServiceImpl"; private static final Map<String, HotelService> HOTEL_SERVER_MAP = new ConcurrentHashMap<>(); /** * 初始化实现类到COMPANY_SERVER_MAP中 */ static { HotelEnum.getList().forEach(v -> { String className = CLASS_PATH + v + GAME_SERVICE_SUFFIX; try { HOTEL_SERVER_MAP.put(v, (HotelService) Class.forName(className).newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }); } /** * 获取餐馆服务实现 * * @param hotelCode * @return */ public static HotelService getHotelServerImpl(String hotelCode) { return HOTEL_SERVER_MAP.get(hotelCode); } }
这里有一个问题,若是你的服务实现类是交给Spring容器管理的,里面有注入Mapper等等,使用反射的方式new出来的话,其中的属性是没有值的。
Spring容器就至关因而一个工厂了,能够直接从Spring上下文中获取(怎么获取Spring上下文对象这里就不细说了,有须要能够自行百度)。
/** * 服务工厂类 */ public class HotelServerFactory { /** * 类路径目录 */ private static final String CLASS_PATH = "com.tactics.service.impl."; /** * 服务实现后缀 */ private static final String GAME_SERVICE_SUFFIX = "ServiceImpl"; /** * 获取餐馆服务实现 * * @param hotelCode * @return */ public static HotelService getHotelServerImpl(String hotelCode) { Class clazz = Class.forName(CLASS_PATH + hotelCode + GAME_SERVICE_SUFFIX); String className = hotelCode + GAME_SERVICE_SUFFIX; return (HotelService) ApplicationConfig.getBean(className, clazz); } }
最终的调用
public List server(String hotelCode) { //获取对应的服务 HotelService hotelService = HotelServerFactory.getCompanyServerImpl(hotelCode); //获取通过逻辑计算后返回的集合列表 return hotelService.getMenuList(); }
怎么样,是否是以为可读性,复用性和扩展性都大大提升了,业务扩展须要新加一个餐馆的时候,只须要在枚举类中加一个hotelCode,而后定义一个实现类实现HotelService接口就行了,这个Demo也让咱们知道了策略模式和工厂模式在实际项目中的应用场景。