软件架构之“道”和“术”哲学思考

有道无术,术尚可求,有术无道,止于术
道为事物规律,术为规律之用。有道者术能长久,无道者术必落空,学术先需明道,方能大成,学术若不明道,终是小器。

Hello~,断更了有一段时间了,笔者又回来啦。最近发生的两件“大事”让我们这些群众每天都有“瓜”吃hhh,剧情发展好像还挺魔幻,网友云:“最近我们都在为两件事操心,一件是中国最有钱,一件是美国最有权,话说你的KPI完成了吗?”其实在两位70多岁老人竞赛前笔者是押红方川宝赢的,经过几天数据惊心动魄地刷新,唉,最终还是凉凉了。另一件蚂蚁延迟上市之事,作为股民的大伙肯定有很多想法,反正笔者的红包是没了,唉,在风口浪尖上财富自由这个词总有那么几层意味。言归正传,笔者今天想聊一聊我对软件架构的哲学思考,这是一个超大的命题,即使某些前辈在软件领域摸爬滚打多年,也不敢说已经窥其一二,今日胡说一通,希望得到大家的指正,这也算是最近和某位某级从业软件多年的大佬聊地总结的一些心得。

emm,下面确实有 Bug ……
在这里插入图片描述
在软件和编程领域有很多书籍会冠之以“某某之道”、“某某之美 ”或是“某某之禅”,软件领域也有自己的一套哲学思想。
“道”即道路或者方向,是自然规律,是原则和目的,道路和方向是错的,无论你怎么努力都很难成功;
“术”是方法,手段和技巧,好的方法和工具可以让你在成功的路上事半功倍,前提是你走在正确的道路上。
“道”是价值观,是主观的东西,也就是你认为什么是正确的事。不同的人对什么是正确的事有着不同的认知。
商鞅的霸道带领秦国统一六国,但是注定不能长久,秦国也未能如秦始皇的愿,千秋万代。
“术”是方法论,相对比较客观,一个好的方法,基本上是可以推广和重用。

软件架构之“道”

笔者个人认为,软件架构之道最核心的问题是解决复杂性的问题。如果说“道”是方向,那么软件架构之路应该带领码农走向简单。
这里的简单应该包含:
软件分层应该简单,不应该引入不必要的层级;
软件的模块应该简单,不应该引入不必要的功能;
软件模块之间的关系应该简单,不应该引入不必要的交互和依赖;
软件代码应该简单,应该容易理解和阅读;
软件构建应该简单,应该容易搭建构建环境;
软件测试应该简单,应该容易找出软件中的错误;
软件调试应该简单,出错时应该容易定位错误源;
软件运维应该简单,应该容易监控和管理;
最重要的,软件的功能应该简单,应该容易让用户找到自己想要的功能,并且轻松的使用该功能,达成用户想要的目标。
那么我们想一想,软件发展到今天,我们上面说的这些有变简单了嘛?似乎没有,那么是不是软件架构在背道而驰,变得越来越复杂了呢?笔者不这么认为。为了理解这个问题,我们来看看软件中的复杂性的元凶,是什么带来了软件的复杂性。
首先,软件的复杂性来自于功能。我们之前提到,软件架构的中心问题是满足功能要求。随着人们希望软件能够提供越来越多的功能,软件架构的设计必然会随之变得复杂。而这个复杂度的增加和功能之间的关系并不是线性的,而是几何级数甚至更高,因为软件开发是个动态的过程,新加入的功能必然会和已有的功能产生互动,有些是依赖,有些是制约,有些是干扰。例如系统最初有一个功能a运作良好,当加入功能b的时候,a功能会制约b功能,所以在设计b功能的时候,除了要完成b本身的功能外,还要设计如何和a功能互动,或者如何屏蔽a功能对b功能的限制,到c功能到来的时候,a和b同时要影响c,甚至可能要考虑abc的联动,这样系统就会变得越来越复杂了,所以复杂的功能是当前软件复杂性的主要原因。
其次,软件的复杂性的另一个元凶是人为的。也就是由于开发软件的人和组织因为能力不足,或者因为懒惰、贪婪、傲慢等原罪,人为的使软件变得复杂。
笔者认为软件发展的大势仍然是向着简单性的方向在前进。因为对功能的要求越来越多,我们现在看到的复杂的软件架构实际支撑了更为复杂的诸多功能,所以从这个角度来看,软件架构实际上是像个简单的趋势发展。
我们所能做的是把复杂性封装在更低的层次,例如,操作系统封装了对计算资源(内存,CPU,进程,文件系统等)的使用,AWS云在基础设置Infrastructure层次对网络、主机的使用进行封装,而kubernetes利用容器集群,封装了应用部署。操作系统、云、kubernetes都是很复杂的,但是通过良好的封装和简单的接口,他们给使用者带来了便利。作为架构设计的一个原则,应该尽量把复杂性封装在更低的层次。
通常在分层架构中,越高的层次一般意味着更多的代码和更多的使用。当一个问题发生的时候,一般总是表现在最高层处(UI或者应用层),然而对这个问题的处理,可以在各个层次解决。例如,为了提高访问效率,我们可以在UI或者数据层加入缓存,数据层的缓存可以支持所有的UI用户,而UI层的缓存只能针对使用该类型的UI的用户。例如Web端和移动端都不用UI各自要实现自己的缓存。当然实际中所有的层次都可以利用自己的缓存来解决问题,但是从解决问题的效率来看,在低层次上解决问题的效率要高于更高的层次。所以我们应该尽可能把复杂问题的解决放在更低的层次上。
但是我们之前也提到过人是软件架构的一个基本点之一,分层的架构往往也意味着分层的组织。当UI的团队试图解决性能问题的时候,他们往往希望在组织可控的范围内解决问题。这并不是因为他们不明白在数据层解决问题的优势,而是因为要和另一个组织—数据库部门去沟通,带来的额外成本比自己加一个缓存可能还要高,做正确的事是有代价的。
爱因斯坦说“Simple ,but not simper”;
建筑大师路德维希.密斯.凡德罗说“less is more”;
奥卡姆说“如无必要,勿增实体”;
老子说“大道至简”。
软件架构之道在于找到设计的平衡点,使得架构足够简单,但是能够满足需要。
除了简单作为道之根本,软件架构设计还有一些常见的通用原则,笔者认为这些原则都是和降低系统复杂度一致的:

