咱们在平时开发中遇到最多的问题,无异于实体类属性的变化,可能咱们开发出来的接口跟前端要的字段不少不同,或者需求变动,须要返回的不少内容不同。html
假设咱们如今有这么一个需求,返回一个配件的详细信息,也许咱们以前返回的格式以下前端
/** * 配件商品提供者 */ public interface Provider { /** * 添加商品提供者(包含门店,服务) * @param provider * @return */ boolean addProvider(Provider provider); /** * 移除商品提供者 * @param provider * @return */ boolean removeProdvider(Provider provider); /** * 根据id获取一个商品提供者 * @param id * @return */ Provider findProvider(Long id); /** * 获取全部商品提供者 * @return */ List<Provider> allProvider(); /** * 根据id获取下层商品提供者 * @param id * @return */ List<Provider> findContaint(Long id); }
public interface ProductService { Page<Provider> showProduct(Map<String,Object> params); Provider findProduct(Long id); Long findProviderId(Long id); }
需求变动前,咱们定义的配件实体类以下app
@Slf4j @Data @NoArgsConstructor public class ProviderProduct implements Provider,ProductService { private Product product; private String code; private Brand brand; private String details; private String levelName; private boolean freeShipping; private DefaultProvider provider; private ExtBeanWrapper otherValues; public ProviderProduct(Product product,String code,Brand brand) { this.product = product; this.code = code; this.brand = brand; } @Override public boolean addProvider(Provider provider) { throw new RuntimeException("不支持此方法"); } @Override public boolean removeProdvider(Provider provider) { throw new RuntimeException("不支持此方法"); } @Override public Provider findProvider(Long id) { return null; } @Override public List<Provider> allProvider() { return null; } @Override public List<Provider> findContaint(Long id) { throw new RuntimeException("不支持此方法"); } @Override public Page<Provider> showProduct(Map<String, Object> params) { ProductDao productDao = SpringBootUtil.getBean(ProductDao.class); int total = productDao.countProductInDefaultProvider(params); List<Provider> providers = Collections.emptyList(); if (total > 0) { PageUtil.pageParamConver(params,false); providers = productDao.findAllProductSimpleByProviderId(params); } return new Page<>(total,providers); } @Override public Provider findProduct(Long id) { ProductDao productDao = SpringBootUtil.getBean(ProductDao.class); OtherPropertyDao otherPropertyDao = SpringBootUtil.getBean(OtherPropertyDao.class); Provider product = productDao.findProductById(id); Map map = ((ProviderProduct) product).getOtherValues().getInnerMap(); Map<String,String> insteadMap = new HashMap<>(); for (Object key : map.keySet()) { log.info("键名为:" + String.valueOf(key)); String name = otherPropertyDao.findNameById(Long.parseLong(String.valueOf(key))); insteadMap.put(name,(String) map.get(key)); } ((ProviderProduct) product).getOtherValues().setObj(insteadMap); return product; } @Override public Long findProviderId(Long id) { ProductDao productDao = SpringBootUtil.getBean(ProductDao.class); return productDao.findProviderIdById(id); } }
/** * 配件提供者工厂 */ public class ProviderFactory { public static ProductService createProviderProduct() { return new ProviderProduct(); } }
Controller以下(有删减)ide
@Slf4j @RestController public class ProductController { private ProductService productService = ProviderFactory.createProviderProduct(); /** * 展现某个配件商的全部配件(带分页) * @param params * @return */ @Transactional @SuppressWarnings("unchecked") @GetMapping("/productprovider-anon/showproduct") public Result<Page<Provider>> showProduct(@RequestParam Map<String,Object> params) { return Result.success(productService.showProduct(params)); } /** * 查看某一个配件的详细信息 * @param id * @return */ @Transactional @SuppressWarnings("unchecked") @GetMapping("/productprovider-anon/findproduct") public Result<Provider> findProduct(@RequestParam("id") Long id) { return Result.success(productService.findProduct(id)); } }
业务变动后,咱们建立新的实体类测试
@Slf4j @NoArgsConstructor public class ProductDetail implements ProductService,Provider { private ProviderProduct providerProduct = new ProviderProduct(); @Getter @Setter private Long id; @Getter @Setter private String name; @Getter @Setter private List<String> imgUrl; @Getter @Setter private Price price; @Getter @Setter private ProductProvider provider; @Getter @Setter private ExtBeanWrapper otherValues; @Getter @Setter private List<OtherProperty> otherProperties; @Getter @Setter private Integer collectedNum; @Getter @Setter private Double avgStar; @Getter @Setter private List<Evaluate> evaluateList; @Getter @Setter private String details; @Data @AllArgsConstructor private class OtherProperty { private String name; private String value; } @Override public Page<Provider> showProduct(Map<String, Object> params) { return providerProduct.showProduct(params); } @Override public Provider findProduct(Long id) { ProductDetailDao productDetailDao = SpringBootUtil.getBean(ProductDetailDao.class); EvaluateClient evaluateClient = SpringBootUtil.getBean(EvaluateClient.class); OtherPropertyDao otherPropertyDao = SpringBootUtil.getBean(OtherPropertyDao.class); CollectionDao collectionDao = SpringBootUtil.getBean(CollectionDao.class); Provider product = productDetailDao.findProductById(id); String imgUrlStr = productDetailDao.findImgUrlById(id); String[] imgUrls = imgUrlStr.split(","); ((ProductDetail)product).setImgUrl(Arrays.asList(imgUrls)); List<Evaluate> evaluates = evaluateClient.allEvaluateOfProduct(id, ((ProductDetail) product).getProvider().getId()); log.info("门店评论:" + JSON.toJSONString(evaluates)); ((ProductDetail)product).setEvaluateList(evaluates); OptionalDouble average = evaluates.stream().mapToDouble(Evaluate::getStar).average(); ((ProductDetail)product).setAvgStar(average.orElse(0.0)); Map map = ((ProductDetail)product).getOtherValues().getInnerMap(); ((ProductDetail)product).setOtherProperties(new ArrayList<>()); map.keySet().stream().forEach(key -> { log.info("键名为:" + String.valueOf(key)); String name = otherPropertyDao.findNameById(Long.parseLong(String.valueOf(key))); ((ProductDetail)product).getOtherProperties().add(new OtherProperty(name,(String) map.get(key))); }); ((ProductDetail)product).setOtherValues(null); ((ProductDetail)product).setCollectedNum(collectionDao.countCollectionByProviderId( ((ProductDetail) product).getProvider().getId())); return product; } @Override public Long findProviderId(Long id) { return providerProduct.findProviderId(id); } @Override public boolean addProvider(Provider provider) { return providerProduct.addProvider(provider); } @Override public boolean removeProdvider(Provider provider) { return providerProduct.removeProdvider(provider); } @Override public Provider findProvider(Long id) { return providerProduct.findProvider(id); } @Override public List<Provider> allProvider() { return providerProduct.allProvider(); } @Override public List<Provider> findContaint(Long id) { return providerProduct.findContaint(id); } }
按照需求一比必定义字段,咱们能够看到新类也实现了这两个接口ProductService,Provider,因为咱们只须要变动展现配件明细,其余的需求并不须要修改,则咱们委托了一个private ProviderProduct providerProduct = new ProviderProduct(),让其适配原有的业务便可,须要变动的是findProduct()方法this
dao和mapper省略......lua
此时要使用新类来跑新需求,咱们须要变动工厂的实现类url
/** * 配件提供者工厂 */ public class ProviderFactory { public static ProductService createProviderProduct() { return new ProductDetail(); } }
如此咱们不须要修改任何原始代码就完成了需求变动,Controller也无需作修改。彻底符合开闭原则,同时遵照了依赖倒置原则,李氏替换原则,接口隔离原则。spa
最后返回的结果以下.net
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface ProductVersion { int value(); }
给咱们的原始类和新类打上版本标签
@Slf4j @Data @NoArgsConstructor @ProductVersion(value = 1) public class ProviderProduct implements Provider,ProductService
@Slf4j @NoArgsConstructor @ProductVersion(value = 2) public class ProductDetail implements ProductService,Provider
注:这两个类必须放在同一个包下面
修改配件工厂类以下
public static ProductService createProviderProduct() { //搜索该包下的全部类,并放入集合classes中 Set<Class<?>> classes = ClassUtil.getClassSet("com.cloud.productprovider.composite"); Object instance = null; try { //过滤有@ProductVersion标签的类 instance = classes.stream().filter(clazz -> clazz.isAnnotationPresent(ProductVersion.class)) //过滤实现了ProductService接口的类 .filter(clazz -> ProductService.class.isAssignableFrom(clazz)) //找出版本号大的类,并实例化为对象 .max(Comparator.comparingInt(clazz -> clazz.getAnnotation(ProductVersion.class).value())) .get().newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return (ProductService)instance; // return new ProductDetail(); }
之后若是再有需求变动,只须要在该包下再增长一个新的实现类,打上@ProductVersion标签,增长版本号就能够了。
搜索包下全部类的ClassUtil源码能够参考@Compenent,@Autowired,@PostConstruct自实现
现将整体思想总结以下