专栏首页算法之名使用简单工厂加接口加适配器模式来遵守开闭原则 顶

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

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

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

{

"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也无需做修改。完全符合开闭原则。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • spring cloud zuul网关的作用

    zuul一般有两大作用,1是类似于Nginx的网址重定向,但zuul的重定向的一般是整个spring cloud里在Eureka注册中心的模块.

    算法之名
  • 设计模式整理 顶

    Iterator模式可以帮助我们分离具体的集合跟遍历,就是在代码中更换了集合,也可以不需要重新调用新集合的方法。

    算法之名
  • Java函数式编程整理 顶

    Java函数式编程的第一个作用是可以将匿名类改写成函数式表达式,由系统自动判断类型

    算法之名
  • SpringBoot学习笔记(十一:使用MongoDB存储文件 )

    MongoDB是一个面向文档的数据库,使用BSON(Binary JSON:二进制JSON)格式来存储数据。

    三分恶
  • 一步一步实现Android的MVP框架

    内容大纲: Android 开发框架的选择 如何一步步搭建分层框架 使用 RxJava 来解决主线程发出网络请求的问题 结语 一、Android开发框架的选择 ...

    腾讯Bugly
  • 微服务使用 Hystrix 实现服务降级

    设想一个场景,很多人都在请求一个接口,此时这个接口就会变得非常繁忙,调用者就要等待很长时间,而微服务是互相调用的,所以微服务的调用者即客户端回等待很长时间,给用...

    wsuo
  • Unix time(同一时刻全世界的timestamp值是一样的吗)

    通常把它叫做时间戳,是指格林尼治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数,用有符号32位...

    peakxie
  • spring webmvc转webflux的初步尝试

    最近在看api网关的源码,发现他用的是webflux,对这个挺感兴趣,所以尝试将手上的项目改成webflux

    星痕
  • 解决WordPress升级4.2后调用国外图片导致大量404请求的问题

    前几天就收到 WordPress 官方发来的预更新通知,告诉我本周会更新到 4.2。果然,昨天就收到了更新推送消息,随手就点击升级了,前台打开看了下没有看到明显...

    张戈
  • @Accessors 的三种用法的解释

    @Accessors Accessor的中文含义是存取器,@Accessors用于配置getter和setter方法的生成结果,下面介绍三个属性

    逐梦的青春

扫码关注云+社区

领取腾讯云代金券