Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

架构整洁之道 #61

Open
Petelin opened this issue Nov 16, 2020 · 0 comments
Open

架构整洁之道 #61

Petelin opened this issue Nov 16, 2020 · 0 comments

Comments

@Petelin
Copy link
Owner

Petelin commented Nov 16, 2020

架构整洁之道

单一职责原则

单一职责原则主要讨论的是函数和类之间的关系——但是它在两个讨论层面上会以不同的形式出现。在组件层面,我们可以将其称为共同闭包原则(Common Closure Principle),在软件架构层面,它则是用于奠定架构边界的变更轴心(Axis of Change)

image

calculatePay() 函数是由财务部门制定的,他们负责向 CFO 汇报。
reportHours() 函数是由人力资源部门制定并使用的,他们负责向 COO 汇报。
save() 函数是由 DBA 制定的,他们负责向 CTO 汇报。

尽管report hours和calculate pay有相似的需求, 都需要统计出勤日期, 但是本质上他们是对两件事负责, 一个Employee对三个actor(身份,角色) 负责, 这就不符合单一职责.

OCP 开闭原则 THE OPEN-CLOSED PRINCIPLE

OCP 是我们进行系统架构设计的主导原则,其主要目标是让系统易于扩展,同时限制其每次被修改所影响的范围。实现方式是通过将系统划分为一系列组件,并且将这些组件间的依赖关系按层次结构进行组织,使得高阶组件不会因低阶组件被修改而受到影响。

image

猛地一看可能会被这个图吓到, 但是这个图所展现的层次依赖关系是非常清晰明了的. 入口/逻辑/数据/渲染层次清晰, 层和层之间解耦的手法都是经典中的经典

  • 类是复用的基础
  • 控制反转是让我们能自由组合类的神器, 例子: 我们实现一个cp, 之前是要依赖底层硬件, 但是定义好io这个抽象之后,变成了底层硬件要实现io. 控制反转, 使得写高层次应用的软件真正被解放出来, 不在拘泥于底层细节.
  • 想要做到开闭, 就需要良好组织的关系+类

信息隐藏: 这种传递性依赖违反了“软件系统不应该依赖其不直接使用的组件”这一基本原则

LSP 里氏替换原则 THE LISKOV SUBSTITUTION PRINCIPLE

LSP 可以且应该被应用于软件架构层面,因为一旦违背了可替换也该系统架构就不得不为此增添大量复杂的应对机制。

这个原则是在强调什么是子类型, 只有完全可以替换才是子类型, 像三角形和正方形, 设置他们的参数就可能统一, 那他们就不是可替换的类型

ISP 接口隔离原则 THE INTERFACE SEGREGATION PRINCIPLE

任何层次的软件设计如果依赖了它并不需要的东西,就会带来意料之外的麻烦。

image

在这种情况下,如果 D 中包含了 F 不需要的功能,那么这些功能同样也会是 S 不需要的。而我们对 D 中的这些功能的修改将会导致 F 需要被重新部署,后者又会导致 S 的重新部署。更糟糕的是,D 中一个无关功能的错误也可能会导致 F 和 S 运行出错。

接口隔离原则是说, 不要传递引用, 而是把自己需要的一小部分接口定义出来, 不要依赖自己不需要的东西

DIP 依赖反转原则 THE DEPENDENCY INVERSION PRINCIPLE

如果我们改了接口, 那么一定要改实现, 但是改了实现不一定会改接口. 我们需要让自己定义的接口比实现要稳定的多. 毕竟争取在不修改接口的情况下为软件增加新的功能是软件设计的基础常识。 (这一点要花费很大力气才能做到)

作者给出以下建议

  • 应在代码中多使用抽象接口,尽量避免使用那些多变的具体实现类 - 因为实现多变
  • 不要在具体的脆弱的实现类上创建衍生类。 - 因为这个绑定关系太强了, 绑死在一个一边的具体的事情上
  • Don’t override concrete functions. - 因为具体的function经常会被写死依赖
  • Never mention the name of anything concrete and volatile. - 这一点变成了依赖注入

抽象工厂

image

Application 类是通过 Service 接口来使用 Concretelmpl 类的. 但是我们不想让Concretelmpl被感知, 所以在Service接口创建一个工厂, 返回service.
这条曲线将整个系统划分为两部分组件:抽象接口与其具体实现。抽象接口组件中包含了应用的所有高阶业务规则,而具体实现组件中则包括了所有这些业务规则所需要做的具体操作及其相关的细节信息。

需要注意

我们在软件系统中并不可能完全消除违反 DIP 的情见通常只需要把它们集中于少部分的具体实现组件中,将其与系统的其他部分隔离即可。
绝大部分系统中都至少存在一个具体实现组件 我们一般称之为 main 组化 因为它们通常是 main 函数所在之处。在 图 11.1 中,函数应该负责创建 ServiceFactoryImpl 实例,并将其赋值给类型为 ServiceFactory 的全局变量,以便让 Application 类通过这个全局变量来进行相关调用。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant