经典软件架构模式

目录

(一) 架构模式是什么

(二) 分层模式案例

(三) 微核模式案例

(四) 管道与过滤器案例

(五) MVC模式案例

(六) REST模式案例

(七) SOA模式案例

(八) 如何选择架构模式案例

(九) 业界应用模式的经典案例

本文将分N天推送,从以上9个方面来对“经典软件架构模式”进行阐述。今天先详细分析前三点。

架构模式是什么

软件架构模式,诞生于软件开发的最大难题——需求变更。由于需求变更,导致了大量项目因为超出预算的人力、时间而归于失败。软件开发成本有限的,但需求变更似乎是无限的,这成为了一个非常难解决的问题。

软件需求变更的结果,基本上就是对于软件代码的修改。而软件代码的修改却是程序员们最头疼的事情。因为一些大型系统,其代码根本就无法完全看懂,即便能了解部分细节,在着手修改的时候,也会碰到“触一发而动全身”的问题:因为有些功能的修改,需要修改整个系统的很多部分,导致了无穷的BUG。

另外一个致命的问题,就是在紧迫的时间内,对于代码的修改往往只能依赖有限的一个或几个程序员,只有他们对系统是最熟悉的。但是面临巨大的工作量,几乎无法让更多的程序员参与进来,其他人只能干瞪眼。一旦熟悉系统的程序员离职,有可能就代表了整个系统无法维护。即便是系统能分割给几个人负责,在“集成”几个部分的代码的时候,其调试和除错的工作,又常常是旷日持久的,因为那些从来没协作过的代码,隐藏着大量的误解和不兼容问题。——这一切的根源,其实只是一个最简单的事实,就是系统中对于“代码耦合”的结构问题。糟糕的代码耦合让整个系统变得难以理解、难以修改、难以分工、难以集成。

针对代码耦合的问题,软件界进行了大量的理论研究和实践,最后发现:系统的架构设计,是改善耦合的最好方式。架构设计的本质,就是:

  1. 划分耦合的单位——也就是划分模块。系统应该划分成什么样的模块,代表了设计者对于系统应对的需求的基本理解。一旦能清晰的划分出模块了,其代码耦合就有了最基本的范围。而模块本身也是提示程序员理解系统的基本单位。

2. 规范耦合的形式——代码耦合的形式有很多种,如直接调用、事件响应、消息队列等等,这些形式提供了代码耦合的不同特征。直接调用的代码在静态阅读的时候非常容易理解,而事件响应则提供了运行时耦合的好处。耦合的形式还有另外一层含义,就是代码耦合的规范:那些模块之间应该直接耦合,哪些不能耦合,是否应该加入中间层次等等。

这两个关于耦合的设计,就形成了各种架构设计。

在软件界多年的架构设计工作之后,人们总结出一些经验,这些经验被成为“架构模式”。架构模式包含了“名称”“适应场景”“模块定义”“模块关系”这几个部分。没有任何一种架构模式是万能的,所以每个模式都必须有“适应场景”。而模式本身的内容,就是通过“模式定义”和“模块关系”两个层面的规定来表现出来。除了模式的本身内容,为了理解某种特定的架构模式,人们还会附带上一些应用案例,好让学习者能通过实例来理解这种设计的真谛。

最后,还会有些关于某种设计模式的实现手段——在面向对象的设计思想下,往往会使用一种或者多种“设计模式”来作为实现。当然只要是符合架构模式的耦合规定的,都可以成为某种架构模式的实践,但是使用软件业常见的“设计模式”,还是能比较轻松的去用代码来实现这些架构。

分层模式

下面我们开始第一个架构模式的介绍,先看一个案例:

【此案例并非完全真实情况,有一定提炼修改成分】

这是一个有着复杂功能的多人在线社区,其服务器端是我们需要讨论的重点。这个产品的服务器端必须满足多样的功能:玩家移动到不同的场景中,玩家可以换上不同的服装,可以互相加好友并且聊天,同时还有广播频道的聊天,每个玩家还有自己的资料库和背包,另外还有各种运营活动。

