设计风格和设计模式
设计风格和设计模式这两个概念对于初学者(本菜鸡)而言还是比较容易混淆的,尤其是中文里两个词语并没有很明确的体现出他们的区别。这两者的共同点在于,它们都是为软件的设计提供一种可以复用的模板,且在同一项目中可以搭配使用不同的风格/模式。而这两者的区别在于,设计风格是较为宏观的架构设计,而设计模式则更偏向于代码设计。
- 设计风格(style):较为宏观的设计,一般对应软件架构层面,一般会有对应架构图。
- 设计模式(pattern):如 23 种设计模式,一般对应代码层面,一般会有对应样例代码。
管道/过滤器风格(Pipes/Filters Style)
管道/过滤器风格使用管道和过滤器一连串的过程和事件,每个事件的输出是下一个事件的输入,连续的事件之间可能有数据缓存。
- 过滤器(filter):相互独立的数据处理器。
- 管道(pipe):串联各个过滤器,进行数据翻译和传输。

变体
- 反馈回路(feedback-loops),如 P6,将数据回传给曾经经过过的过滤器。
- 分叉管道(splitting pipes),如 F2 和 F3,将同样数据传输给几个不同的过滤器。
优点
- 高内聚(high cohesive):每个过滤器实现一个功能。
- 低耦合(low coupling):过滤器之间仅通过管道联系。
- 可重用(reusability):通过连接不同管道,过滤器可重用。
- 实现简单(easy to implement):要么并发(concurrent)要么按序(sequence)。
- 可扩展(extendibility):增加过滤器很容易。
- 灵活性(flexibility):容易重新规划。
缺点
- 为了确保管道和过滤器可以以任意方式组合,需要给管道规定数据传输格式,并在传输前后对数据进行编码和解码,增加大量开销。
- 很难支持基于事件的交互(event-based interaction),过滤器需要通过数据的输入输出进行连接。
仓库模型(The Repository Model)
仓库模型将所有数据存储在一个“仓库”中,在需要数据时,其他相关部分从仓库中取用数据。仓库模型通常用于需要共享大量数据的时候,可以通过共享数据库或同时更新所有子数据库实现。仓库模型包括黑板风格和仓库风格。

黑板风格(Blackboard Style)
当某一子系统修改了“仓库”中的数据时,所有其他子系统都会收到提醒。该提醒由一个特定的触发器(trigger)来实现。
触发器包括以下三个部分:
- 事件(event):当某个子系统修改数据时,触发警报。
- 条件(condition):确认触发器是否活跃。
- 活动(action):当警报被触发且触发器活跃时执行的特定操作(如通知每一个元件数据修改情况)。
仓库风格(Repository Style)
当子系统需要数据时,子系统自行确认当前的数据情况。一个子系统修改数据不会提醒其他子系统。
优点
- 高效共享大量数据。
- 高内聚(high cohesive):每个子系统相互独立,元件间仅通过共用数据进行耦合。
- 共用数据有利于数据维护和修复,便于保护数据安全。
缺点
- 数据难以管理,一个格式修改可能会同时影响很多元件。
- 重构数据会很困难且花费巨大。
- 当数据存储出现问题整个系统将会崩溃。
客户端-服务器风格(Client-Server Style)
客户端-服务器风格由一个服务器和多个客户端组成,客户端和服务器基于RPC网络交互协议远进行通信。在客户端-服务器风格中,所有客户端都依赖于同一个服务器,但是客户端之间相互独立。客户端知道服务器的情况,但是服务器不知道客户端的信息(位置,数量等等)。客户端-服务器风格多用于多用户共享同一份数据信息的情况。
- 客户端(client):向服务器发出请求,并使用系统环境处理输入输出。
- 服务器(server):基于自身存储的数据和请求处理办法,响应客户端请求。

在通常情况下,一个系统可以被分成三层,即给用户显示的表现层(presentation logic),处理业务逻辑的业务层(application logic),和处理数据的数据层(data management)。根据业务层的位置不同,客户端-服务器风格又可以分为瘦客户端和胖客户端。
- 瘦客户端(thin client):只有表示层在客户端,数据和业务层都在服务器上。网络传输较少,但是对服务器性能要求较高。
- 胖客户端(thick client):表示层和一部分业务层在客户端上,另一部分业务逻辑和数据在服务器上。减少服务器性能压力,但是客户端相对臃肿,且需要更多数据交互。
优点
- 扩展性(scalable):可以随时添加、移除客户端。
- 有效利用网络传输,减少客户端的硬件要求。
- 可以轻松修改服务器,从而同时调整所有的客户。
- 允许多个用户共享数据。
缺点
- 系统性能受服务器性能、网络传输的限制,当用户量变大时性能会变差。
- 修改客户端十分困难,需要同时修改所有客户端,或让服务器同时支持不同客户端版本。
MVC 风格(Model-View-Controller)
MVC 风格将整个业务拆分成模型、视图、控制三个部分。三个部分各司其职,相互协助。
- 模型(model):处理主要业务逻辑。接收控制器的输入,根据输入执行对应的逻辑,并将结果返回给控制器。此外,model 存储了所有调用该方法的 view,在需要的时候(如数据刷新时)刷新对应的 view。
- 视图(view):通常为图形界面。监听模型的变化,并根据变化向用户展示不同的内容。接收用户发交互并传送给控制器进行处理。在需要刷新时 view 会访问对应的 model 来获取当前状态。
- 控制器(controller):处理用户请求,选择恰当的视图及逻辑,给与用户反馈。

