通常一个软件系统都包含不同部分互相交互耦合,我们希望设计能够将系统划分为有意义的各个部件,各个部件能够独立的开发、演进、部署。这时整体性的设计已经无法满足这些挑战,这就需要我们对系统进行合理清晰的划分。通常我们为待开发的系统定义多个层次,每一层完成独立的功能。
设计原则:
1:系统分为多层,每层完成独立的功能,层内部继续细分子模块,每层能够独立演进、部署。分层原则可以基于业务抽象、硬件、变化性等来划分,比如sqlite就是基于业务抽象来进行分层的。实际的框架设计可能同时结合多种维度比如常见的表示层、逻辑层、数据层就结合了业务抽象和变化两个维度。
2:各层的功能基于同层和底层的功能之上,如图所示L1的所有功能基于本层、L2、L3(实际设计中不建议基于跨层功能),也就是说上层只能调用本层和下层的接口。这样的设计避免互相调用导致的结构复杂。
3:各层提供相应的接口与实现分离,对该层的访问只能通过接口进行。通过接口进行访问有效的分离了关注点。外层关注接口,层内部关注接口的实现,实现的修改不会对其它层产生影响。实际应用中可以使用适配器模式来有效的分离接口和实现。假设业务数据可能通过不同的协议栈承载实际数据的传输,不同的协议有不同的实现,我们可以如下设计接口:
上层使用如下数据传输接口:
struct transportDescription { int (*receiveData)(void*privateConnectionInfo,void*buffer,int bufferLen,); int (*sendData)(void *privateConnectionInfo,const void *buffer,int bufferLen); }
下层提供协议注册接口:
int initializeTransports(transportDescription* pTransInfo);
#ifdef _HTTP_ #define initializeTransports HTTP_initializeTransport #endif #ifdef _WSP_ #define initializeTransports WSP_initializeTransport #endif
上层应用通过调用initializeTransports注册承载协议,上层只需要调用receiveData、sendData 接口进行数据收发,下层协议信息对上层完全透明,通过定义不同的协议宏可以在不同的协议间切换而不影响上层的实现。
4:底层不能依赖高层的功能,这样设计避免了结构的复杂,你不能指望操作系统来调用你的接口吧。但是一些系统的事件确实需要上层应用做出相应的处理,该如何处理呢?这就需要用到观察者模式了,在C语言中一般通过回调机制来实现,这时下层提供接口,上层把实现注册进去。这样底层就可以在不调用上层的接口下控制上层的行为了。
5:数据流是双向交换的,比如在协议栈中数据可以在相邻的层次之间交换的,下层到上次的数据流可以有多种方式比如消息机制、回调机制等。消息机制有利于分布式部署并且是一种松耦合的形式,只要接收双方定义了消息格式即可,不再需要依赖具体的接口。
分层架构通过对关注点的分离有利于分化系统的复杂性,提高系统的可扩展和可维护性,但在分层架构中为了获取底层的功能可能需要进行多个层次的传递,不可避免的导致性能的下降。为了保持架构的稳定在开发中增加功能往往需要在各层都要添加相应的代码。