Kiss原则
“Keep is simply and stupid”是我们之前的简单性原则的一种说法。笔者想说的是简单未必愚蠢,很多时候大智若愚。

最小代价(努力)原则
光总是走最短路径,人也一样,程序员的惰性与生俱来,我们总是选择最容易行走的路径?这也是为什么我们应该尽可能在一开始的时候做出正确的选择,因为一旦这个架构设计出现,后面的人很有可能不愿意为了更好的架构而改进,而是遵循已有的设计。这个和简单性原则一致,如果我们不能在一开始做出正确的选择,因为最小代价原则,系统必然会走向复杂。

最小意外原则
以前读过一本讲UX设计的书《点石成金》,英文名叫《Don’t make me think》。和这个原则一致,UX设计应该自然,符合用户的常识和使用习惯。如果用户需要通过思维才能理解如何交互,那么一定是设计出了问题。同样的,架构设计也一样,好的设计应该避免意外,遵守通用的规范和习惯,代码也是。这些意外其实是软件架构和设计中的复杂因素。最小意外也就是意味着尽可能的简单。

Dry原则
重复是软件的原罪之一,“Don’t repeat yourself”告诉我们,应该尽可能的消灭重复和冗余重复使得软件的阅读、修改,测试变得复杂,消灭重复,是使软件变得简单的手段之一。
所有的这些原则都会和降低复杂性不无关系。

软件架构之“术”

