zz从面向对象的设计模式看软件设计

原贴:https://coolshell.cn/articles/8961.htmlhtml

前些天发了一篇《如此理解面向对象编程》的文章,而后引发了你们的热议。而后我在微博上说了一句——“那23个经典的设计模式和OO半毛钱关系没有,只不过人家用OO来实现罢了……OO的设计模式思想和Unix的设计思想基本没什么差异”,结果引来了一点点争议。因此,我写下这篇文章把个人观点说明一下。我但愿这样可让你们更容易地理解什么是设计模式。我顺便帮OO和 Unix/Linux搞搞基前端

什么是模式

在正式说明GoF的那23个经典的设计模式其实和OO关系不大并和Unix的设计思想很类似的这个观点以前,让我先来讲说什么是模式?设计模式的英文是Design Pattern,模式是Pattern的汉译。所谓Pattern就是一种规则,或是一种模型,或是一种习惯。Pattern这个东西处处都是,并不仅有技术圏子里才有。好比:面试

  • 文章有文章的Pattern。如新闻有新闻的Pattern(第一段话简述了整个新闻),诗歌老是抒情的,论文老是死板的,讲稿老是高谈的,漫画老是幽默的,……
  • 小说有小说的Pattern。好比,
    • 武侠小说必然要整个武林大会,整几个NB的武功和大师,分个正派和反派,还有一个或数个惊天阴谋,坏人老是要在一开始占尽优点,好人老是要力挽狂澜……
    • 言情小说老是要有第三者,老是要有负心人,里面的女子老是要哭得死去活来,但又痴心不改,……
  •  新闻联播的模式是:头10分钟领导很忙,中间10分钟人民很幸福,后10分钟国外很乱。中国政府官方宣传稿也模式也很明显,各类赞美,口号,胜利,老是要坚持个什么,团结个什么,迈向个什么,某某精神,某某思想,群众情绪稳定,不明真相,等等……
  • 春节的模式是,回家,吃饺子,放个鞭炮,给压岁钱,同窗聚会…… 同窗聚会的模式基本上都是在饭桌上回忆一下校园时光,比较一下各自的当前处境,调戏一下女同窗……
  • …… ……

这就是Pattern,只要你细心观察,你会发现这世间有不少不少的Pattern。算法

 

GoF的23个设计模式

设计模式》这本书中,GoF这四我的总结了23个经典的面向对象的设计模式,某中有5个建立模式,7个结构模式,11个行为模式。不少人都会以为这是面向对象的设计模式,不少人也以为非面向对象不能用这些模式。我以为这是一种教条主义。就像《那些流行的编程方法》中的“设计模式驱动型编程”同样,就像《如此理解面向对象》同样的那么的滑稽。shell

好了,回到个人论点——“GoF的这23个设计模式和OO关系不大,而且和Unix的设计思想基本一致,只不过GoF用OO实现了它们”,就像我上面说过的那些生活中的Pattern同样,只要你仔细思考,你会发现这23个设计模式在咱们的生活和社会中也能有他们的身影。并且也同样能够用OO的方式实现之。数据库

让咱们来看看这23个经典的设计模式中的几个经常使用的模式:编程

Factory 模式,这个模式多是是我的都知道的模式。这个模式在现实社会中就像各类工厂同样,工厂跨界的很少,基本上都是在生产同一类的产品,有的生产汽车,有的生产电视,有的生产衣服,有的生产卫生纸……基本上来讲,一个生产线上只有作同一类的东西。这和Factory模式很类似。编程中,像内存池,线程池,链接池等池化技术都是这个模式,固然,Factory给你的一个对象,而不仅仅只是资源,factory建立出来的对象都有一样的接口能够被多态调用。这其实和Unix把全部的硬件都factory成文件同样,并提供了read/write等文件操做来让你操做任意设备的I/O后端

