经典软件架构模式(二)

今天继续推送“经典软件架构模式(二)之管道和过滤器模式、MVC模式。

管道和过滤器模式

第三个案例是一个WEB的例子,但并不是简单的CGI加数据库,而是一个在网站上点播图文铃声短信、订阅各种短信服务的系统。从界面上就可以看到,这个网站可以下发不同的歌曲铃声,各种手机格式的图片,还有一些特别的文字短信,这些称之为“点播”服务。

网站上搜集了大量的铃声、图片、文字来提供此功能。另外一类称之为“订阅”服务,就是每天会不定时发一些最新的新闻、黄历、心理测试等短信内容给订阅了服务的手机号。最后一类比较复杂,属于交互性服务,比如彩票、交友、股市、邮箱,这些服务让用户可以通过发送短信来和系统互动,比如查某个股票的价格,购买彩票,发布自己的交友信息,查收邮件内容等。总体上此网站的功能比较复杂,但大部分是把信息从互联网发送到手机上。

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

为了建设一个这样的网站,一般来说最开始,都是从两个模块开始:一个WEB网站,一个收发短信的进程。WEB网站最常见的方案,就是用PHP或者其他类CGI的程序,连接一个数据库,然后把页面展现给用户。

因此我们按照业务模块,把这些PHP划分成图片、铃声、文字短信、新闻、黄历、邮件……等等的模块,每个模块负责展现业务内容。当这些模块需要发送短信的时候,就把要发的短信的内容写到数据库的某个表里面。然后短信收发进程就从数据库里读取内容,发往各个不同的运营商的短信网关。

这样的系统本身是问题不大的,但是随着用户量的增长,首先出现了一个要命的情况:数据库负载很高。因为用户访问WEB网站需要读写数据库,发送短信也要先写数据库,短信收发进程也要先读后删数据库记录,这让数据库不堪重负。或者说,本身让数据库来承担这种“消息队列”通讯就是错误的用法。

同时,也发生了另外一个问题:由于接入的手机运营商越来越多,当时都是分省接入的,短信收发进程变的非常复杂,其原因是发送短信的内容并非只有收、发手机号和发送文本这3个字段而已,而是还有很多其他的逻辑要处理。譬如说每条短信都要填一个“业务代码”,代表了短信的资费信息,然而每个省运营商申请的代码都是不同的,一条新闻发往不同的短信网关,需要填写不同的“业务代码”。有时候还会需要“套用”和“借用”一些业务代码。这样的复杂逻辑代码都塞到每个短信发送进程里,随着频繁的商务关系的变化,全部修改起来非常麻烦。

因此我们做了一次重构,核心思想是把发送短信的各个流程,划分到不同的模块里,形成一个工作流。然后使用文件代替数据库,作为消息队列;加入专门处理短信商务关系的业务模块,统一解决类似“业务代码”的问题。这样处理之后,所有的处理过程都被分割在单一的模块里,可以很方便的单独修改。

这种架构,实际上就是“管道和过滤器”架构:把一个需要多重步骤处理的数据流程,分割为多个独立的处理模块,称之为“过滤器”,然后根据业务需要,把“过滤器”连接成处理数据的“管道”。所有的代码,都要符合“过滤器”的要求,也就是要针对特定接口“数据”的输入端和输出端。而且这些输入和输出端是可以互相连接的。

“管道和过滤器”——非常适合分布式系统

● 适应:面向数据处理。在很多情况下,我们都是需要处理某种特定格式的输入数据,然后结果输出成某种数据。这种“单进单出”的模型是非常常见的情况,比如在编译器、Shell脚本系统、网络处理系统。特别是如果你需要让多个服务器共同工作,最简单的方式就是写很多个进程充当“过滤器”,然后监听TCP/IP端口,等待需要处理的网络包,结果则是通过发送网络包给另外一个后续步骤处理的进程。由于过滤器进程天然的可以使用操作系统的网络功能,因此很容易让多个不同服务器上的进程系统工作起来。

● 不适应:输入输出复杂系统。假如是做一个人机交互的GUI程序,或者是一个输入有多个输出的程序,这些都打破了“单进单出”的模型,就不太适合这种模式了。当然你也可以强行这么做,结果就是要写很多代码把大量的输入和输出数据整理成单个输入、输出的的数据模型。

● 方法论:关注数据处理步骤。构造这种模式的方法,最重要的是切法数据的处理步骤,一般来说我们会根据处理的计算消耗,以及处理步骤所需的数据模型复杂程度来切分。在分布式系统中,我们会希望不要让一个过滤器的负担过重,影响服务器负载的均衡;在其他的系统中,我们希望每个过滤器的编程接口尽量简单,而不是要面临大堆复杂的状态值。

● 设计模式实现:

● 装饰器模式——我们可以通过组装多个过滤器对象,构造出一条处理管道。java.io库就是一个最典型的例子。

● 责任链模式 ——责任链就是一个典型的处理管道,我们可以用这种模式几乎一一对应的实现出管道和过滤器模式。

MVC模式

当我们要写一个GUI程序的时候,基本上都想到这种著名的架构模式。因此我们举一个简单的例子:MP3播放器。这个系统有播放按钮、进度条、播放状态、播放列表、歌词等部分,这些部分都要结合到一起来适应正在播放的一首歌。

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