在这里插入图片描述
我们再来聊聊软件架构的“术”,在软件和软件架构设计领域,有很多方法和工具,我们来看看其中最常见的一些。我们可以把他们归为“术”,这些和软件架构都有着直接或者间接的关系。
数据结构和算法
数据结构和算法是软件编程领域最重要的方法。数据结构,是抽象的表示数据的方式;算法,则是计算的一系列有效、通用的步骤。数据结构是以某种形式将数据组织在一起的集合,它不仅存储数据,还支持访问和处理数据的操作。算法是为求解一个问题需要遵循的、被清楚指定的简单指令的集合。算法与数据结构是程序设计中相辅相成的两个方面,是计算机学科的重要基石,运用数据结构和算法可以有效地解决编程中遇到了一些常见的复杂问题。
每一个程序员从一开始学习编程就接触数据结构和算法。有人说程序等于数据结构加算法,这有一定的道理,但是显然不够全面。数据结构和算法作为编程的基础方法,是各大软件厂商招聘考核的标杆,不管你是程序员还是架构师,都需要投入精力于此,数据结构和算法是软件和架构设计的基础。
面向对象和设计模式
仅有数据结构和算法,还不足以应对复杂的软件开发的需要。面向对象的设计成为了软件开发领域里最为流行的思想。面向对象是一种对现实世界理解和抽象的方法,利用的是隐喻的手段。隐喻其实是我们在软件设计中常用的一种手段,为了便于理解,我们把现实世界中的概念用软件中的概念来模拟,这样做的好处是便于我们去思考,因为我们的设计是基于我们对现实世界的理解,所以可以重用我们现实世界积累的成功经验。这样做的缺点是软件世界有自己的特点,完全套用现实世界的偏见可能并非最为有效,现在虽然对面向对象的程序设计仍处于统治地位,但是它的声音已经渐渐变弱,与之对应的有面向过程、函数式编程、面向切面等思想。
设计模式起源于《设计模式:可复用面向对象软件的基础》一书,提出和总结了对于一些常见软件设计问题的标准解决方案,称之为软件设计模式。该书作者,后以“四人帮”著称。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案,这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

单一职责原则single responsibility
一个代码组件(例如类和函数)应该只执行单一的预设的任务。

开放封闭原则open close
程序里的实体项(类、模块、函数等)应该对扩展行为开放,对修改行为关闭。换句话说,不要写允许别人修改的类,应该写能让人们扩展的类。

里氏替换原则Liskov Substitution
里氏替换原则的内容可以描述为:派生类(子类)的对象可以在程式中代替其基类(超类)的对象。

接口隔离原则interface Segregation
指明客户应该不依赖它不使用的方法。接口隔离原则,拆分非常庞大臃肿的接口,成为更小和更具体的接口,这样客户将会只需要他们的感兴趣的方法,这种缩小的接口也被称为角色接口。接口隔离原则的目的是系统解开耦合,从而容易重构,更改和重新部署。

依赖倒置原则Dependency inversion
程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。

以上的五个原则构成了著名的SOLID,除此之外,还有一个著名的高内聚低耦合原则,是指具有相似功能的代码应该放在同一个代码组件里。一个代码片段(代码块,函数、类等)应该最小化它对齐代码的依赖。这个目标通过尽可能少的使用共享变量来实现。“低耦合是一个计算机系统结构合理,设计优秀的标志,把它与高聚合特征联合起来,会对可读性和可维护性等重要目标的实现具有重要的意义。”该原则和我们之前提到的把复杂性封装在更低的层次上是一致的。对复杂性的封装就是高内聚。

行走在软件江湖

如果软件行业是个江湖,那我们程序员就是行走在江湖上的武林人士。每个人要做的就是找寻“道”,研习“术”,磨炼“器”。
“道”是价值观,是你的江湖理想,是你的追求。就像《神雕侠侣》时期守襄阳,信奉“侠之大道,为国为民”的郭靖
“术”就是你的内功心法和拳艺招数。数据结构和算法就像是内功,帮助你提高对战的效率。和各种其他方法就像是招数,你可以像是学了独孤九剑的令狐冲,仅凭招数就可以杀敌无数,但是要成为武林盟主,内功修养也是不可或缺的。
“器”是工具,各种语言、IDE,可以划分到“器”,工欲善其事,必先利其器,各位大侠行走江湖,免不了要选几样趁手的兵器,有人喜欢剑走偏锋,有人喜欢暴力砍杀。什么,你说你喜欢九齿钉耙?笔者只能赞你一声:二师兄威武hhh。
江湖苦修非一日之功,希望各位大侠在软件江湖早日得道,成为武林至尊或者一方豪杰!

写到这里呢,笔者组建了一个高质量的技术交流微信群,欢迎各位江湖同僚们入群各显大招!
ps:若二维码过期可私信博主,博主拉你。
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值