在最初的开发过程中,我们针对每个需要开发的功能,建立了一个模块。这些模块通过单独和客户端、数据库的操作,完成所需功能。如果要开发新功能,就重新写一个这样的模块。这种架构设计在一开始是非常有效的,产品功能被不断的开发出来,模块的数量也在增多,但是也潜藏了一个问题。

这个问题的爆发是随着一个叫做“任务系统”的功能而出现的。因为任务系统本质上是需要很多其他模块的功能提供支持。如需要玩家去某个场景(场景模块),获得某个东西(背包模块),然后添加一个好友(好友模块),或者换上某个服装,说一句话等等……这样的任务功能的实现,被迫要修改很多个模块的代码,因为每个模块都只有最基本的“自由使用”功能的代码,编程接口都仅仅是面向客户端的,而数据结果都是直接SQL到数据库的。这种需要组合的功能请求,以及获得功能的结果状况,都是其接口上没有的。这导致了非常复杂的,持续的代码修改。因为任务的内容可是时常会变化的哦!

在分析了问题之后,我们决定重构整个架构,我们把架构从一字排开的设计,修改成为可以多个层次互相调用的模块。这些模块直接的接口,有面向客户端的,也有面向其他模块的,这样我们就能直接调用那些现成的功能,组合开发出更复杂强大的功能。不管任务系统如何变化,我们都可以不用重写那些已经实现的功能,这让整个系统成为可以应对这种需求变化的关键。

实际上,这样的架构正是一个著名的架构模式——分层模式。

分层模式的规定非常简单而有效:

1. 每个模块都必须属于某个层次,提供给“第N+1层”(上层)服务;同时委派任务给“第N-1层”的模块。

2. 任何一个模块,都不得逆层次调用:属于第N层的模块,不得调用(耦合)第N+1层或以上层次的模块。

3. 任何一个模块,都不得跨层调用:属于第N层的模块,不应该调用第N-2层或更下层的模块。

分层模式是架构中最基本的模式,但是也是我们开发中最被忽视的模式。我们开发中往往没有去定义代码的“层次”,仅仅以“功能”纵向划分模块,没有按实现层次横向切分。

门面模式、策略模式都常常用来实现分层架构。

上文所说的例子,后来改造成了分层模型:

1)重复代码减少了,功能开发更快了——一顶层开发只需要学习下层类库,就可以开始开发;近似功能模块被统一,修BUG覆盖面更好。

2)性能提升代码能更好的覆盖了——集中了通用实现代码,也集中了优化部分

3)可以并行开发,资深人员负责底层,一般人员负责上层

总结一下分层模式:

“模式的模式”——强大同时灵活

★ 适应:集成不同类型功能 ——当我们需要把很多不同功能的代码集成起来的时候,这种模式提供了最合理的结构。能让我们的代码有足够的灵活性去应对需求变更。

★ 不适应:简单业务模型——如果系统本身不复杂(或者叫做可预期的修改很少),建立各种层次,然后为了符合层次间调用的规定,会增加很多不必要的代码,陷入过度设计的泥坑。

★ 方法论:以业务逻辑特征建模 ——使用分层模式,往往需要我们在大脑里对问题领域进行层次抽象,这种抽象最可信赖的原则,就是按照业务逻辑去做。比如现实业务中有一个角色,我们就建立一个角色的模块;如果我们有一个场景,就以此为名建立一个这样的模块……。以业务逻辑建立的模块,本身也会让系统更容易被理解,因为在代码里能找到和现实中一一对应的概念。

★ 设计模式实现:

★ 门面模式 ——我们对于每个模块或者每个层次都会设计一个“门面”来降低耦合的复杂程度。

★ 策略模式——抽象层次会隐藏底层的实现细节,这就是策略模式最基本的设计,我们往往会把上层作为功能接口,下层作为可选的策略来实现。

