咱們在平時開發中遇到最多的問題,無異於實體類屬性的變化,可能咱們開發出來的接口跟前端要的字段不少不同,或者需求變動,須要返回的不少內容不同。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自實現
現將整體思想總結以下