Open luoway opened 4 years ago
需要分解的大型系统
设计一个系统,其主要特征是需要同时解决高层问题和低层问题,且高层操作依赖于低层操作。
需要平衡以下作用力:
将系统划分成适当的层数,并将它们堆叠起来。从最低的抽象层开始,沿抽象梯度往上堆叠到顶。
每层的所有组件都必须处于相同的抽象层级。第N层提供的大多数服务都由第N-1层提供的服务组成,服务还可能依赖于同一层的其他服务。
分层模式的主要特征是:第N+1层只使用第N层的服务,层之间没有其他直接依赖关系。
这种结构类似于栈或洋葱,每层都将下面的各层保护起来,禁止上面的层直接访问它们。
从上往下传输的信息和控制流被称为“请求”,从下往上传输的调用被称为“通知”。
自上而下的通信:客户端向第N层发出请求,第N层将一些子任务交给第N-1层,第N-1层处理交给下一层,直到第1层。将高级服务映射到更低级的服务。自上而下传输的请求常被拆分为多个请求。
自下而上的通信:第1层得到输入后,将数据交给第2层,第2层及之后层进行处理并向上传输,直到到达顶层。自下而上传输的通知在上层要么被合并,要么不拆分也不合并。
请求不穿越所有层。当其中一层能满足该请求时,例如缓存层。
通知不穿越所有层。例如重发通知,需要拦截。
两个分层的栈相互通信。这种情景的栈被称为协议栈。
实现步骤,每步并非必不可少:
定义将任务划分到不同层的抽象准则
例如,根据离硬件的距离划分较低的层,按概念复杂度划分较高的层。
根据抽象准则确定抽象层级数
层数太多可能带来不必要的开销,层数太少又可能导致结构不清晰。
给每层命名并分派任务
最高层的任务就是整个系统要完成的任务。其他各层的任务是辅佐上一层。
规范服务
实现原则是相邻层界线分明,任何组件都不跨越多层。
完善层次划分
反复执行1-4步,直到分层结构自然而稳定。
规范每层的接口
黑盒方法:第N层对第N+1层而言是黑盒,就应设计一个统一接口,提供第N层的所有服务。
白盒方法:第N+1层知道第N层的内部结构,直接调用服务。
灰盒方法:第N+1层知道第N层的部分内部组件,直接调用这些组件,但不知道组件内部构造。
应尽可能采用黑盒方法。
确定各层的结构
重点是确保层间关系合理。对于复杂的层,应将其划分为多个组件,避免层内部混乱。
规范相邻层之间的通信
在层间通信方面,最常用的机制是push模型:第N层调用第N-1层的服务时,随服务调用传递所需信息。
相反的是pull模型,指下层根据自己的判断从上层取回信息,可能增加下层对上层的依赖关系。
将相邻层解耦
自上而下的通信,适合使用单向耦合:修改第N层时,不用考虑第N+1层,条件是被修改的服务的接口和语义保持不变。
自下而上的通信,可使用回调:上层向下层注册回调函数,特定事件发生时调用这些函数。下层负责存储事件到回调函数的映射。
制定错误处理策略
错误发生后,要么在当前层处理,要么转给上一层。
应尽可能在当前层处理错误,否则至少应该将类似的错误类型合并为更笼统的错误类型再向上传播。
TCP/IP是使用最广泛的通信协议,它没有严格准守OSI模型,只包含4层:应用程序-TCP-IP-传输介质。
TCP/IP对各层的行为,及在各层间传输的数据分组的结构,做了严格的定义。
Relaxed Layered System
每层都可能使用它下面所有层的服务,而不仅仅是下一层的服务。
此变种提高了灵活性和性能,但代价是难以维护。
适用于变化不频繁,且性能比可维护性更重要的情景,如基础设施系统。
Layering Through Inheritance
将低层实现为基类,高层继承低层的实现,从而能够请求基类的服务。
优点是高层可以按需修改低层的服务;缺点是继承关系将高层和低层紧密地耦合,修改基类会影响其超类。
虚拟机
如果低层能够向高层隐藏低层的细节和硬件,就可以认为它们是虚拟机。如JVM
API
应用程序编程接口(API)封装了常用的低层功能。
信息系统
商务领域的信息系统通常采用两层架构:数据库和应用程序。
分层模式具有以下优点:
各层可重用
如果层表示的抽象明确,接口清晰具有良好的文档记录,便可在多种环境中重用它。
以黑盒方式重用现有层可显著减少开发工作量,还可减少缺陷。
支持标准化
通过采用定义明确且为大家普遍接受的抽象层级,可开发标准化接口,从而可以更换接口的实现。
这让你能够在不同层使用不同厂商的产品。
限制了依赖关系的范围
由于层间的标准化接口,修改代码、更换硬件只会影响当前层。
这提高了系统的可移植性,也改善了可测试性,因为能够独立地测试特定层。
可更换性
低层改动对接口进行适配,高层无需修改接口就可以照常请求低层的服务。
为了获得可更换性这一优点,付出的代价是加大了工作量,可能降低运行性能。
缺点:
行为变化可能引发雪崩效应
高层变化通常不受低层变化的影响。如果为完成局部调整必须对很多层做大量修改,那分层就成了缺点。
效率低下
例如,通信协议对来自高层的消息进行转换:添加报头和报尾。
不必要的工作
如果低层服务做了高层为要求的多余或重复工作,将对性能带来负面影响。
例如,低层做校验,高层也做校验。
难以确定正确的层次粒度
层数少不能充分发挥分层结构在可重用性、可修改性、移植性方面的潜力;
层数多会增加不必要的复杂性,还会增加各层分离以及对参数和返回值进行转换的开销。
背景
需要分解的大型系统
问题
设计一个系统,其主要特征是需要同时解决高层问题和低层问题,且高层操作依赖于低层操作。
需要平衡以下作用力:
解决方案
将系统划分成适当的层数,并将它们堆叠起来。从最低的抽象层开始,沿抽象梯度往上堆叠到顶。
每层的所有组件都必须处于相同的抽象层级。第N层提供的大多数服务都由第N-1层提供的服务组成,服务还可能依赖于同一层的其他服务。
结构
分层模式的主要特征是:第N+1层只使用第N层的服务,层之间没有其他直接依赖关系。
这种结构类似于栈或洋葱,每层都将下面的各层保护起来,禁止上面的层直接访问它们。
动态
从上往下传输的信息和控制流被称为“请求”,从下往上传输的调用被称为“通知”。
自上而下的通信:客户端向第N层发出请求,第N层将一些子任务交给第N-1层,第N-1层处理交给下一层,直到第1层。将高级服务映射到更低级的服务。自上而下传输的请求常被拆分为多个请求。
自下而上的通信:第1层得到输入后,将数据交给第2层,第2层及之后层进行处理并向上传输,直到到达顶层。自下而上传输的通知在上层要么被合并,要么不拆分也不合并。
请求不穿越所有层。当其中一层能满足该请求时,例如缓存层。
通知不穿越所有层。例如重发通知,需要拦截。
两个分层的栈相互通信。这种情景的栈被称为协议栈。
实现
实现步骤,每步并非必不可少:
定义将任务划分到不同层的抽象准则
例如,根据离硬件的距离划分较低的层,按概念复杂度划分较高的层。
根据抽象准则确定抽象层级数
层数太多可能带来不必要的开销,层数太少又可能导致结构不清晰。
给每层命名并分派任务
最高层的任务就是整个系统要完成的任务。其他各层的任务是辅佐上一层。
规范服务
实现原则是相邻层界线分明,任何组件都不跨越多层。
完善层次划分
反复执行1-4步,直到分层结构自然而稳定。
规范每层的接口
黑盒方法:第N层对第N+1层而言是黑盒,就应设计一个统一接口,提供第N层的所有服务。
白盒方法:第N+1层知道第N层的内部结构,直接调用服务。
灰盒方法:第N+1层知道第N层的部分内部组件,直接调用这些组件,但不知道组件内部构造。
应尽可能采用黑盒方法。
确定各层的结构
重点是确保层间关系合理。对于复杂的层,应将其划分为多个组件,避免层内部混乱。
规范相邻层之间的通信
在层间通信方面,最常用的机制是push模型:第N层调用第N-1层的服务时,随服务调用传递所需信息。
相反的是pull模型,指下层根据自己的判断从上层取回信息,可能增加下层对上层的依赖关系。
将相邻层解耦
自上而下的通信,适合使用单向耦合:修改第N层时,不用考虑第N+1层,条件是被修改的服务的接口和语义保持不变。
自下而上的通信,可使用回调:上层向下层注册回调函数,特定事件发生时调用这些函数。下层负责存储事件到回调函数的映射。
制定错误处理策略
错误发生后,要么在当前层处理,要么转给上一层。
应尽可能在当前层处理错误,否则至少应该将类似的错误类型合并为更笼统的错误类型再向上传播。
示例解答
TCP/IP是使用最广泛的通信协议,它没有严格准守OSI模型,只包含4层:应用程序-TCP-IP-传输介质。
TCP/IP对各层的行为,及在各层间传输的数据分组的结构,做了严格的定义。
变种
Relaxed Layered System
每层都可能使用它下面所有层的服务,而不仅仅是下一层的服务。
此变种提高了灵活性和性能,但代价是难以维护。
适用于变化不频繁,且性能比可维护性更重要的情景,如基础设施系统。
Layering Through Inheritance
将低层实现为基类,高层继承低层的实现,从而能够请求基类的服务。
优点是高层可以按需修改低层的服务;缺点是继承关系将高层和低层紧密地耦合,修改基类会影响其超类。
已知应用
虚拟机
如果低层能够向高层隐藏低层的细节和硬件,就可以认为它们是虚拟机。如JVM
API
应用程序编程接口(API)封装了常用的低层功能。
信息系统
商务领域的信息系统通常采用两层架构:数据库和应用程序。
效果
分层模式具有以下优点:
各层可重用
如果层表示的抽象明确,接口清晰具有良好的文档记录,便可在多种环境中重用它。
以黑盒方式重用现有层可显著减少开发工作量,还可减少缺陷。
支持标准化
通过采用定义明确且为大家普遍接受的抽象层级,可开发标准化接口,从而可以更换接口的实现。
这让你能够在不同层使用不同厂商的产品。
限制了依赖关系的范围
由于层间的标准化接口,修改代码、更换硬件只会影响当前层。
这提高了系统的可移植性,也改善了可测试性,因为能够独立地测试特定层。
可更换性
低层改动对接口进行适配,高层无需修改接口就可以照常请求低层的服务。
为了获得可更换性这一优点,付出的代价是加大了工作量,可能降低运行性能。
缺点:
行为变化可能引发雪崩效应
高层变化通常不受低层变化的影响。如果为完成局部调整必须对很多层做大量修改,那分层就成了缺点。
效率低下
例如,通信协议对来自高层的消息进行转换:添加报头和报尾。
不必要的工作
如果低层服务做了高层为要求的多余或重复工作,将对性能带来负面影响。
例如,低层做校验,高层也做校验。
难以确定正确的层次粒度
层数少不能充分发挥分层结构在可重用性、可修改性、移植性方面的潜力;
层数多会增加不必要的复杂性,还会增加各层分离以及对参数和返回值进行转换的开销。