优点
- 可重用(reusability):多个 view 可以共享一个 model,因此可以方便的重复利用 model 中的函数在不同 view 中实现同一功能.
- model 和 view 相互独立。确定 controller 以后,model 和 view 的改变相互不影响。
- 可测试性 (testability):各个部分相互独立,可以方便的使用自动化测试等工具对各个部分进行测试。
缺点
- 功能拆分会产生大量文件,如果没有进行妥善的管理,容易产生混乱。
分层架构(Layered Architecture)
分层架构将软件分成多个层级,每个层级仅负责该层级的相关事务。每个层级可以访问它下层的接口以获得信息,并将处理结果封装成接口提供给它上面的层级。最底层应该不依赖于其他层级(比如数据操作),而最上层应该汇总所有功能(比如图形界面)。对于严格的分层架构,每一层仅允许获取它的下一层的信息,而宽松的分层架构允许该层调用它以下的所有层。
在应用分层架构时,软件设计者可以自行定义层数以及每一层的功能。过多的层次增加数据传输的开销,而层次太少则容易分工不明确导致架构混乱。因此,如何分层,如何定义每一层的功能,是使用分成架构时需要权衡的问题。

优点
- 高内聚(high cohesive):每层仅包含一种类型的功能。
- 安全性(security):每一层的具体实现方式被封装在层内,其他层只能访问接口。
- 逻辑修改方便,每一层的修改最多只影响它的上一层。
- 支持迭代式开发(incremental development),在层级中添加新功能不会对已有功能造成影响。
- 可重用(reusability):不同上层函数可以重复利用同一个下层接口,复用代码十分方便。
- 可测试性 (testability):可以方便的使用自动化测试等工具对接口进行测试。
缺点
- 当上层代码需要使用几层以下的信息时,严格的分层架构需要将信息逐层传输,会加大开销。
- 有时无法非常清晰的将逻辑拆分到不同层中,会产生混乱。
- 有时一个简单修改会影响多层(如改变数据结构),需要逐层修改,十分繁琐。
事件驱动风格(Event-Driven Style)
事件驱动风格主要包括管理事件输入的调度器(dispatcher)和对事件进行处理的处理器(processor)。每当出现一个新的事件(一般为用户输入或者其他相关系统的输入)时,调度器对事件进行过滤并传递给合适的处理器。处理器再对事件进行处理并给出反馈。系统运行需要实时的事件输入,因此事件驱动风格多用于实时交互的系统。
广播模型(Broadcast)
处理器在调度器处登记它接收的事件类型。当调度器接收到某个事件时,它会将这个事件传递给所有接收这个事件的处理器。处理器自行决定是否对事件做出反应,调度器仅仅负责传递事件。

操作中断模型(Interrupt-Driven Control)
调度器管理所有处理器。调度器决定新输入的事件是否需要通知某个处理器,或者是否需要中断某个处理器当前运行的事件来对新事件进行处理。处理器完全听从调度器的指令。操作中断模型通常用于需要快速响应的系统,即当事件输入时,经常需要调度器立即中断某个处理器的当前任务来对新的输入进行处理。

优点
- 低耦合(low coupling):处理器之间相互独立,只和调度器连接。处理器相互之间不影响,方便修改和维护。
- 可扩展(extendibility):只需要调整调度器即可方便的添加或删除处理器。
- 可以很好的支持出现多个处理器需要同时对一个事件做出响应的情况。
缺点
- 调度器是性能瓶颈。如果处理器过多,调度器会无法迅速找到并通知事件对应的处理器。
- 可靠性(dependability)问题:调度器一旦产生错误,整个系统就无法运行。
- 广播模型无法保证事件一定会被处理。
对等风格(Peer-to-Peer Style)
一个对等风格的系统中包含多个平等的节点(node),每个节点拥有独立的数据存储和功能,可以单独存在,节点与节点几乎没有相互依赖。不同节点之间依靠网络数据传输进行交互。对等风格是一种去中心化(decentralization)的分布式架构,节点相互交互而非统一和某个中心节点交互。

优点
- 有非常强的并行处理(parallel processing)能力,可以灵活的使用不同节点处理不同事物。
- 可以动态联通多个节点,因而不需要大规模投资某个节点的性能。
- 每个节点上都有数据备份,对故障的包容能力较强,且减少信息传输所需要的开销。
- 可以动态的添加删除节点,在不需要的时候可以轻易释放资源,可以最大化利用资源。
缺点
- 规模较大的情况下维护节点群十分复杂,资源分享信息同步容易紊乱。
- 安全性较低,大量独立节点及信息传输难以全方位保护,容易被攻击。
微服务架构(Microservice Architecture)
微服务架构将一个大的服务(如一个网上商城的后台管理系统)中的功能拆成多个微服务(如账号管理、商品管理、订单管理)。每个微服务拥有自己的逻辑及数据存储,并且可以独立部署。服务和服务之间通过接口和网络进行通信。

优点
- 模块化(modular):程序被拆解成可管理的服务模块,方便单独的进行开发、部署、管理和维护。
- 可以方便的单独对某一模块进行升级、重构,便于分工管理。
- 数据库拆分,降低数据库读取压力和性能损耗。
缺点
- 很多功能会涉及多个服务,因此定位错误十分困难,需要一个服务一个服务排查。
- 消息传输复杂且难以管理。
- 服务之间相互依赖,一个服务宕机会容易引发连锁反应。
参考资料
- 北京-都柏林国际学院 - Software Architecture - Xiaobin Xu
- 知乎 - 微服务架构是什么?
- 一篇文章快速理解微服务架构