一、架构设计
架构设计,包括下面的设计思想,其实对很多人来说,是一种很虚妄的东西。因为他看不到,摸不到。顶多看到代码里有一个设计模式,就会想,看,这就是设计的威力。你说他理解的错吗?那倒不一定。架构设计其实可以拿盖房子来举例。我们很多人,跟着别人盖了好多年房子,然后回家自己搞了个包工队,有样学样,甚至可以生出些新花样的盖房子,但对他们来说,其实就是一个重复经验的过程。所谓的新花样,只不过是在不改变主体而增加的一些新功能而已。对应到编程上来,就是原来用得c++98,后来用c++11,但活儿该怎么干,还是怎么干。
这就是绝大多数程序员,甚至相当多的架构师目前的水平。这些人的特点其实就一个:没有独立的设计思想和架构理念,只是跟在别人的屁股后面跑。不想创新,更不敢创新。很多人学会了“拿来主义”的形,而没有学到“拿来主义”的神。
架构设计的目的是什么?还是以盖房子为例,房屋设计的目的就是在实际的资源限制下,以最小的代价,实现最好的居住效果。这就决定了架构设计一定不是千篇一律的,但实际为什么市场上的屋子都差不多呢?这就需要脑子想想了。映射到计算机的世界,是不是类似。许多公司的代码其实就是堆砌的功能,鲜有架构设计。说句难听点的,甚至UI都可以看到别人味道,原因和现实世界一样,他们都不需要架构设计师,都不想浪费那些时间和金钱。
再多说一句,设计的再好,那设计完成了,刀也就砍到你头上了。这就是网上总提到的防御性“屎山”编程。人是活在现实的世界里,不是纯技术的世界里,所以,还是根据自己的实际场景来决定自己想怎么干吧。
二、设计思想
进行架构设计,先要有规则。规则怎么来?就是从实践中来的。比如盖房子中,想省钱,把墙盖的超薄,可发现冬天不保暖,夏天不保凉,强盗来了一脚踹开。所以这就得实验几把,到底多厚的墙,可以保多少的温。如果墙体的材料更换后,又会是什么情况。林林总总,诸般如此,从基础开始,这就是规则的产生的过程。但是,如何把这些规则有机的结合起来,设计一个既达到客户的目的,又综合成本最低,又有与别人明显不同的建筑,这就是设计师的责任了,而设计师为什么这么做的想法,就是设计思想。也就是说,从架构到设计思想,是一个质的飞跃。
思想是灵活的,是自由的世界,但是他又必须落在架构上,架构是有规则的,是一个必然的世界。当二者成功结合时,大则产生的就是世界上著名的建筑,小则就是各地的有名的建筑。
当然,不能奢求每个开发者都成为设计师,那个不现实也不客观。毕竟很多人只是为了生活踏进了这个行业,但退而求其次,知道一些思想架构的知识,也是好事情。至少拿来别人的东西时,不要生吞活剥,鲜血淋漓,搞得象原始人一样。
三、理论和实践
古人说的好:高山仰止,景行行止,虽不能至,然心向往之。设计思想太远,今天就从设计原则切入,简单的聊几句。设计原则有六种的,七种的,八种的等等说法。不争论这个,先说说常见的几大设计原则:单一职责原则、开闭原则、里氏替换原则、迪米特法则、接口隔离原则和依赖倒置原则。其实这些原则讲起来都特别简单,可能每个开发者也一听就明白,但在实践中却全然不是那么一回事。原因其实就在于没有自己的设计思想指导,只是跟着业务功能在跑,所以往往一开始想得还好好的程序,最后就成为了屎山。当然,可能很多开发者会驳,就你是大聪明,没站在实际情况下就乱下定语。前面已经说过了,实际情况是指导一切设计思想的前提,但不能以这个借口写成屎山(防御性编程除外)。
扯回来,这几个设计原则中,最应该坚持的,就是第一条,单一职责。那单一职责如何体现出来呢?这就是常说的小函数,小类。也就是常说的一个函数通常控制在四十行左右,原则上不超过八十行。一个类尽量控制在五百行内,不要突破千行。其它功能模块可以根据实际情况适当划分。代码量限定的情况下,想在一个函数或者一个类里干更多的事儿,也难。但实际这条也最容易被放弃。原因非常简单,如果增加一个判断功能,大多数开发者,愿意使用if else而不是再写一个函数。那么再一个呢?类似的功能多几个,函数的大小就轻松破百行了,类更是如此。解决这类问题的方法很多,函数的话可以使用函数指针,通过预定义分发的路径,多写几个函数,那么处理功能的函数,只需要一个判断有无,然后执行函数即可。如果是类,可以抽象为功能单一的各种类,然后使用继承,也可以使用组合,也可以使用依赖注入,也可以通过消息、事件来达到写多个类然后互相通信来解决。如果语言支持反射,而且对性能不严苛的要求下,反射也是一种非常好的解决方法。也就是说,单一职责会强迫你学会抽象,学会使用各种手段和技术整合不同的小类(或小函数)来实现整体功能。
其次应该坚持的,就是开闭原则。这个原则其实非常简单,就是可以增加,但不可以修改。这种例子最容易理解的就是对外暴露一个接口类(抽象类),你只可以继承实现或者增加,而没办法修改接口类的内容。开闭原则其实更注重的是对内,因为一般情况下对外已经被接口隔离了,一般涉及不到内部扩展和修改的问题。这其实回到单一职责问题,如果坚持了单一职责,那么这个原则就会少很多问题。开闭原则的重要性在于兼容和稳定即依赖于抽象,举一个和协议有关的软件实例,比如公司卖出去了一万台设备,一年后,国家或者第三方或者本公司的通信协议有了升级。是改原来的通信协议呢还是增加一个通信协议呢?肯定是后者啊,你改了前面的协议,那么卖出去的一万台设备怎么办?售后成本有多高。所以说如果不是国家强制标准升级,一般不能动前面的协议,而动态根据设备的类型来选择使用的协议。而一般来说,协议的实现就是N个类组成的。开闭原则其实是一种实事求是的妥协的原则或说包容的原则。在某些情况下,既要还要,当然能够又要就更好了。
说完这两个,其实对于很多程序员来说,这个原则理论上就不需要再谈了。比如里氏替换原则,很多人连继承都不用,何谈此原则。还有很多人确实用继承,但根本没有替换的可能,又何谈此原则。
迪米特法则亦是如此,很多开发者设计的类或者模块基本就只用一两回,或者干脆见谁跟谁聊。而且后期维护者才不会管你那个,达到目的就好。生拉硬扯,拉郎配的多了去了。
接口隔离原则现在有两种主要情况,一种是被强制隔离,比如使用一些框架,没办法,只能使用别人的接口(这种情况在网络服务开发中最多,大公司也最多),另外一种就是代码量太少(中小公司基本都是这种现象,一般代码量最多一两万行),基本涉及不到分层和大的模块划分,那么接口隔离原则就谈不上了,或者说意义不大。当然也有其它情况,所以这个在公司中实际意义也不算多大或者说不好做到。
依赖倒置,这个就其实已经有些抽象的意思了,属于一种行动上的自觉。公司那么多人,每个开发者都这样,不可能。
所以,其实一个开发者想朝着架构设计方向前进,优先做好的就前面两个,单一职责和开闭原则,如果始终尽量坚持这两个原则,慢慢会发现,其它的原则就会不知不觉里明白并开始应用。所以坚持是一种美德。
设计原则每个原则都是非常重要的,但如何实践它,需要根据自身的情况切入。政治就是妥协,开发何尝不是呢。
四、总结
架构设计和思想这些东西,是非常不愿意谈的。一个是网上和实际的书本上资料太多;而另外一个就是,这些东西是需要大量的编程实践来支撑的(这也意味着不同的环境发展可能产生不同思想体系)。换句话说,对一个没有任何编程经验或者说编程经验少得可怜的人来说,谈这些根本没有任何意义。那么有人说,我好好的编程,搞这个十几年,不就有了架构和设计的思想体系了么?大错。编程经验是架构设计的一个必要条件而已。
大家都明白道理,但是一真正涉及到自己,就拎不清楚了。如何从实践抽象到理论,也就是如何从编程的经验抽象到架构设计的思想理念,这个东西有一些佛家讲的顿悟的意思。单纯教是教不会的,需要自己不断的有意识的在实践中反复的总结抽象,由小抽象到大抽象,到最后顿悟。每个人的实际的经历必然不同,但结果都是达到了一个设计思想的产生。
再换句话说,刻意的僵硬的去抽象而不动脑筋总结反馈,就很难到顿悟的境界。这里说的顿悟,不是说编程水平一下子提高多少,而是指明白了从思想上去指导编程,而不是为了编程而编程。
说这些是不是有些玄学?其实这就是人们总提到的“师傅领进门,修行在个人”的意思,也是“积小胜为大胜”的意思。道理很简单,但何找一条适合自己的路达到目的,就看机缘了。正常的社会财富的分配,理论上也是如此。
可以这样理解,这篇文章是篇务虚的文章。一家之言,仅供参考!