Abstract Factory:抽象工厂这个模式是建立一组有同一主题的不一样的类。这个模式在现实社会当中也有不少例子,好比:设计模式

  • 移动公司的合约机计划,88套餐(通话100分钟,短信100条,彩信,20条,上网200M),128套餐(通话200分钟,短信150条,彩信50条,上网500M)……
  • 家里的装修,老是要有厨卫,有门,有灯,有沙发,有茶几,有床,有衣柜,有电视,有冰箱,有洗衣机……,这些都是必需的,只是每一个家庭里的具体装修不同。
  • Diablo游戏中的Normal,Hard,Nightmare,Hell模式,这些模式的怪和场景和故事状况都差很少,就是每一个场景的怪物和装备的属性不同。或是WarCraft中的地图就是一个Abstract Factory模式(注:Warcraft的地图什么都能干)。这和学校中的小学,初中,高中,大学差很少,都是同样的学习环境,同样的教学方式,同样的教室,都要期中考和期末考,都有班长和科表明,就是学的东西的难度不同,但基本上都是语文,英语,数,理,化,还有永远都有的政治课。学校就是一个抽象工厂。

这就是抽象工厂的业务模型(或是:Business Pattern),你以为是否是不必定非要用OO来实现这样的模式?(咱们思考一下,咱们会不会被先入为主了,以为不会OO都不知道怎么实现了),不用OO,用相同格式但内容不一样的配置文件是否是也能实现?在Unix下,抽象工厂这个模式在Unix下就像是/etc/rcX.d下的那些东西,1表明命令行单用户,2,表明命令行多用户,3表明命令行多用户完整模式启动,5表明图形界面启动,0表明关机,6表明重启,你要切换的话,init <X>就好了数组

Prototype模式,原型模式,复制一个类的实现。这个模式在现实中的例子也有不少:传真,复印,都是这个模式。Unix进程和Github项目的Fork就是一种。进程fork明显不是OO的模型(参看:关于Fork的一道面试题)。用非OO的方法一样能够实现这个模式。

Singleton模式,单例模式。生活中,公司只有一个CEO,法律限制你只能有一个老婆,你只能有一个身份证号,一个TCP端口只能被一个进程使用,等等。软件开发方面,并不必定只有OO才能作到,你能够用一个全局变量,一个中心服务器,甚至可使用行政手段来约束开发中不会出现多个实例。Unix下实现单例进程的一个最经常使用的实践是在进程启动的时候用“(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)”模式打开一个“锁文件”

Adapter模式,适配器模式。能够兼容欧洲美国中国的插头或插座,万能读卡器,能够播放各类格式多媒体文件的插放器,能够解析FTP/HTTP/HTTPS/等网络协议的浏览器,能够兼容各大银行的银联接口、支付宝、Paypal、VISA等银行接口,能够适配各类后端的解释器的Nginx或Apache,等等。用非OO的编程方式就是从新包装成一个标准接口。这个模式很像Unix下的/dev下的那些文件,操做系统把系统设备适配成文件,因而你就可使用read/write来进行读写了

Bridge模式,桥接模式。这个模式用的更多,好比一个灯具能够接各类灯泡或灯管,一个电钻能够换上不一样的钻头来适应不一样的材料,一辆汽车能够随时更换不一样的轮胎来适应不一样的路面,你的桌面能够随时更换一个图片来适应你的心情,你的单反相机能够更换不一样的镜头来拍不一样的照片…… 桥接模式说白了就是组件化,模块化,能够自由拼装。在OO中,其主要是经过让业务类组合一个标准接口来完成,这在非OO的程序设计中用得实在是太多了,主要是经过回调函数或是标准接口来实现。这个也是Unix设计哲学中的主要思想。在Unix中,文件的权限使用的就是Bridge模式,标准接口是用户,用户组和其它,rwx三个模式,而后用 chmod/chown改一改,这文件就有不一样的属主和属性了

