wip-7.6
zero7.6 Summary
7.6 总结
- Code complexity is defined by the number of decision-making points in the code, both explicit (made by the code itself) and implicit (made by the libraries the code uses).
代码复杂度由代码中的决策点数量定义,包括显式决策点(由代码本身做出)和隐式决策点(由代码所使用的库做出)。 - Domain significance shows how significant the code is for the problem domain of your project. Complex code often has high domain significance and vice versa, but not in 100% of all cases.
领域重要性表示代码对项目问题领域的重要程度。复杂代码通常具有较高领域重要性,反过来也常成立,但并非 100% 情况都如此。 - Complex code and code that has domain significance benefit from unit testing the most because the corresponding tests have greater protection against regressions.
复杂代码和具有领域重要性的代码最能从单元测试中获益,因为相应测试具有更强的防止回归能力。 - Unit tests that cover code with a large number of collaborators have high maintenance costs. Such tests require a lot of space to bring collaborators to an expected condition and then check their state or interactions with them afterward.
覆盖大量协作者代码的单元测试维护成本很高。这类测试需要大量篇幅来把协作者置于预期状态,然后检查它们的状态或与它们的交互。 - All production code can be categorized into four types of code by its complexity or domain significance and the number of collaborators:
所有生产代码都可以根据其复杂度或领域重要性,以及协作者数量,划分为四种代码类型: - Domain model and algorithms (high complexity or domain significance, few collaborators) provide the best return on unit testing efforts.
领域模型和算法(高复杂度或高领域重要性,少量协作者)能为单元测试投入提供最佳回报。 - Trivial code (low complexity and domain significance, few collaborators) isn’t worth testing at all.
平凡代码(低复杂度和低领域重要性,少量协作者)完全不值得测试。 - Controllers (low complexity and domain significance, large number of collaborators) should be tested briefly by integration tests.
控制器(低复杂度和低领域重要性,大量协作者)应该通过集成测试进行简要测试。 - Overcomplicated code (high complexity or domain significance, large number of collaborators) should be split into controllers and complex code.
过度复杂代码(高复杂度或高领域重要性,大量协作者)应该被拆分为控制器和复杂代码。 - The more important or complex the code is, the fewer collaborators it should have.
代码越重要或越复杂,它拥有的协作者就应该越少。 - The Humble Object pattern helps make overcomplicated code testable by extracting business logic out of that code into a separate class. As a result, the remaining code becomes a controller—a thin, humble wrapper around the business logic.
Humble Object 模式通过把业务逻辑从过度复杂代码中提取到单独类里,帮助这类代码变得可测试。结果是,剩余代码变成控制器——围绕业务逻辑的一层薄薄的、谦逊的包装器。 - The hexagonal and functional architectures implement the Humble Object pattern. Hexagonal architecture advocates for the separation of business logic and communications with out-of-process dependencies. Functional architecture separates business logic from communications with all collaborators, not just out-of-process ones.
六边形架构和函数式架构都实现了 Humble Object 模式。六边形架构主张分离业务逻辑与进程外依赖通信。函数式架构则把业务逻辑与所有协作者的通信分离,而不仅仅是进程外协作者。 - Think of the business logic and orchestration responsibilities in terms of code depth versus code width. Your code can be either deep (complex or important) or wide (work with many collaborators), but never both.
可以用代码深度与代码宽度来理解业务逻辑和编排职责。你的代码可以是深的(复杂或重要),也可以是宽的(与许多协作者协作),但不能二者兼具。 - Test preconditions if they have a domain significance; don’t test them otherwise.
如果前置条件具有领域重要性,就测试它们;否则不要测试。 - There are three important attributes when it comes to separating business logic from orchestration:
在分离业务逻辑与编排时,有三个重要属性: - Domain model testability—A function of the number and the type of collaborators in domain classes.
领域模型可测试性——领域类中协作者数量和类型的函数。 - Controller simplicity—Depends on the presence of decision-making points in the controller.
控制器简单性——取决于控制器中是否存在决策点。 - Performance—Defined by the number of calls to out-of-process dependencies.
性能——由对进程外依赖的调用次数定义。 - You can have a maximum of two of these three attributes at any given moment:
在任何给定时刻,这三个属性最多只能同时拥有两个: - Pushing all external reads and writes to the edges of a business operation—Preserves controller simplicity and keeps the domain model testability, but concedes performance.
把所有外部读写推到业务操作边缘——保留控制器简单性并保持领域模型可测试性,但牺牲性能。 - Injecting out-of-process dependencies into the domain model—Keeps performance and the controller’s simplicity, but damages domain model testability.
把进程外依赖注入领域模型——保持性能和控制器简单性,但损害领域模型可测试性。 - Splitting the decision-making process into more granular steps—Preserves performance and domain model testability, but gives up controller simplicity.
把决策过程拆分为更细粒度的步骤——保留性能和领域模型可测试性,但放弃控制器简单性。 - Splitting the decision-making process into more granular steps—Is a trade-off with the best set of pros and cons. You can mitigate the growth of controller complexity using the following two patterns:
把决策过程拆分为更细粒度的步骤——这是优缺点组合最好的一种取舍。你可以使用以下两种模式缓解控制器复杂度增长: - The CanExecute/Execute pattern introduces a CanDo() for each Do() method and makes its successful execution a precondition for Do(). This pattern essentially eliminates the controller’s decision-making because there’s no option not to call CanDo() before Do().
CanExecute/Execute 模式为每个 Do() 方法引入一个 CanDo(),并把 CanDo() 成功执行作为 Do() 的前置条件。这个模式本质上消除了控制器的决策,因为在 Do() 之前没有“不调用 CanDo()”的选项。 - Domain events help track important changes in the domain model, and then convert those changes to calls to out-of-process dependencies. This pattern removes the tracking responsibility from the controller.
领域事件有助于跟踪领域模型中的重要变化,然后把这些变化转换为对进程外依赖的调用。这个模式把跟踪职责从控制器中移除。 - It’s easier to test abstractions than the things they abstract. Domain events are abstractions on top of upcoming calls to out-of-process dependencies. Changes in domain classes are abstractions on top of upcoming modifications in the data storage.
测试抽象,比测试抽象所代表的具体事物更容易。领域事件是对即将发生的进程外依赖调用的抽象。领域类中的变化是对即将发生的数据存储修改的抽象。