安卓清理垃圾清理代码

从战中清除代码–验证

让我们直接从一个例子开始。 考虑一个简单的Web服务,该服务允许客户向商店下订单。 订单控制器的非常简化的版本可能如下所示–

@RestController
@RequestMapping(value = "/",consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
public class OrderController {private final OrderService orderService;public OrderController(OrderService orderService) {this.orderService = orderService;}@PostMappingpublic void doSomething(@Valid @RequestBody OrderDTO order) {orderService.createOrder(order);}
}

和相应的DTO类

@Getter
@Setter
@ToString
public class OrderDTO {@NotNullprivate String customerId;@NotNull@Size(min = 1)private List<OrderItem> orderItems;@Getter@Setter@ToStringpublic static class OrderItem {private String menuId;private String description;private String price;private Integer quantity;}
}

从此DTO创建订单的最常见方法是将其传递给服务,根据需要对其进行验证,然后将其保存在数据库中

@Service
@Slf4j
class OrderService {private final MenuRepository menuRepository;OrderService(MenuRepository menuRepository) {this.menuRepository = menuRepository;}void createOrder(OrderDTO orderDTO) {orderDTO.getOrderItems().forEach(this::validate);log.info("Order {} saved", orderDTO);}private void validate(OrderItem orderItem) {String menuId = orderItem.getMenuId();if (menuId == null || menuId.trim().isEmpty()) {throw new IllegalArgumentException("A menu item must be specified.");}if (!menuRepository.menuExists(menuId.trim())) {throw new IllegalArgumentException("Given menu " + menuId + " does not exist.");}String description = orderItem.getDescription();if (description == null || description.trim().isEmpty()) {throw new IllegalArgumentException("Item description should be provided");}String price = orderItem.getPrice();if (price == null || price.trim().isEmpty()) {throw new IllegalArgumentException("Price cannot be empty.");}try {new BigDecimal(price);} catch (NumberFormatException ex) {throw new IllegalArgumentException("Given price is not in valid format", ex);}if (orderItem.getQuantity() == null) {throw new IllegalArgumentException("Quantity must be given");}if (orderItem.getQuantity() <= 0) {throw new IllegalArgumentException("Given quantity "+ orderItem.getQuantity()+ " is not valid.");}}
}

validate方法写得不好。 很难测试。 将来引入新的验证规则也很困难,因此删除/修改任何现有的验证规则也很困难。 从我的经验中,我看到大多数人通常在集成测试类中针对这种类型的验证检查编写一些通用断言,仅涉及一个或两个(或更多,但不是全部)验证规则。 因此,将来只能在“ 编辑”和“祈祷”模式下进行重构。

如果使用多态来替换这些条件,则可以改善代码结构。 我们创建一个通用的超级类型来表示一个验证规则

public interface OrderItemValidator {void validate(OrderItem orderItem);
}

下一步是创建验证规则实现,该实现将集中于DTO的单独验证区域。 让我们从菜单验证器开始

public class MenuValidator implements OrderItemValidator {private final MenuRepository menuRepository;public MenuValidator(MenuRepository menuRepository) {this.menuRepository = menuRepository;}@Overridepublic void validate(OrderItem orderItem) {String menuId = Optional.ofNullable(orderItem.getMenuId()).map(String::trim).filter(id -> !id.isEmpty()).orElseThrow(() -> new IllegalArgumentException("A menu item must be specified."));if (!menuRepository.menuExists(menuId)) {throw new IllegalArgumentException("Given menu [" + menuId + "] does not exist.");}}
}

然后商品说明验证器

public class ItemDescriptionValidator implements OrderItemValidator {@Overridepublic void validate(OrderItem orderItem) {Optional.ofNullable(orderItem).map(OrderItem::getDescription).map(String::trim).filter(description -> !description.isEmpty()).orElseThrow(() -> new IllegalArgumentException("Item description should be provided"));}
}

价格验证器

public class PriceValidator implements OrderItemValidator {@Overridepublic void validate(OrderItem orderItem) {String price = Optional.ofNullable(orderItem).map(OrderItem::getPrice).map(String::trim).filter(itemPrice -> !itemPrice.isEmpty()).orElseThrow(() -> new IllegalArgumentException("Price cannot be empty."));try {new BigDecimal(price);} catch (NumberFormatException ex) {throw new IllegalArgumentException("Given price [" + price + "] is not in valid format", ex);}}
}

最后,数量验证器

public class QuantityValidator implements OrderItemValidator {@Overridepublic void validate(OrderItem orderItem) {Integer quantity = Optional.ofNullable(orderItem).map(OrderItem::getQuantity).orElseThrow(() -> new IllegalArgumentException("Quantity must be given"));if (quantity <= 0) {throw new IllegalArgumentException("Given quantity " + quantity + " is not valid.");}}
}

这些验证器实现中的每一个现在都可以轻松地,彼此独立地进行测试。 关于它们每个的推理也变得更加容易。 将来的添加/修改/删除也是如此。

现在是接线部分。 我们如何将这些验证器与订单服务集成在一起?

一种方法是直接在OrderService构造函数中创建一个列表,并使用验证程序填充它。 或者我们可以使用Spring将List注入到OrderService中

@Service
@Slf4j
class OrderService {private final List<OrderItemValidator> validators;OrderService(List<OrderItemValidator> validators) {this.validators = validators;}void createOrder(OrderDTO orderDTO) {orderDTO.getOrderItems().forEach(this::validate);log.info("Order {} saved", orderDTO);}private void validate(OrderItem orderItem) {validators.forEach(validator -> validator.validate(orderItem));}
}

为了使它起作用,我们将必须将每个验证器实现声明为Spring Bean。

我们可以进一步改进抽象。 OrderService现在正在接受验证者列表。 但是,我们可以将其更改为仅了解OrderItemValidator类型,而无需其他任何操作。 这使我们可以灵活地将来注入单个验证器或任何组成的验证器。

因此,现在我们的目标是更改订单服务,以与单个验证器相同的方式来处理订单项验证器的组成。 有一个著名的设计模式称为
Composite让我们可以做到这一点。

让我们创建验证器接口的新实现,该接口将为复合

class OrderItemValidatorComposite implements OrderItemValidator {private final List<OrderItemValidator> validators;OrderItemValidatorComposite(List<OrderItemValidator> validators) {this.validators = validators;}@Overridepublic void validate(OrderItem orderItem) {validators.forEach(validators -> validators.validate(orderItem));}
}

然后,我们创建一个新的Spring配置类,该类将实例化并初始化此组合,然后将其公开为bean

@Configuration
class ValidatorConfiguration {@BeanOrderItemValidator orderItemValidator(MenuRepository menuRepository) {return new OrderItemValidatorComposite(Arrays.asList(new MenuValidator(menuRepository),new ItemDescriptionValidator(),new PriceValidator(),new QuantityValidator()));}
}

然后,我们通过以下方式更改OrderService类

@Service
@Slf4j
class OrderService {private final OrderItemValidator validator;OrderService(OrderItemValidator orderItemValidator) {this.validator = orderItemValidator;}void createOrder(OrderDTO orderDTO) {orderDTO.getOrderItems().forEach(validator::validate);log.info("Order {} saved", orderDTO);}
}

我们完成了!

这种方法的好处很多。 整个验证逻辑已完全从订购服务中抽象出来。 测试更容易。 将来的维护更加容易。 客户只知道一种验证器类型,而没有其他信息。

但是,上述所有方法也都存在一些问题。 有时人们对此设计不满意。 他们可能觉得这太抽象了,或者对于将来的维护他们将不需要太多的灵活性或可测试性。 我建议根据团队文化采用这种方法。 毕竟,在软件开发中没有唯一正确的方法。

请注意,为了本文方便,我在这里也做了一些简化。 其中包括在验证失败时引发通用IllegalArgumentException。 您可能希望生产级应用程序中有一个更特定/自定义的异常,以在不同情况之间进行标识。 十进制分析也很天真地完成,您可能要修复特定的格式,然后使用DecimalFormat对其进行解析。

完整的代码已上传到Github 。

翻译自: https://www.javacodegeeks.com/2017/05/clean-code-trenches.html

安卓清理垃圾清理代码

安卓清理垃圾清理代码_从战中清理代码相关推荐

  1. python怎么清理垃圾和缓存_怎么才能正确清理电脑的缓存垃圾?

    笔者一直苦于某某卫士那些华而不实的功能和小广告,而且点开资源管理器一瞧,它占内存还挺大,东西,站着位子不做实事 笔者发出了怒吼:还笔者清爽的电脑! 但笔者又养成了清 理电脑垃圾的习惯,不清 理难受系列 ...

  2. mac电脑清理垃圾软件有哪些 mac电脑怎么清理垃圾

    为了使mac能健康稳定的运行,我们通常会安装一款电脑清理软件.那么,mac电脑清理垃圾软件有哪些?软件市场上有众多清理垃圾软件,我们如何选择适合电脑的软件?小编今天向大家推荐几款比较不错的垃圾清理软件 ...

  3. acer清理工具 clear下载_时秋极致清理大师app下载安装_时秋极致清理大师软件最新版免费下载...

    Shiqiu Ultimate Cleanup Master应用程序是一个简单易用的手机清洁工具. Shiqiu Ultimate Cleanup Master应用程序可以一键清除手机垃圾,释放手机内 ...

  4. python怎么清除代码_python的shell中的代码怎么清理?

    python shell是Python的命令行. 交互模式下使用Python很方便,如果想清除显示过的信息,有两种方法可以采用. 方法一.针对Python命令行(python shell) 直接使用下 ...

  5. 计算机清理垃圾文件丢失怎么恢复,电脑管家清理的文件怎么恢复

    原标题:电脑管家清理的文件怎么恢复 对于爱惜电脑,有整理习惯的小伙伴们来说,定期的清理电脑里的垃圾是一件必须要做的事情,这样这是个特别好的习惯,因为这样节省了电脑的空间,保持了电脑文件的条理性和有效性 ...

  6. 手机能写python代码_用手机写Python代码!可以随时随地学Python!

    我们翻开软件后首要输入命令 apt update更新一下可以装置的软件,接着输入apt list 就可以看到如下的效果 然后就可以输入apt install python 如果要装置Python2就 ...

  7. 50行python游戏代码_使用50行Python代码从零开始实现一个AI平衡小游戏

    使用50行Python代码从零开始实现一个AI平衡小游戏 发布时间:2020-10-23 09:26:14 来源:脚本之家 阅读:74 集智导读: 本文会为大家展示机器学习专家 Mike Shi 如何 ...

  8. python以什么表示代码层次_在Python中,采用代码缩进和( )区分代码之间的层次。_学小易找答案...

    [填空题]29 号元素 Cu 基态时的电子排布式为 ___ __ ,该元素在周期表中位于 __ _ 周期. __ __ 族. [单选题]"我要努力实现梦想,以弥补小时候吹过的牛"小 ...

  9. python反向缩进_在Pycharm中对代码进行注释和缩进的方法详解

    在Pycharm中对代码进行注释和缩进的方法详解 一.注释 1. #单行注释 2. """ 多行注释 """ 3. pycharm多行注释快 ...

最新文章

  1. 快讯!Sharding-Sphere正式进入Apache孵化器
  2. jenkins 手动执行_Jenkins(一)
  3. 【抓包工具】Fiddler(详细介绍)
  4. 【python 8】python 装饰器
  5. Coding and Paper Letter(一)
  6. 普通html和vue单选框的样式,vue2实现自定义样式radio单选框
  7. kernel32.dll已加载但找不到入口点_关键词分析法和工具,关键词分析包括哪几个方面(已帮助1376人)...
  8. BMIP002协议介绍
  9. C语言之fwrite()与fprintf()区别(十一)
  10. 具体化和实例化的应用
  11. PLC编程语言都在这里了!
  12. oracle 中explain,oracle执行计划的使用(EXPLAIN)
  13. 微信公众号获取openid流程
  14. matlab插值拟合案例,matlab插值与拟合
  15. java23种设计模式
  16. 第23集丨人生的智慧:练就一颗从容自在的心
  17. 常见笔顺错误的字_有哪些常见笔顺错误?
  18. Laplance算子(二阶导数)
  19. 微信企业号开发常用工具类总结(一)
  20. C++ 计算多边形的面积,计算IOU

热门文章

  1. 洛谷P5212:SubString(SAM、LCT)
  2. P4590-[TJOI2018]游园会【dp套dp】
  3. P3960-列队【权值线段树】
  4. P4549-[模板]裴蜀定理
  5. POJ3585-Accumulation Degree【树形dp,二次扫描与换根法】
  6. 【bfs】Replication G(P7151)
  7. 【DP】奖励卡(jzoj 3937)
  8. 【数位DP】好数(jzoj 1521)
  9. MyBatis-generator使用,Example缺少分页问题解决
  10. JAVA面试常考系列一