微核模式

我们来看第二个案例,这是一个多人在线的游戏社区。在这个社区中,除了可以聊天、换衣服之外,还有大量的小型、中型、甚至大型游戏可以玩。这些游戏的内容,都会和整个社区以及角色集合起来,有点像几十个不同的游戏融合成的一个大游戏。

【此案例并非完全真实情况,有一定提炼修改成分】

我们这次要讨论的是客户端的架构。由于这个游戏,有一个统一的社区场景,还有大量的游戏内容,再加上覆盖在场景上的UI,所以我们针对每个功能,设计了模块。如“场景:码头”就是一个模块,包含了场景中的美术资源和其他所有功能。每个游戏也是一个单独模块,玩家开始这个游戏,就整个进入这个单独的模块。退出游戏则返回到之前的场景模块中。而UI也是分成主工具栏,聊天界面等模块,每个可以打开的面板,都是一个单独的模块,比如背包就是一个模块。

这种设计在初期有很大的好处,因为每个模块都可以单独的开发、修改和测试。一个模块包含了所有的代码和资源,是一个完整的整体。对于添加功能,只需要增加一个这样的模块,然后把连接代码组合到就的模块中就大功告成了。

但是这种架构并没能维持很久,因为策划要求加入一个“商店系统”,需求是:玩家能够在场景中触发商店的界面进行购买操作,同时在每个游戏中,也要能购买物品,但是界面却不能是商店本地的UI界面,因为要和游戏中的风格结合。同时,玩家还要能从聊天界面中,通过点击文字连接,去打开针对物品的商店界面。这种需求同样需要修改多个模块,导致工作量很大。加上每个模块都是单独开发的,连开发人员都不是同一个人,所以这种需求要实现起来似乎困难重重。同时作为修改速度非常快的客户端部分,策划和程序员们也都迫切希望能维持之前的能单独调试的结构。

因此,我们通过改进架构,实现了一个“不可见”的客户端模块——“统一处理框架”,这个框架负责处理网络通信、界面事件、模块事件三个主要的模块关联操作。

每个原来的模块,都必须使用这个“统一处理框架”来和其他模块互动。另外,我们还对所有的游戏整合了一个“游戏系统”的模块,对游戏中统一的开始游戏、结束游戏、物品产出、物品消耗等功能做了接口定义。

所有的游戏都需要符合“游戏系统”的需求,实现其要求的接口,这样整个系统任意的代码,都可以使用这些接口去控制一个具体的游戏行为。最后,我们把场景的基础代码和具体美术资源、具体逻辑代码做了拆分,形成了一个“场景播放器”模块,这个模块能让任意的代码,实现对任何一个场景的切入和退出。

通过这样的修改,对于商店系统的实现,仅仅是调用“统一处理框架”的API,发送几个功能事件,具体的图形播放和逻辑处理都会由提供功能的模块实现。同样,这些每个模块还是可以单独运行和单独调试,他们之间的关系,都通过发送事件和接收事件来处理就可以了。你可以发送一个没人处理的事件,也可以自己虚拟一个需要接收处理的事件,这样就可以调试任何一个模块。

这个架构,实际上就是参考了著名的“微核模式”而构成的。微核模式的核心是:

• 基本服务封装到微核 ——主要是一些每个模块都会用到的功能

• 内、外服务器负责功能实现(插件系统)——外服务器负责整合某个特定领域的抽象。内部服务器负责通用的功能抽象,如网络功能、日志等。

• 应用程序、服务器通过微核通信 ——这是最核心的部分,一个基于“事件”的运行时交互系统,用来沟通各个不同的模块。

• 外部服务和应用程序的差别,在于是否通过一个适配器来和微核耦合。这个适配器实际上能让应用程序模块更换不同的微核,这在于可移植系统上很重要。

微核模式实际上是一种特化的分层模式,他把最底层的功能封装层“微核”,同时把各个模块的交互规定为“运行时的事件”。这样简化了的3层架构,提供能非常好的模块独立性。

