我们在平时开发中遇到最多的问题,无异于实体类属性的变化,可能我们开发出来的接口跟前端要的字段很多不一样,或者需求变更,需要返回的很多内容不一样。
假设我们现在有这么一个需求,返回一个配件的详细信息,也许我们之前返回的格式如下
{
"code" : 200 ,
"data" : {
"brand" : {
"code" : "001" ,
"firstChar" : "�" ,
"id" : 1 ,
"logoUrl" : "http://123.456.789" ,
"name" : "飞利浦" ,
"sort" : 1
},
"code" : "0002" ,
"freeShipping" : false ,
"levelName" : "高级项链" ,
"otherValues" : {
"innerMap" : {
"商品等级" : "国际" ,
"运费设置" : "包邮" ,
"生产厂家" : "飞利浦" ,
"包装规格" : "10" ,
"商品产地" : "呼和浩特"
},
"obj" : {
"$ref" : "$.data.otherValues.innerMap"
}
},
"product" : {
"hotSell" : false ,
"id" : 2459901248443253560 ,
"model" : "朝天DDL" ,
"name" : "项链天窗" ,
"onShelf" : false ,
"price" : {
"begin" : false ,
"normalPrice" : 3000.0000000000
},
"recommend" : false
},
"provider" : {
"code" : "0001" ,
"productProvider" : {
"id" : 2459698718186668856 ,
"logoUrl" : "http://123.456.23.12/12.jpg" ,
"name" : "大众4S店" ,
"productList" : []
},
"status" : false
}
},
"msg" : "操作成功"
}
但是由于现在需求变化,我们需要返回如下格式
{ data: { product: { // 产品信息 id: '', name: '', // 产品名称 imgs: [''], // 产品图片 price: 123, // 价格 }, store: { // 店铺信息 name: '', id: '', logo: '', rate: 4.5, // 评价 collect: 2032, // 收藏数 }, comments: { // 评论 rate: 4.4, // 总评分 list: [ { id: '', content: '', img: [''], ... } ] }, specs: [ // 自定义属性 {name: '商品等级', value: '国际'} ], picTextContent: '<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);
}
需求变更前,我们定义的配件实体类如下
@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如下(有删减)
@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 Integer collectedNum;
@Getter
@Setter
private Double avgStar;
@Getter
@Setter
private List<Evaluate> evaluateList;
@Getter
@Setter
private String details;
@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);
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());
((ProductDetail)product).setEvaluateList(evaluates);
OptionalDouble average = evaluates.stream().mapToDouble(Evaluate::getStar).average();
((ProductDetail)product).setAvgStar(average.getAsDouble());
Map map = ((ProductDetail)product).getOtherValues().getInnerMap();
Map<String,String> insteadMap = new HashMap<>();
map.keySet().stream().forEach(key -> {
log.info("键名为:" + String.valueOf(key));
String name = otherPropertyDao.findNameById(Long.parseLong(String.valueOf(key)));
insteadMap.put(name,(String) map.get(key));
});
((ProductDetail)product).getOtherValues().setObj(insteadMap);
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()方法
dao和mapper省略......
此时要使用新类来跑新需求,我们需要变更工厂的实现类
/**
* 配件提供者工厂
*/
public class ProviderFactory {
public static ProductService createProviderProduct() {
return new ProductDetail();
}
}
如此我们不需要修改任何原始代码就完成了需求变更,Controller也无需做修改。完全符合开闭原则。