前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用简单工厂加接口加适配器模式来遵守开闭原则 顶

使用简单工厂加接口加适配器模式来遵守开闭原则 顶

作者头像
算法之名
发布2019-08-28 10:35:40
5370
发布2019-08-28 10:35:40
举报
文章被收录于专栏:算法之名

我们在平时开发中遇到最多的问题,无异于实体类属性的变化,可能我们开发出来的接口跟前端要的字段很多不一样,或者需求变更,需要返回的很多内容不一样。

假设我们现在有这么一个需求,返回一个配件的详细信息,也许我们之前返回的格式如下

{

"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 />' // 图文详情 } }

业务相关有两个接口

代码语言:javascript
复制
/**
 * 配件商品提供者
 */
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);
}
代码语言:javascript
复制
public interface ProductService {
    Page<Provider> showProduct(Map<String,Object> params);
    Provider findProduct(Long id);
    Long findProviderId(Long id);
}

需求变更前,我们定义的配件实体类如下

代码语言:javascript
复制
@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);
    }
}
代码语言:javascript
复制
/**
 * 配件提供者工厂
 */
public class ProviderFactory {
    public static ProductService createProviderProduct() {
        return new ProviderProduct();
    }
}

Controller如下(有删减)

代码语言:javascript
复制
@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));
    }
}

业务变更后,我们创建新的实体类

代码语言:javascript
复制
@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省略......

此时要使用新类来跑新需求,我们需要变更工厂的实现类

代码语言:javascript
复制
/**
 * 配件提供者工厂
 */
public class ProviderFactory {
    public static ProductService createProviderProduct() {
        return new ProductDetail();
    }
}

如此我们不需要修改任何原始代码就完成了需求变更,Controller也无需做修改。完全符合开闭原则。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档