1 关于设计模式
设计模式是什么?个人理解,其是软件开发中对一些通用问题整理的解决方案,是经过经验总结所提炼的相对较为抽象的,有一定适应性和变化性的"套路"。这里借用了"套路"这个不太好听的词,但目的却是为了避坑。
关于设计模式经典的著作就是称为GOF的四人组所编写的那本Design patterns。多说两句,关于设计模式,个人有两点看法:其一,现代软件系统设计的越来越庞大复杂,能够用简单的概念和方法解决问题,反而是一种本领。这样一来,软件的可维护性和健壮性将得到大大提升。如果有一天,人工智能发展到绝顶聪明的程度,不再需要人类编写维护软件时,这一条观点就可以过时了,但是现在还远远未到这个要求,所以这一点还是要奉为圭臬。很多一线大佬都指出,哪些复杂无比,充斥不必要技巧的代码,往往是对问题没有彻底理解清楚的产物,对于这一点,我是比较认可的。回到设计模式,我觉得书中所体现的思想是值得学习的,而具体各个模式的方法,反而是要避免复杂化的。形式大于内容并不可取,容易走向花架子方向,偏离本质。反倒是有一些简单的介绍这些模式的书籍,倒是比较推荐的,像大话设计模式。其二,当你学完所有的模式后,不知道是否有这样一种感觉,就是所有模式其实都是围绕着一个中心点在反复咀嚼,这个中心点就是抽象。如果一个人把抽象掌握的熟透,并在实际开发中加以应用,回过头来,你会发现,他或多或少都是采用了某个设计模式,或者说有某个模式的影子。可能并不完全一致,但是其却有相通的妙处。
运用抽象的好处,我在发布的其他博文里也有提及并加以反复强调,这里再提提。抽象最大的好处,就是本质不变特性。这一特性应用于软件开发,特别适合应对变化。因为变化是软件开发的最大绊脚石。有了这一法宝,当然,前提是拿捏的好,运用的秒,就可以做到以不变应万变。关于抽象,这里就说这么多,不再展开。
博主第一次接触到大量运用设计模式的代码是Android的源码。对于初学者而言,是不错的学习资料。在了解了设计模式的概念后,遇到实际的代码,要多想想,为啥采用这种模式,有什么好处。多思考,才能有收获。
2 设计模式本身的概念:
设计模式被分为三大类型,分别是创建型、结构型以及行为型。我是比较讨厌这种分类的,因为官方的分类方法和我自己的理解总感觉有出入,这样在拿出来要用的时候,就比较痛苦,需要一些背诵记忆的能力。这种感觉跟中学学政治一样,不知道是不是因为自己语文太差,对一些文字的含义理解不到位而导致了这种错觉。既然官方按这种方式分类了,记住就好,实在觉得别扭,就找一些比较容易理解的能够往官方分类方向靠的例子来辅助记忆。
具体的分类如下:
创建型包括工厂模式、抽象工厂模式、单例模式、构建者模式、原型模式
结构型模式包括桥接模式、适配器模式、组合模式、代理模式、享元模式、装饰模式、外观模式
行为型模式包括职责链、策略模式、迭代模式、命令模式、状态模式、观察者模式、解释器模式、中介模式、访问者模式、模板方法、备忘录模式
关于这些模式的具体说明,这里就不再展开了,网络的资料十分丰富。虽然GoF提炼了23种模式,但是实际中,常见的模式就那么几种,没必要死扣每一种,而且不用的那些,时间长了也就会遗忘,所以大概了解即可,达到给个选择题能够分辨出来的程度即可。
3 设计模式在系统中的应用和体现
对于设计模式,学以致用才是关键。能够在了解-理解-掌握-运用的链条上达到最终的自如运用,才说明内化于心了。下面,仍然以前述某电力系统项目为例,谈谈在项目中对设计模式的运用。
首先是适配器模式。这是很常见的一种设计模式,多应用于数据匹配上。在该电力系统项目中,也是做数据适配这一目的,但是细节上又有所不同。项目中,围绕会商功能开展了诸多业务,这些业务关联了诸多数据,如何有效、高效管理这些数据,就是设计上的一项小挑战了。比如,除了常见的音频、视频数据外,还有各种采集的电压、电流、温度、定位、图像、短信等各种业务数据。为了有效的管理这些数据,系统设计时,对数据进行了抽象,将其分解为两类数据,一种是流类数据,一种是包类数据,是不是有点类似TCP和UDP。确实,单从抽象概念上讲,是挺像的。每一类数据都有一个源与其关联,而所有的源都来自一个统一的抽象,这样当某个功能或业务需要数据时,就将其对接到抽象源上,也就是适配到统一的适配器上,而具体每一个数据源的处理,又根据其不同的特点,就自己独特的实现。这样,既满足了抽象的统一,有实现了个体的独立,后续有新的业务或数据需要适配时,也仍然走一套统一的抽象对接流程和具体的实现范式,不对整体系统的设计构成冲击,又满足了变化的需求,可谓一石二鸟。
从这个例子中,我们可以再次感受到前面所提的模式和抽象的关系。
其次,观察者模式。这也是非常非常常见的一个模式,特别是应对有回调处理的一类事务时。比如,像界面相关的处理,通用组件的一些处理(按钮响应、列表滚动选择等等),大都会用到这一模式。在该电力系统项目中,也是大量使用了观察者模式。首先是有关数据采集的部分,底层基于异步事件,构建了一个统一的框架,而每一类事件的数据响应,就是通过观察者模式对接到界面应用的,这包括了电压电流数据、GPS定位数据、电源管理数据、按键数据、WIFI事件、对讲事件等等。另外,对于音视频的处理也是采用类似的方法。底层首先设计了一套管线,用于完成音视频数据的采集、编码、传输、接收、解码、渲染等过程。但是,在这条管线上,还有许多关联业务需要处理,此时观察者模式就派上用处了。比如,满足特定条件时进行音视频的截取录制、混音降噪等处理,通过观察者模式实现数据的分流,在基本业务特性不受影响的前提下,可插入丰富的定制业务功能,实现主线和分支的统一管理,协同工作。
第三,模板方法模式。该模式的使用有点类似Android的activity抽象。在工单业务流上,采用统一的任务处理模板,基于抽象的任务模式,定制实现独特的各种子类业务功能,是不是又感受到了抽象的魔力。而且,界面的功能处理,也是采用了统一的生命周期模板,业务界面回到前台,退出前台,运行退出运行,都是该模式的体现。升级和文件的上传下载,也采用了统一的进度模板,只需要传递总量数据和当前片长即可。另外,终端部分的驱动,也是运用模板方法的生动体现。整个框架分为了初始化、读写、中断、去初始化等几个块,这样一来,将抽象和复用利用到了极点。抽象方便了复用,而复用,又促进了抽象,很多时候抽象并不是一开始就想象出来的,而是在开发过程中通过对共性的提炼总结,最终自然而然的就会形成抽象的雏形。
第四,状态模式。状态模式是对传统C语言中状态机的等价表示。客户端自身也是有状态的,比如登录、加入会商、在线、离线等,这些逻辑的处理,就是状态模式发挥作用的地方。另外,网络库部分,也是状态模式抽象的重点。连接、断开的高效管理,不仅简化了业务层面的开发,也使得整个代码更加易读,减少了错误,提高了效率,增强了可靠性,一举多得。
最后,还有其他一些模式的使用,比如代理模式,跨进程的接口代理,做测试桩,都是大量使用的。单例模式,一个时刻一台终端保持一个客户端,就用单例实现。全局统一的消息队列,也用单例,方便获取使用,保证了异步安全性。工厂模式,用于创建各种实例,特别是经典的数据库实例场景,通过工厂模式,统一支持不同数据库产品,不再为切换不同厂家数据库产品而改系统代码。
关于设计模式在实际系统中的应用就整理这么多。很多时候,我们并不是刻意去选择使用哪种模式(除非是一些非常常见的经典的场景,有现成可用的模式模板可以顺手拿来就用),而是先充分理解功能,理解业务本身,然后考虑各种可能和变化,在这个过程中尽可能的提炼,得到抽象物,之后再将抽象应用于设计实现,在这一整套思考并实践的过程中,你会发现,某些设计模式会自然而然的被应用于系统中,反倒是那些刻意为之的模式,往往最后给人一种别别扭扭的感觉,难以重构或者问题频出。其实,生活中的其他领域也是如此,体现本质的设计,往往更有生命力,而那些流于花哨形式的设计往往得不偿失。