用Unix的设计思想来应对多变的需求
之前,@风枫峰 在“这是谁的错?”中说过开发团队对需求来者不拒,而@weidagang 也在“需求变更和IoC”中说过用IoC来最大程度地解决需求变更。今天我也想从Unix设计思想的角度来说说什么是好的软件设计,什么样的设计可以把需求变更对开发的影响降低。(注意:这并不能解决用户或是PM的无理需求,面对无理需求,需要仔细分析需求,而用技术的手段无法搞定这个事,但是可以减轻需求变更带来的痛苦) 我曾经在《Unix传奇》的下篇中写过一些Unix的设计哲学和思想(这里重点推荐大家看一下《The Art of Unix Programming》,我推荐过多次了),以前也发过一篇《一些软件设计的原则》,不过,这些东西都太多了,记不住。其实,这么多年来,我的经验告诉我,无论是Unix设计,还是面向对象设计,还是别的什么如SOA,ECB,消息,事件,MVC,网络七层模型,数据库设计,等等,他们都在干三件事——解耦,解耦,还是解耦!所谓解耦,就是让软件的模块和模块间尽量少地依赖起来。
现实当中的例子
让我先举几个现实生活中的例子:
1、现实社会中,制造灯具的工厂完全不关心制造灯泡的工厂,制造灯泡的工厂完全不关心制造灯具的工厂,但是,灯泡和灯饰可以很完美地组合成用户所喜欢的样子(这和@weidagang 在“需求变更和IoC”说到的那个PC的例子相仿)。他们是怎么做到的?
2、互联网上,做网站的人完全不用关心用户在用什么样的操作系统,什么样的客户端浏览器(当然事实上,浏览器的不标准让网站那边很头痛,这里只是举个例),反过来,上网的人也不关心做网站的人在用什么的技术开发网站。但是大家在完全不关心对方的情况下,可以很正常地协同工作在一起。为什么?
这样的例子太多了。为什么可以做成这样呢?因为大家依赖的是一个接口,灯具和灯泡并不互相依赖,他们依赖的是一个接口,做网站的人和浏览网站的人依赖的还是接口——HTTP协议。这就是面向对象的核心思想——依赖于接口而不是实现,这就是解耦。当你看过这两个例子以后,我希望你以后设计的软件至少不能比我们现实社会中的这些方法要差。不然,你就是在让社会倒退了,呵呵。 你会说,这和Unix,和应对需求变化有什么关系?好让我们再来看一下Unix的设计。
Unix设计的例子
下面是几个Unix下的例子:
1、Unix下,所有的硬件都可以通过文件的方式存取。其统统在/dev下。于是,软件和硬件的耦合被解开了,操作系统只需要把硬件统统变成文件,而程序只需要使用三个东西,一个是fd,一个是read(),一个是write(),就可以来操作任意的硬件了,这就是抽象,简单到不行。
2、Unix下,所有的命令都可以用管道串起来(管道绝对是个伟大的发明),这样,所有的命令间的交互全部解耦到只依赖于STD_IN, STD_OUT设备上。最酷的是,用户可以使用管道任意地拼装那些命令,以完成各式各样的功能。管道这个设计思想可以映射为今天的Web Service,你可以任意地拼装各种Web Service。
看到这里,你会发现,这还是解耦,本质上来说,也是一种依赖倒置——OOD的精髓。但是,Unix还不仅仅是这些。我们再来看几个例子:
1、Unix下,软件都是绿色地安装。在iOS上更明显——各个程序间基本上互不干扰,这个程序产生的垃圾文件不会影响到另一个程序。你删掉一个程序不会让另一个程序不举,各是各的空间。你可以删除这些程序,只要把内核心留着,系统照样可以启动。
2、Unix下,你可以通过设置一些环境变量,让多种环境同时存在,比如:某个LAMP用的是Apache 2.0, Mysql 4.0, PHP 4.0,某个LAMP用的是Apache 2.2, Mysql 5.0,PHP5.3,你不但可以方便地在系统中切换这两个环境,你甚至还可以同时启动他们。
3、Unix下,你可以随意地替换你想要的程序。比如,你不喜欢bash,你可以替换成ksh/csh等,你不喜欢awk ,你可以替换成 gawk ,所有的东西都像零件一样,你不喜欢什么,你就可以替换什么。
这三个例子告诉了我们——当你把你的软件设计地耦合度非常地低时,你可以随意地组合,随意地安排你的系统。相当的灵活,灵活到Windows到今天都学不会。
应对需求变化
看到这里,你可能明白我想说的是什么了,你可能开始觉得怎么样的系统设计会更有效了。如果你还记得《Steve Y 对平台的长篇大论》,你就会知道我想说什么了。是的,我想说的就是,当你真正了解了Unix的设计思想后,你会觉得今天的很多东西都是对Unix设计思想的一种传承或是变种。这种东西就是:
1)解耦,解耦,解耦。尽量地让你的模块不要在实现上耦合,而是耦合某个规范,某个标准。
2)KISS,KISS,KISS。要做到高度解耦,你的模块就一定要很简单,当然不是说简单到只有几行代码,而是简单到只干一件事,并把这件事干到极致。然后通过某个标准拼装起来。
3)拼装,拼装,拼装。我想不起来是谁说的了,这句话是这样的,当我想用一个模块的时候,我直接调用就好了,没有必要像C或Java一样,还要编译。是的,拼装需要一个框架,需要一种标准协议,然后让所有的系统都耦合在这种规范上,各自独立运行,就像一个机器上的各个部件一样,当我觉得这个部件不爽,换了就是了。(例如,当我们在尝试不同的算法的时候)
想想建材和家俱市场,无论用户过来想装修什么,我都可以满足用户的不同需求,只要你是和家装相关,我基本上都能满足你,不是吗?无论你怎么变,只要不变态,我基本上都可以满足你。这就是解耦,拼装带来的好处。 你可能会说我说得太简单了,另一方面,你可能觉得有一些系统这样做没必要,我承认,不过,你可以有选择的或多或少地试试。(其实,我相信你已经在不自觉得或多或少地使用这种方式开发软件了) (全文完)
(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)
《用Unix的设计思想来应对多变的需求》的相关评论
博主您上辈子一定是一个优秀的工匠,要不就是木匠
工匠的概念不包括木匠吗?@Conanca
不管是面向过程,还是面向对象,或者面向方面,面向函数,确确实实,都只是在做一件事:解耦。所以实在没必要争论是面向过程好还是面向对像好,哪个更能让你特定应用场合更容易做到解耦,就用那个。
话说多年前,面向对象横空出世的时候,其宣扬的最大优势,就是可以把现实世界的对象和计算空间的对象的对应起来,所以能够弥合real world和cyber space之间鸿沟。但实际上这么多年下来,那些 xxxFactory,xxxer之类的,到底和现实世界的什么对象是对应的?一个典型的模拟现实世界的办公业务系统,我估计能直接一一对应的对象,也不会超过总对象数的20%。其实,没有所谓对应,有的还是解耦,和面向过程没区别,只不过解耦的手段有时候更方便使用一些。
我们项目以前有个需求在我看来是无理需求,请浩哥回答下,在你看来这些需求是否无理。
1. 有客户说打开windows的任务管理器,然后点开“联网”tab,“本地连接”中的曲线波动偶尔比较大,比如从一个80%突然降到1%,之后又比较平稳。客户认为这是第三方软件引起的,希望我们能把系统的这个曲线波动修改一下,让它一直比较平稳。我认为这是一个无理的需求,也是没有必要的需求,因为这改变不了网络的流量和利用率,只是欺骗一下自己的眼睛而已。况且我认为大部分情况下都是程序的问题,我们应该尽可能地从使用的软件中去找问题,而不是去修改系统工具检测到的值。
2.客户使用第三方软件,说这个软件在他们机器上总是占大约20%的cpu,希望我们能把这个利用率降低一下。这个软件很大而且我们是不能拿到源码的。因为没有去现场,我只能在自己的机器上安装该软件运行,然后attach到windbg调试,找出哪些线程占cpu的时间最多并尽量了解该线程是干了些什么事情。但是在我的机器上,这个程序没有哪个线程明显占用太多cpu,任务管理器上显示的所占cpu利用率大概也就1%。在没有源码所以不可能修改程序的情况下要求降低这个程序的cpu使用率,我也认为是一个无理的需求。
这些东西的确很无理,因为完全不影响正常的功能使用,对性能也没有什么影响。你可以和用户解释一下,为什么会有那个网络峰值,为什么会占CPU20%,程序在干什么?这些事如果不干的话,这个软件的功能就有问题了。如果客户不理解你,不可沟通,那么,你就找客户那边支持你的人,如果找不到,你可以说,我们以后会考虑的,因为这个不影响你现在的正常使用。然后把想办法转移客户的注意力,让这些事不了了之了。
您说的很好,软件开发就是不断的在解耦合,已提高开发的效率。面对复杂的难题,现在科学上的通用解法就是分的方法,不断的将难的 复杂的问题,抽象 分解成小的问题,分而治之,最后通过解决小的问题,归纳 推理,解决大的难题,这样才能认识到事情的本质,解决难题。
说到UNIX与WINODOW,我认为是两种操作系统,同样是都是工程上的杰作,一个是提供服务的OS一个是桌面的,各有所长,风格不同罢了。
unix方式不错,我现在的gui工具也是:先做一个命令行的功能工具,再做个gui界面,它调用命令行来实现各种功能
其实,*nix也应该如此:既然已经有了各种命令行工具了,再做一下gui(甚至是文本ide模式,如mc)界面
这样,应该会大大方便*nix在桌面的普及
*nix的绿色,有一点不同意:安装一个东西,分散在sbin、var、usr….
给初学者的感觉就是晕:它到底安装到哪里去了?!想删、升级,自己怎么能控制??
说到管道,想起一个疑问:app1 | gzip > \tmp\x.gzip
app1在生成20×20的皇后结果(运行12402小时了,还未完)
同时gzip在结果未完的时候就开始工作了(也运行了13小时,得到900M的gzip)
从这个例子看,app1和gzip是并行工作了!
但是并行程度是怎么决定的?gzip自行决定有多少输入才足够进行压缩前的分析?
但是,灯具和灯饰可以很完美地组合成
用记用户?所喜欢的样子应该是灯具和灯泡。呵呵。我一直以为灯具就是灯泡。sorry
@陈皓
好的,谢谢了!
我有几个问题。
首先, web 开发者恐怕还是要考虑用户的浏览器和 os 的,尤其是前者。我不是做 web 开发的,不过有听闻过,某些新特性是浏览器相关的,那些 moz 、 webkit 前缀的东西,都不是跨浏览器的。这个就涉及到两个影响了现实中接口统一的问题,标准的不同版本之间的兼容性以及标准的各个不同实现对标准的支持程度。
然后,我没有用过 unix ,不过,根据我使用 linux 的一些经验,很多软件包管理工具在处理软件依赖的时候,都比较繁琐,没有做到博主所说的“互不干扰”、“绿色安装”。 linux 的共享库管理方式是全局管理的,优势是方便升级,节省磁盘空间,而缺陷就是软件包之间的依赖度高。apt 系列的依赖关系是相当麻烦的,很多没必要的依赖。 yum 的循环依赖也让人头痛。gtk3 带来的软件兼容问题(如输入法、终端等方面),目前还常常出现。
还有最后的,文件抽象的那个。我记得有在书上读到过, windows 下的图形设备,开始就是那么抽象的。win95 时代的辉煌也和那种方便的开发方式有关。但是,代价是那种抽象方式,很难利用起底层设备的一些特性,在游戏等方面,无法满足开发者的需求。所以,微软最后推出了 directx ,这个非常“不标准”,也非常“不抽像”的东西。
只能说,解耦合的想法很好,但是在现实的实现中,总是遇到相关的问题。毕竟系统是个整体,桌面环境本身是个巨大的生态环境,要做到各种开发包之间的独立,真的很困难。
关于Web开发,你说的是对的,需要考虑浏览器,为什么?因为有些浏览器如果IE6没有支持标准。所以,只能让我的网站耦合浏览器的实现了。我们也可以看到耦合带来的各种问题。(我应该说邮件服务器和客户端可能就会好一点)
另,你说的是Linux的开源部分的依赖的确很扯,也很让人头疼。不过,瑕不蔽玉。另外,这也说明了,耦合是多少一些恐怖的事。
独自各干各的事,互不干扰.
当需要一起的时候又能协调的工作,
只用一个接口.简洁,完美
OSGI
:D
这些确实是“需求”,是否合理,还是可以判断的。判断依据就是你们和客户的SOW(Statement Of Work)和SLA(Service Level Agreement)是怎么签的。猜测:你是IT Service部门的?呵呵,估计你碰到的问题已经跑题了。现实,做开发的和做IT service完全是两个不同的Domain啊,需要的Skill是彻底不一样的。。。你碰到的这两个问题,在开发看起来是无理需求,在做IT service的看起来却是“合理要求”,是有解的,不过不适用开发者的方法。。。@云端孤鹜
还记得上大学时第一次看到“KISS”原则时的震撼,尽管自己到现在也未必真正理解它,但它确实是美的代表。
皓哥的“Unix下,软件都是绿色地安装。”这一论断有点脱离实际了。
现在的Linux发行版里,软件与软件的包依赖关系复杂到了需要借助工具才能管理的地步…
其实软件安装绿色与否,在于软件的设计而不在于操作系统,Windows上很多软件一样是绿色的。
以前的一些Windows大型软件由于共享组件需要依赖于注册表等耦合机制,不过现在引入了程序集的概念,有了很大好转。
其实这也符合皓哥此文的中心,解耦。
你说的是linux,不是unix。你看看MacOS你就知道以前的Unix是怎么玩的。另外,千万别提Windows绿色了,也别提Windows解耦了,这个Windows做得太差了。
解释的很好,有种茅塞顿开的感觉。。。
@vx13
我觉得Web开发的那个例子还是合适的。文中也提到了,解耦有个前提:“标准”的接口。
就拿Web来说,用HTTP传输、用HTML描述网页结构、用CSS控制样式……好,不管浏览器开发者还是网站开发者,都按这些个标准来就好了。到了用户那里,不管用什么浏览器看到的都是一样的东西。但是各家浏览器或多或少都有些非标准的东西,就好像moz的专有API。要是网页中用到了那些API,或是用了“在XXX浏览器下实现OOO效果”之类的东西,这就是在具体实现上的耦合了,刚好拿来做反面例子。
文件抽象那个,我觉得你说的东西层次不同,不能拿来相提并论的。操作系统要面对的设备各式各样,对设备的抽象当然要简单,要有广泛的适用性。而directx面对的主要是开发者,就是要抹去繁杂的硬件细节,让图形开发变得容易。
最后聊聊*nix下软件依赖的问题……这东西确实很让人头疼。绿色不绿色先不说,我觉得这个没操作系统什么事,更多的是看卸载程序或是包管理器。倒是在*nix下删个软件还真的很容易让其他软件不举(或多或少的),这个问题主要也是源于Unix那“每个程序只做好一件事,多个程序组合完成更大的任务”的设计思想。本来这个想法挺好,可偏偏能做好那件事的软件太多了……一组程序解决一个问题时,便形成了一个工具链,我替换了其中一个,问题就这么发生了……这是不是也是“在实现上的耦合”呢?
只能说显示很残酷啊。
现在对unix和mac跃跃欲试了
linux是不是比unix差很多啊?
这个倒没有。就算是安装软件有这么多的依赖性,也并不能说明Linux不支持绿绝安装。那只不过是一种安装方式罢了。
@陈皓
Linux被开除出Unix序列了么,只有血统纯正的BSD以及有爹的Solaris,AIX,HP-UX才能算Unix么…
有个typo:依赖于接口而不是实现,这就是触耦
这里应该是“解耦”,而不是“触耦”
确实,Unix文化中有着太多的设计精髓。管道(pipe)使得功能单一的过滤器(filter)能够组合拼接成看似难以完成的任务。或许字节流是Unix中为数不多的耦合之一。
那些依赖挺折腾的,不算符合KISS原则吧
我觉得不算KISS。老实说,这些烂事不是Unix干出来的,是开源社区干出来的,呵呵。
学生,有的地方还不是很懂,大神终于有更新了
解耦、KISS这些概念很通俗浅显,我这个编程新手理解起来没有你那么深了,不过学习了~
看完设计模式以及程序员修炼之道之后,我就不再看其他类似的书了,其实说来说去好的程序就是“低耦合,高内聚”…说起来容易,但真的要做到就是一个经验的问题了…
@Conanca
请问与木匠有什么关系?
那个灯泡和用户浏览网页的例子举的非常恰当 :-)
个人认为实践经验很重要,其实设计模式一书一开始就提出了两个原则:
1. 针对接口编程,而不是针对实现编程
2. 优先使用对象组合,而不是类继承
就看是否能真正理解并运用了。
@YY
不是被开除了,是从一开始就没有继承UNIX的内核,是单独开发的,LINUX是在MINIX的基础上重新开发的,而从UNIX系统的演进看的话,MINIX、LINUX和现在的SOLARIS,HP UNIX,MAC OS等分支,是完全独立了。1992年LINUX和GNU软件结合产生产生完全自由的操作系统。GNU就是GNU’s Not Unix的缩写。呵呵。当然GNU开发的很多优秀软件后来都被移植到了其他系统中。
@云端孤鹜
算站着说话不腰疼吧:
第一个,可以尝试降低任务管理器中显示刷新频率,这个是可以设置的,如果设了还不行。。。那就不行吧;正确理解业主的要求,业主口头甚至在合同或者文件中所要求的并不一定真的都是业主的实际需求,要让业主真正明白这个系统到底要做什么业务,如何做才能满足业务要求。让曲线平缓,充其量是美观上的需求,且优先级很低。
第二个,去现场看看吧,windows的环境比类unix系统的要复杂的多的多,很可能有其他问题。
深有同感
这算什么烂事? 如果有100个软件依赖python包, 难道它们都要自带一个python, 这样当然绿色了, 但绿得有点愚蠢. 包管理器正是为了解决这样的依赖而产生的.
win95时代的抽象是得其形而未得其神。
要知道,linux的文件抽象是可以轻松支持0拷贝技术的(如果你用linux搞过防火墙、代理、下载服务器之类东东,就一定会知道这个);0拷贝实质上也就是直接内存/硬件访问。
而win95呢,它只顾得“封”和”装”了——也就是搞个什么什么模式+委托调用那种——却不知道如何封装才能既不损害性能、又可获得安全方面的好处(这也是微软的“封”“装”的一贯作风,你总是经常不得不去找找它的“未公开调用”,否则一些功能可能就无法实现)。
于是,程序员想在屏幕上显示点什么,都必须委托系统来来回回的拷贝,速度自然就惨不忍睹了。
所以,它才不得不又在“封”“装”上掏个窟窿,让程序员透过这个窟窿直接写内存/硬件。这就是dx。
@haitao
你不需要管理软件到底放到那个目录 。
如果一定要干净,在自己的账户下安装~username/。
另外用debian等包管理器可以负责软件的安装位置。
另外关于pipe,操作习惯会很聪明的处理pipe前后的进程,google sigpipe,看看相关的资料就行。
@陈皓
这算烂事?大量软件都依赖相同的库,难道每个软件都带一次那就是好事?
呵呵,谢谢pipi,你说的我其实都知道,我一直把Unix-like的系统都算作Unix…当然,的确,Linux这个缩写本身就是Linux Is Not UniX的意思。
模块,模块,模块,搭积木,搭积木,搭积木,思想都相通
当解决非核心复杂度的问题时,总是能够找到方式,解耦,保持简单,各种思想.
但是当面对的问题是隶属核心复杂度的范围,问题就恰恰反了过来. 为什么需要解耦,很多时候是用来对付面对已知的变化.先有变化,才有解耦,不然解耦何用? 需求的变化导致没有银弹, 保持简单的思想当然很好,但是前提是 “飞机能够飞起来” .再怎么解耦也不大可能把 客机 的座位 安装到 战斗机上去吧?
@YY linux从最开始就跟Unix没啥关系,所有代码都是新写的。
@stevegy
我是做开发的啊,而且那些软件不是我们编写的,没有源码。
一句话,没有标准是不行的。
没有标准绝对是行不同的。http://www.mayipe.com
浩歌,一定是个大牛。。。
抽象问题,细化分层,建立标准,这是在所有领域都通用的思想
linux的设计目标是用在服务器上,以命令行为主,用户是专业人员。windows的设计目标是日常使用,目标是普通用户。linux的使用者会精心配置环境,甚至自己编译linux,而程序要干的活就相对简单,不需要考虑太多的兼容问题,因为使用者会自己搞定。而windows则需要考虑环境是否符合等情况,甚至有些程序会根据环境来决定程序的表现,比如win7会根据硬件配置决定是否启用aero。
“千万别提windows的绿色了”。windows下的绿色软件怎么了,说清楚吧,别简单一句别提了。
windows的绿色软件一般是一个文件夹,所有程序数据都在这里,删掉这个文件夹即可卸载。而非绿色软件,也无非是装个服务,写个注册表,user目录产生一些文件,这样的软件都会提供卸载程序,可以删得干干净净(如果不是流氓软件的话)。而且很少会见到,卸载一个软件导致其他软件不能用。
linux的软件,会散在usr、var等各目录下,不使用工具就不知道有哪些文件在什么地方,一样要用工具来卸载,和windows的非绿色软件有什么不同?而卸载很可能导致其他软件不能正常使用(不知apt是否能一定程度上解决)。
这正是”每个程序只做好一件事”这个设计导致的。所以说每个设计即有优点,也有缺点,关键是要用在合适的情况下。”每个程序只做好一件事”,就适合用户是专业人员这个情况,所以linux使用这个设计。而windows就不使用这个设计。
ios也显然不符合”每个程序只做好一件事”。例如,装了一个stanza,一个dropbox,可以把stanza里的书,都同步到dropbox上吗,不行,因为dropbox根本访问不了stanza的数据。为什么苹果这么设计呢,因为iphone面向的是普通用户(当然只是原因之一)。
就像marmot所说的,”没有银弹”。iphone4的配置其实一般,很多高配置的android手机,用起来却不如iphone4流畅。因为iphone的软件和硬件是作为一个整体设计的,也就是耦合。
所以,适合需求的设计才是好设计。
【见过无耻的,没见过这么无耻的!】只要没看到妆点网的公开道歉,接下来的一个月里,我每天只要有时间就不择手段四处评论、四处转发!如有打扰,请直接无视或删除!http://marketoversea.com/news-story/elady-zdface.html
Unix就没有依赖了吗?Unix虽然增加了灵活性,但也使自身碎片化,不也是很让人头疼吗?
其实软件安装绿色与否,在于软件的设计而不在于操作系统,Windows上很多软件一样是绿色的。
以前的一些Windows大型软件由于共享组件需要依赖于注册表等耦合机制,不过现在引入了程序集的概念,有了很大好转。
Unix各种软件包之间的依赖关系比windows复杂太多了
这点既是其优势,也是其使用不方便的地方