Decorator模式,装饰模式。这个模式在生活中太多了,你给你的手机或电脑贴个什么,挂个什么,吃东西的时候加点什么佐料,多点肉仍是多个蛋,一个Unix/Linux命令的各类参数是对这个命令的修饰,等等。我以为这个模式在Unix中最常常的体现就是经过管道把命令链接起来来完成一个功能,好比:ps -elf  是列进程的,用管道 grep hchen就能够达到过滤的目的,grep的逻辑没有侵入ps中,grep 修饰了 ps,可是其组合起来完成了一个特定的功能。可见,这和OO没有什么关系。

Facade模式,这个模式咱们每一个人从会编程的时候就在无心识地用这个模式了。这个模式就是把一大堆类拼装起来,并统一往外提供提口。在现实生活中这样的例子太多了,好比:旅行社把机票,酒店,景点,导游,司机,进店打了一个包叫旅行;IBM把主机,存储,OS,J2EE,DB,网络,流程打了个包叫企业级解决方案。Unix中最典型的一个例子就是用Shell脚本组合各类命令来创造一个新的功能,这是的Shell中的各类命令经过标准I/O这个接口进行组合交互。

Proxy模式,代理模式。咱们租个房,买个机票,打个官司,都少不了代理,人大表明代理了老百姓去行使政治权力。咱们去饭馆里吃饭也是一种代理模式,由于咱们只管吃就行了,洗菜作饭洗碗的工做都被Proxy帮你干了,因而你就省事多了。操做系统就是硬件的代理,CDN就是网站的代理,……使用代理你可让事情变理更简单,也能够在代理层加入一些权限检查,这样可让业务模块更关注业务,而把一些非业务的事情剥离出来交给代理以完成解耦。可见这个模式和OO没啥关系。Unix下这个模式最佳体现就是Shell,它代理了系统调用并提供UI。还有不少命令会帮你把/proc目录下的那些文件内容整理和显示出来。

Chain of Responsibility模式,劫匪来抢银行,保安搞不定,就交给110,110搞不定就交给武警。有什么事件发生时的响应的Escalation Path,办公中的逐级审批。这个模式用一个函数指针数组或是栈结构就能够实现了。这个思想很像编程中的异常处理机制,一层一层地往上传递异常直到异常被捕捉。在Unix下,一个最简单的例子就是用 && 或 || 来把命令拼起来,如:cmd1 && cmd2  或 cmd3 || cmd4 , 若是cmd1失败了,cmd2就不会执行,若是cmd3失败了,cmd4才会执行。如: cd lib && rm -rf .o 或 ping -c1 coolshell.cn && ssh haoel@coolshell.cn

Command模式,这恐怕是软件里最多的模式了,好比:编译器里的Undo/Redo,宏录制。还有数据库的事务处理,线程池,设置向导,包括程序并行执行的指令集等等。这个模式主要是把一个对象的行为封装成一个一个的有相同接口的command,而后交给一个统一的命令执行器执行或管理这些命令。这个模式和咱们的Unix/Linux机器启动时在/etc/init.d下的那些S和K开头的脚本很像,把各类daemon的启动和退出行为封装成一个脚本其支持reload/start/stop/status这样的命令,而后把他们按必定的规范作符号连接到/etc/init.d目录下,这样操做系统就会接管这些daemon的启动和退出

Observer模式,观察者模式,这个模式也叫pub-sub模式,很像咱们用手机订阅手机报,微博的follow的信息流也是这样的一个模式。MVC中的C会sub V中的事件,用非OO的方式其实也是一个回调函数的事。在不少异步系统中,你须要知道最终的调用有没有成功,好比说调用支付宝的支付接口,你须要向支付宝注册一个回调的接口,以便支付宝回调你。Linux下的一些系统调用如epoll/aio/inotify/signal都是这种思路

Strategy 模式,策略模式,这个模式和Bridge模式很像,只不过Bridge是结构模式,其主要是用于对象的构造;而Strategy是行为模式,主要是用于对象的行为。策略模式很像浏览器里的各类插件,只要你装了某个插件,你就有某个功能。你能够安装多个插件来让你的浏览器有更多的功能(书本上的这个模式是你只能选用一个算法,固然,咱们不用那么教条)。就像《你可能不知道的Shell》中的那个设置设置$EDITOR变量后能够按ctrl+x e启动编译器,或是用set -o vi或set -o emacs 来让本身的shell像vi或 emacs 同样,或是像find -exec或xargs同样的拼装命令