如果我们只是简单的思考如何实现,往往会做的比较简单:我们按照可以显示的部分,划分出几个模块,譬如播放按钮模块,进度条模块,播放状态(歌曲标题)面板,歌词面板等等。然后在所有可以操作的GUI控件上增加操作事件函数。比如按下播放按钮,各个面板和进度条就开始显示对应的信息。但是,如果面板和按钮变多了,我们会发现写在事件函数中的代码会越来越复杂,因为整个界面的各个部分,都是关联的。按下一个按钮,可能需要修改多个面板,一旦写漏了一个逻辑,那个面板的显示就是错误的。随着UI界面变得复杂,这种关系会呈几何式增长,最终代码会变得不可维护。

但是实际上,我们只要稍加思考就会发现,只要加入一个表示“歌曲列表”的不可见的状态对象,就能大大简化这种关系。

由于操作UI基本上都是去修改播放的状态的,所以他们只需要管理这个对象即可。而所有的显示UI,都是从这个播放状态对象读取数据,不管具体要如何操作。而这个结构里的“播放状态对象”,就是MVC模型里面的Model,操作UI中的事件函数,就属于Controllor,而显示UI的模块,就是View了。

MVC模型本身并非是只有一种标准方案,而是存在多种不同的描述的。但是他们基本上都会把系统的模块定义为M\V\C三类。对于这三类模块之间的耦合,定义了一些必须遵守的原则。一般来说,会把程序的状态放在M型模块里,而供用户操作的代码,放在Controllor类型的模块,View类型的模块就专心的负责根据Model的数据来做效果显示。注意MVC模型并非“三层结构”!因为彼此之间形成对客户程序的屏蔽,而是互相之间都有双向的管理。

“GUI首选”——交互界面频繁改

● 适应:多变的交互界面。此模式1978年由Xerox PARC发明,用来配合Xerox发明的GUI界面的开发。是一种非常专用的架构。

● 不适应:非交互领域。需要注意的是PC程序中除了GUI部分,譬如游戏里面的场景——往往不是界面UI的,坚持使用MVC往往容易造成不必要的复杂。

● 方法论:以交互特征划分模块。区分出系统中哪些是需要“人”来操作的,哪些是用来单纯“输出信息”的,哪些是内部的状态,就是构造MVC系统的关键。

● 设计模式实现:

● 观察者模式——一般来说在View对Controllor的触发上,为了避免直接的耦合关系,都会使用观察者模式。有些做法下,Model会和对应View的“同步绑定”,他们的刷新事件,也是通过观察者模式的Update事件来通知。

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

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

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

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏WeTest质量开放平台团队的专栏

QQ空间掉帧率优化实战

作者:邓荣欣, 腾讯移动客户端开发工程师

2940
来自专栏牛客网

美团Android三面面试经历

之前在阿里实习,回来后只参加了美团的面试。最后有幸拿到了阿里的转正offer和点评平台的offer。这里简单地把我在美团面试过程中记录的一些问题分享一下,总体来...

3716
来自专栏编程

开发一款app从PHP到API接口

一、先简单回答两个问题: 1、PHP 可以开发客户端? 答:不可以,因为PHP是脚本语言,是负责完成 B/S架构 或 C/S架构 的S部分,即:服务端的开发。(...

2159
来自专栏C/C++基础

CVTE2017秋季校招笔试题回忆(C++后台岗)

2016.09.06晚参加了CVTEC++岗的在线笔试。笔试题型分为不定向选择题和编程题,总共27题。其中不定项选择题为25道,编程题2道。其特点是不定项选择题...

511
来自专栏大葡萄元元

开发一款app从PHP到API接口

答:不可以,因为PHP是脚本语言,是负责完成 B/S架构 或 C/S架构 的S部分,即:服务端的开发。(别去纠结 GTK、WinBinder)

381
来自专栏Kirito的技术分享

设计RPC接口时,你有考虑过这些吗?

RPC 框架的讨论一直是各个技术交流群中的热点话题,阿里的 dubbo,新浪微博的 motan,谷歌的 grpc,以及不久前蚂蚁金服开源的 sofa,都是比较出...

501
来自专栏PHP实战技术

设计模式之代理模式之读写分离!!!

小伙伴们你们的小可爱逗比又上线了!!! ? 最近感觉带表情的文章看多了,写篇文章不放上几十个表情感觉自己都写不出来什么!!!原谅你们的小可爱放荡。。。不羁。。...

3245
来自专栏王亚昌的专栏

UNIX编程艺术之“模块性”

     本章主要讨论模块划分、接口设计,提出了几个很重要的概念,包括紧凑性、正交性、自顶向下和自底向上的设计、SPOT原则、分层、插件化。下面就这几个概念,谈...

772
来自专栏IT大咖说

一位前端专家构建GraphQL工程的心路历程

内容来源:2018 年 6 月 9 日,国内某大型电商公司用户体验部门前端开发专家邓若奇在“杭州第一届 GraphQLParty—GraphQL与领域驱动带来的...

461
来自专栏java工会

Java Web技术经验总结

1365

扫描关注云+社区