微核模式也就是我们常见的“插件系统”——模块高度独立,可移植

★ 适应:运行时多模块协作系统 ——如果我们需要系统可以运行起来之后,动态的加载和运行不同的模块,微核将是最合适的架构。在许多需要运行时扩展的系统中,比如一些IM软件想要带上各种和好友关系有关的功能;或者是希望同样的代码能在不同的“平台”、“环境”、“操作系统”下运行,都会使用这种架构。

★ 不适应:无需运行时多模块协作系统——如果软件本身不会分为多个需要不定时启动、运行的模块,就不必要实现这个稍嫌复杂的架构。

★ 方法论:实现运行时耦合——这个架构的核心,是把代码的直接耦合,变成运行时的动态调用,因此我们会使用事件机制、消息队列等手段,把代码的调用和具体的“数据”关联起来,从而避免了代码直接写死。

★ 设计模式实现:

观察者模式——观察者模式是最常用的运行时耦合的编码手段,在其他的架构中,我们还将见到他的身影。他是如此的常见,以致于JAVA直接把这个模式的实现代码收纳标准库JDK当中了。

感谢大家的阅读,如觉得此文对你有那么一丁点的作用,麻烦动动手指转发或分享至朋友圈。

原文发布于微信公众号 - 韩大(handa1740168)

原文发表时间:2015-12-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏zhisheng

Java攻城狮是如何养成的?

经常逛知乎,收藏了不少学习方面的资源,以后有机会会慢慢的分享出来,让大家在学习的道路上少走些弯路。今天分享一篇文章《java攻城狮如何养成的?》,干货挺多的,,...

3044
来自专栏杨建荣的学习笔记

自问自答的一些运维开发问题

CMDB完成了80%的基础开发工作,后续需要对元数据信息进行确认和完善,还有部分的接入。

903
来自专栏JavaQ

三分钟学习持续集成

什么是持续集成 持续集成(Continuous Integration),简称CI,是持续地编译、测试、审查、打包、部署源代码的过程,是一种软件开发实践。 持续...

2665
来自专栏前端架构与工程

浅析前端工程化

1. 什么是前端工程化 自有前端工程师这个称谓以来,前端的发展可谓是日新月异。相比较已经非常成熟的其他领域,前端虽是后起之秀,但其野蛮生长是其他领域不能比的。虽...

3067
来自专栏子勰随笔

SDK之我理解的SDK

26610
来自专栏用户画像

6.3.3 异步定时方式

在异步定时方式中,没有统一的时钟,也没有固定的时间间隔,完全依靠双方相互制约的“握手”信号来实现定时控制。通常,把交换信息的两个部件或设备分为主设备和从设备。主...

903
来自专栏程序员的知识天地

这 8款开源思维导图工具真的很神奇【程序员必备学习工具】

思维导图,除了帮助概括提炼、归纳总结、理清楚思维逻辑之外,还是一种非常有效的学习方式,能激发联想力,再衍生出更多的想法。

1152
来自专栏BestSDK

想用APP创业,那你要明白API的重要性?

每一家初创企业和公司都会有提供给世界的接口。有的接口超级简单,比如Google—你能做的只有搜索;有的复杂一点,比如在Amazon上面买东西—你可以浏览、搜索、...

2879
来自专栏恒思考

做什么样的软件系列之Firebase

为什么要写这一篇? 做为一个iOS开发者我没有精力自己实现一套,登陆系统后台,广告系统后台,自己尝试写过身份认证系统,但是忘记密码之类的写的又丑又简陋。同时写后...

1424
来自专栏程序你好

作为一名开发人员,来谈谈微服务架构应用的优势

与使用大型、单体架构应用程序相关的一个大问题是,您被自己的技术所束缚。这可以分两个层面来说,第一,更老的或更有说服力的框架更有可能被选择。这意味着你不太可能使用...

962

扫码关注云+社区