架构这个问题,在还没有整体接触系统前,未曾考虑;在知识面还不够广时,未曾考虑;在没有见过或者听过更多产品形态时,未曾考虑;在理论重于实际前,未曾考虑;在具体大于抽象前,未曾考虑;在细节重于模块前,未曾考虑;在知其然未知其所以然之前,未曾考虑;在重部分而轻整体前,未曾考虑;
未毕业之前,重在学习,因为知识面还不够。此时,很多东西还是模棱两可,不足以探讨架构问题。在此阶段,不断的学习理论知识,很多新鲜的术语不断灌入耳中,也导致很多东西一知半解,或者知其然而不知其所以然。也有很多时候,突然觉得对某个东西,貌似已经完全理解了,但是现在来看,也是很可笑的,那其实是在进行自我的小突破,只是这个突破还很小。到离开学校大门后,回过头来,感觉学得东西好像都理解了,但是具体再想想,又想不明白。可笑当时不够努力,夜郎自大,也可怜当时还妄想着做出超级牛逼的东东,来实现自我。
工作之后,真正步入嵌入式领域,从基础开始。此时,只涉模块,不涉整体,自然,整体的东西也想不明白。但更多的时候,还是专注于自己的小模块世界,总觉得自己的小模块肯定比别人的模块要复杂,比别人的要强,殊不知,大家还是在同一个起跑线。此阶段,也携带了求学时的不良习惯,总觉得理论理解很多,总是用理论来指导实际,结果,在不断的失败中,才逐渐慢慢理解理论的说辞。而此阶段,也有一些弊病,每取得一个小成功,小成果,总觉得自己前进了一大步,跟人家阿姆斯特朗正好相反。
现在总结出来,自己到底厉害不厉害,要看自己能否将存在于实际中的很多系统想的足够明白。这算是自我检测的手法。
工作一段时间以后,也逐渐的对所做的东西有了更进一步的深入的认识,也逐渐的看到了自己的不足。原来还有很多东西自己都不懂,跟那些侃侃而谈的人,差得太远。遂奋起直追。结果,越是追,发现越是远,才感知到自己的渺小。此时再回过头,才发现刚出校门时的回头,根本不能算回头,因为后面没有路,自己是飘着的。那时的理解,现在来看实在太小太弱,就如同一个拿着汽车玩具的人到汽车设计者那里,告诉他自己如何如何设计了一个汽车出来,考虑了如何如何多的实际问题。而设计者则会觉得这些问题太可笑。思想的容纳之大,就在这里。
再之后,开始接触更多的东西,面更广,也接触了更多的软件,设计,产品,甚至自己也设计了一个架构。这过程中,每一个,每一步,都是重循环的过程,从开始的自大,以为懂了一切,到最后的渺小,懂的太少。如此的反反复复,只不过是爬到山峰的必经过程。
终于,有一天,不再纠结于模块,纠结于指针,纠结于协议,纠结于宏定义,纠结于一个命令,纠结于一个产品,纠结于一个结构,纠结于学校的那些理论,而开始考虑架构。
但实际考虑这个问题时,很多时候很难摆脱实际具体的影响,难以做到高层抽象。其实,说到底,还是见得太少,做的太少,感悟的太少。试想一下,一个未曾离开大山的人,如何去抽象外部的世界,一个未曾见过汽车、火车的人,如何很好的抽象交通工具。见得太少,理解的太少,抽象出来的就过于简单,想象不到大的情况,复杂的情况,自然也难以应对自然定律。结果就是,架构的产品无法面对实际的使用环境。但是,这必将是一个过程,一个不断试错并改正的过程,没有人一开始就能够设计一个火车,一个大厦,虽然所有人几乎都可以一开始就设计一个手拉车,一个猪圈。
通过不断的思考,有了一些感悟,这里将这些感悟记录下来,期望有新的理解来打破或者解决这些问题。
当别人告诉我架构是决策时,我很吃惊。架构不应该是如何搭积木吗,造出一个一个的模块,将他们组装起来,就是一个架构,但是,是问题本身解决了这个问题。这些问题包括:
是进程还是线程。我们的产品,软件,程序,到底是要多进程架构呢还是多线程架构。这里,线程是运行在进程空间中的。这是一个决策问题。至于什么是进程,什么是线程,在这里不是问题。另外,还要避开一个认识误区,多任务并非一定提升性能。采用多线程可能是为了利用操作系统的任务调度,避免IO或者慢速设备对性能的阻塞。而有些设计,则纯粹是为了利用CPU的调度,方便逻辑的实现。当然,完全也可以在一个while循环中处理多种事务,只要自己能协调好调度问题。像libuv就是这种设计思路。纯数据的计算,放在一个任务中和两个任务中,并不能在同一环境中得到提升,相反还会降低。
是逻辑还是流程。我们的产品,软件,程序,在设计时,是要按照逻辑来设计还是流程来设计。所谓流程,就是先做什么,后做什么,一条线的各个点,该布置什么。所谓逻辑,就是这一块是什么功能,提供什么特性,那一块是什么功能,提供什么特性,不同的块是上下依赖关系,还是左右协同关系。逻辑更像是告诉我们,软件要有那几部分。
是C还是C++。我们的产品,软件,程序,是要用C语言来设计还是C++来设计。C语言可以利用指针,模拟实现类似C++的封装集成多态。相比较而言,使用C++,便于做复杂设计,但是流程不好跟,同时对语言要求高也是需要考虑的因素。总体感觉,C++偏向设计,偏向逻辑,C偏向流程,偏向数据处理。
是接口还是业务。我们的产品,软件,程序,是按照接口来设计还是业务来设计,接口更面向于开发人员,业务更面向于使用人员。
其实上述很多问题,很多时候,不是单一存在的。考虑接口实现时,是基于业务逻辑还是重工作流程?如果重流程,则接口都存在于任务之中,如果重业务,则接口之中可能包含任务。也许很多开发人员从来都不考虑这个问题。
C的开发可能更多面向纯接口的设计,将接口的调用包裹于业务及业务流程中。而C++的开发,则可能更多的面向业务设计,在业务接口中,隐藏了处理流程。
除了程序本身的考虑之外,产品形态也是一个问题。不同的产品模式,对架构内容的考虑,也不一样。比如:
嵌入式还是PC还是服务器。嵌入式更多处理底层,C更多,流程更多,数据更多;PC,可能涉及GUI,进而涉及MVC模式,数据库,WEB等等,C++与Java更多;服务器,数据更多,模块更多,类似WEB框架,什么数据库,计费,管理,多并发,负载均衡,集群分布式等,虽然有些与业务功能不直接相关,但这些同样是架构的组成部分。
再细分,WEB是一种架构,非WEB方式又可能是一种架构。非WEB方式下,GUI是一种架构,非GUI又可能是一种架构。除此,底层是一种架构,上层又是一种架构。偏向数据处理是一种架构,偏向用户交互又可能是一种架构。
进一步,除了产品形态,程序本身外,软件自身设计考虑,也会有一些问题出现。比如,接口在上下层都可以实现的情况下,是上移还是下放,依据什么原则?
如此苦苦思索,在寻找一种普世法则,想放之四海而皆准。但是,就结果来看,似乎并不存在一种普遍适用的框架,来将所有的问题都套入进去。更多的,倒像是一种思想,一种指导原则,比如:
具体问题具体分析,因地制宜,合适的,才是最好的。
复杂问题分而治之,模块化,加层加接口,屏蔽。
误过度设计。
利用隐喻,简化问题的理解,类比现实世界。
除了这些原则之外,似乎问题本身确实解决了架构的另一问题,那就是开始提出的,架构是一种决策。就像上面的那些问题,当站到更高的角度时,决策显得更加明显。而每一种决策下的东西,都是架构过程中的思考和选择。细节的高度也在拔高。原先苦苦理解的模块,现在变成了细节,变成了自然而然的东西,就好比现在成熟的技术,在过去来看,复杂至极,但是现代人则会觉得这还需要再仔细考虑?这是知识增加的结果。当知道的越来越多,面对设计架构时,决策越来越明显,因为经验告诉你,很多原来需要考虑的东西,现在只不过是细节,你对它们的习性已经了解了。
除了知识增加的结果外,还有一点,那就是站在巨人的肩膀上,你将看的更远。架构也不例外。学习已有软件的架构,将增加知识和经验,而且相对更为捷径,更为快捷。
最终你会发现,苦苦思索之后,就会看到新的天地,得到思考的美的享受,认识世界的美的享受。这时候再来感悟经典,才会觉得经典之所以为经典就是因为它是经典。