Bridge 和 Strategy是OO设计模式里的“Favor Composition Over Inheritance” 的典范,其实现了接口与实现分离的。Unix中的Shell就是一种,你可随意地更换不一样的Shell。还有Emacs中的LISP驱动C,C实现了引擎,交给LISP实现逻辑。把程序分为前端和后端,经过socket专用应用协议进行通信,前端实现策略,后端实现机制。再看看makefile把编译器和源代码的解耦,命令行输出这个接口能够把一个复杂的功能解耦并抽像成各类各样小而美的小功能命令,等等这样的例子,你会发现,还有大量的编程框架都会多少采用这样的思想,可让你的软件像更换汽车零件同样方便。我在用Unix的设计思想来应对变动的需求中说过灯具厂,灯泡厂,和开关厂的例子。

后记

由于写做仓促,上面的那些东西,可能会你让你以为有些牵强,那么抱歉了,你能够帮我看看在生活中和 Unix里有没有更帅的例子。

不过,咱们会发现上面OO搞出来的那么多模式在Unix下看来好像没有那么复杂,并且Unix下看起来并无那么多模式,并且Unix中的设计模式无非就是这么几个关键词:单一,简洁,模块,拼装。咱们再来看看OO设计的两大准则:1)钟情于组合而不是继承,2)依赖于接口而不是实现。还有S.O.L.I.D原则也同样(若是你仔细观察,你会发现SOLID原则在Unix下也是完美地体现)。你看,Unix和OO设计模式是否是完美的统一吗?

我有种强烈的感受——Unix对这些所谓的OO的设计模式实现得更好。由于Unix就一条设计模式!再次推荐《The Art of Unix Programming

Unix Kiss

餐后甜点

我上面提到了《The Art of Unix Programming》,因此我有必要再谈谈这本书中我中毒最深的一章《模块性:保持清晰和简洁》中所谈到的胶合层。

胶合层这一节中说了,咱们开发软件通常要么Top-Down,要么Bottom-Up,这两种方法都有好有很差。顶层通常是应用逻辑层,底层通常是原语层(我理解为技术沉淀层,或是技术基础层)。自顶向下的开发,你可能会由于开发到底层后发现底层可沉淀的东西愈来愈不爽(由于被可能被不少业务逻辑所侵入),若是自底向上的开发,你可能越到上层你愈加现不少你下面干的基础上工做有不少用不上(好比干多了)。因此,最好的方式是同时进行,一会顶层,一会底层,来来回回的开发——说白了就是在开发中不断的重构,边开发边理解边沉淀。

不管怎么样,你会发现须要一层胶合层来胶合业务逻辑层和底层原语层(软件开发中的业务层和技术层的胶合),Unix的设计哲学认为,这层胶合层应该尽可能地薄,胶合层越多,咱们就只能在其中苦苦挣扎。

其实,胶合层原则就是分离原则上更为上层地体现,策略(业务逻辑)和机制(基础技术或原语)的清楚的分离。你能够看到,OO和Unix都是在作这样的分离。可是须要注意到的时,OO用抽象接口来作这个分离——不少OO的模式中,抽象层太多了,致使胶合层太过于复杂了,也就是说,OO鼓励了——“厚重地胶合和复杂层次”,反而增长了程序的复杂度(这种状况在恶化中)。而Unix采用的是薄的胶合层,薄地至关的优雅。(经过这段话的描述,我相信你会明白了《如此理解面向对象编程》中的个例子——为何用OO来实现会比用非OO来实现更为地恶心——那就是由于OO胶合层太复杂了)

OO的最大的问题就——接口复杂度过高,胶合层太多!(注:Unix编程艺术这本书里说了软件有三个复杂度:代码量、接口、实现,这三个东西构成了咱们的软件复杂度)

相关文章
相关标签/搜索