在架构设计的领域,总结出了很多原则。这些原则的很简略,容易传播。但是提出这些原则的往往不会告诉你,为什么应该是这样的原则。哪怕说了背景,过了时间,听的能已经不知道原则提出初衷。这些原则,粗看起来是很有道理,可是在实践中,却往往不是这么回事,那么就沦为鸡汤了。在看这些原则的时候,每个要形成的判断能不要亦云才好。以下是个设计原则的思考,不正确,期望能够引发读者的思考,形成读者的判断。
KISS 原则
KISS(Keep It Simple, Stupid) 原则,翻译成中是“保持简单、愚蠢”。这是没有主语的话,猜想主语应该是指设计师,并且这个“It”应该指的是设计师所设计的系统。这条原则应该是告诉设计师在设计时要保持系统的“Simple and Stupid”。这个原则仔细分析,有两个题:
,“Simple and Stupid”的判断原则是什么,怎样才算是“Simple and Stupid”是这个原则中最让惑的地意识给出的是所认知的“Simple and Stupid”,这未必是提出这条原则的理解的“Simple andStupid”。比如让体操队员来做后空翻,对于他来说,这是“Simple and Stupid”。可是让没经过体操训练的普通后空翻,这绝对不“Simple and Stupid”,可能会摔断脖的。
也就是说“Simple and Stupid”是因异的,对于技术不同的他会的或者他能够熟练掌握的技术才是“Simple and Stupid”。很设计师,看到系统,他可以给出他设计的“Simple and Stupid”,可是如果实现的平很糟糕、技术达不到,勉强去实施的话,有可能会实现不出来的,哪怕做出来了也是问题多多,弄不好要搞出情。因此,要设计系统,要根据实施团队的,做适合他们的架构设计,这样才可以算得上是“Simple and Stupid”。
其次,系统的是为了给来使。设计师设计了“Simple and Stupid”的系统,那么这个系统对来说就好果了设计师,或者说了实施,说不定会影响使统的呢恰是让来“Simple and Stupid”的系统,才是好系统。那么给系统的设计师“Simple and Stupid”的原则,到底是在帮助谁害谁合业务吗多公司的设计师在设计系统的时候,不断的和业务团队冲突,为了保持设计师所持的“Simple and Stupid”理念,很容易会降低的体验,导致最后也都不“Simple and Stupid”。
可见,这个设计原则软件是有问题的,我们不能够为了“Simple and Stupid”去设计,不能够设计系统时,依照“Simple and Stupid”为去设计。因为设计系统的是为了更好的完成系统所服务的的访问,是为了设计者或者实施者。考虑设计者和实施者的时候,是在这个完成访问的前提下,怎么做到低成本的、可持续的迭代。因此,如果设计者不能够理解的需求,不能够理解通过不同的访问周期来达到的,是无法设计好系统的。
只有通过对的业务周期、访问周期进析,根据流量的压同,进理的树状拆分,也因此形成不同的系统,那么这些所形成的系统是内聚的,边界是清晰的,也是“Simple and Stupid”。也就是说,只有从业务上去分析、去拆分,才能够得到“Simple and Stupid”的结构,这是副产品,是。如果依照这个原则为去设计,则可能会破坏业务本身的整体。
这个原则本身是从军业出来的,说明“Simple and Stupid”含义是有军景的,有军业的标准,读者感兴趣的话可以去研究。不去了解概念的历史,盲直接引软件来,很容易吃亏。其中“Stupid”的含义是为了形容系统组件的修理维护简单程度,不知道这是不是这个原则提出者的“Simple and Stupid”的本意。按照这个意思,如果所设计的系统根本不允许或不需要考虑维护或者修理的话,还需要考虑“Simple and Stupid”吗过来,我们回头去审查系统的时候,如果发现所设计的系统对于访问的拆分不够清晰,不是树状结构的时候,那么是不够“Simple and Stupid”的,倒是可以作为架构审查的判断点,帮助改进设计,但是不能够作为设计的依据。
SOLID 原则
SOLID 原则,据 WikiPedia 所说是由 Robert C. Martin 总结的对象设计的原则。这个名字其实是以下原则的母简写:
-
Single responsibility principle;
-
Open/closed principle;
-
Liskov substitution principle;
-
Interface segregation principle;
-
Dependency inversion principle。
原本现实中打保龄球,可以算分,也可以让别忙算。为什么可以拆分开来,这是因为打保龄球的核命周期是打球,算分只是游戏规则,没有这个规则,保龄球也可以打的,因此这个分数计算规则可以拆分出来。并且保龄球游戏产结果是计算分数的输入,这两个步骤是打保龄球游戏的两个连续的周期活动,因此非核命周期可以拆分出去,形成树状结构。Game 的原本功能没变,只不过其中步骤的实现分离出去,通过调回归了。这样 Game 的职责更专注,分数计算也更专注,修改时可以互不影响,确实叫“内聚”比较好。
可是改成“单责”,意思就变化了。后把详细解释的内容从“an axis of change”改成“one reason to change”,意思进不同了。“an axis of change”指的是维度,one reason to change”指的是理由,很难等同,应该是有很争议的。
那么怎么样才算“单呢个是没有确定的标准的。Game 包含打球和算分两个步骤,难道 Game 就不“单了吗龄球要打球和算分的话,这是“单的运动,放在并不算职责不单这样做并不错。但是后续修改和维护的角度来看,如果分数计算规则要频繁的修改,但不希望动 Game 的话,分数计算可以拆分出来,这是架构拆分,但并不是因为“单责”的缘由才拆分的。那么打球和分数计算分离开来了后,难道分数计算职责就“单了吗,如果分数计算有很多不同规则,还可以把规则做架构拆分,分数计算职责也并不“单呀。
从“单的思路去看,最近出现的 CQRS(Command Query Responsibility Segregation),“命令查询责任分离”,把命令和查询拆分开来,分开后这个职责“单吧,可是这个做法却完全破坏了业务本身的内聚。还是前个保龄球的例想象算分和查询分数是不同的类,那么算分的规则发化,那么查询分数的规则不能够跟着算分来变化,Bug 就很容易出现。如果这两个类分为两个不同的维护的话,出现问题的话,这两个可以没完没了的扯,责任也很难分清楚,需要的沟通成本,最后会糟,这就是破坏了业务本身的内聚所带来的后果。CQRS 这个做法往往是数据读写的场景,提升读或写的性能,只有当读、写时不存在业务逻辑的时候、仅仅是做读写通道的拆分的时候才。
随着现代开发理念的发展,越来越多的到了抽象、继承的坏处,越来越多的合的来协作,其实抽象类可以看成是组合的特殊情况。随着代码的变化越来越频繁,拥抱变化反为了风只要代码中的类做到了“内聚”,只要业务代码能够做到内聚、访问通道做到不重那么要重只会是业务代码,这样修改的范围会多,同时依靠版本与依赖管理,完全可以避免修改所产影响。因此这个“开 / 闭原则”,也需要重新再看待,理性使使闭原则,就意味着的抽象类、的继承,意味着内聚的丧失,意味着要付出耦合的代价。
代换原则的本意,应该是对开闭原则的拓展,实现开闭原则。只有能够类来代换,才能够符合开闭原则。但是总不能够每次修改都创建吧因此可以看到,开闭原则也是无奈之举。正确的做法是针对修改创建不同的版本,针对不同的版本来进建、发布。
但是有了这个代换的办法,结果倒是不遵守开闭原则了,尽可能的抽象,结果把本来应该内聚在类中的和属性,分散到许多不同的中去了,这是很弊病。记得以前 Java 认证考试就专门考继承时的变量初始化,许多进这个坑并且这种情况非常容易造成事故,因为这种错误只有在运才能够发现,还不好排查,往往修改时,的 bug 就出现了。没有做到内聚的后果是很严重的。
另外问题是代换的时候,比如中有 Instrument.play(),可以iano.play(),Violin.play() 来代换,虽然引类时可以的很正常,但是 play() 出来的声是不确定的,因此也不能说没有发化,只能说都能够 play,但是 play 的结果是不的。但是当实际业务很复杂,不光要 play,后续还要调整具体的乐器的话,这个抽象就比较麻烦了,因为不同乐器调整各式各样不同,然后就发现原来的抽象不够要费很进抽象。慢慢的在业务的变化下,抽象就变成糟了,最后连也看不懂代码了。
可是何必要花去抽象呢接引际的乐器就好了。除非能够做到抽象能够适应以后所有的变化,否则还是实实的实际情况吧,哪怕有多个乐器需要选择,写个 if-else 就好了,没代码,并且还是可测试的,并且错误是编译期可以发现的,以后修改、扩展也容易。为了这代码,引入那么多抽象,破坏“内聚”不说,代换时,都是运才能确定的,反致运探查问题的麻烦,同时,代码也很难阅读,没去修改,影响质量。
Interface segregation principle ,即“接离原则”。这个原则相当于是预设了调与被调两前提,对于调来讲,被调的接量应该最。这个原则其实就是通道访问的隔离。在访问通道上,不同的客户端,不可以使样的访问通道,因为会导致它们之间的访问互相影响,这是很简单的道理。比如居民的车道和道必须要分离,否则两者通道混杂的话,会出事情的,很容易有危险,产外的问题。
可是为什么会变成恰就是为了要重个接以便让各种不同的调来访问。所以访问通道上的重万万不可的,因此也会导致服务端会变成从慢会变成团队之间的纠纷点,故障点。
个例仅是为了在编译期间不依赖于设备,为了这个这么代价,有什么意义吗终运间对设备的依赖是逃不掉的。其实访问通道依赖于设备是没有关系的,因为通道没有逻辑,不需要测试。所以这种通道的共享是过度设计,根本没有必要。copy 有的逻辑、这个逻辑可能还与通道有关的话,那么这么多通道混杂在,反外的增加了复杂度。并且这个 Copy 程序根本没有必要重因为没有逻辑就没有重价值。如果要从键盘写到磁盘,不如重新写CopytoDisk 更简单,因为可能不,来也更简单,也更独
发构拆分,就意味着要管理这个新增加节点的周期,也意味着额外的成本。只有相依赖的两对对造成影响的时候,才需要通过拆分增加节点,以便让两以独,互不影响。并且增加的这个节点不是 Abstract 的,也可以是实体。建议体,不要bstract,因为依赖于 Abstract 意味着依赖继承树,成本太俗的话说,尽量去正规公司,不要去依赖公司,层级越少,沟通越少,效率越所以,不要始就去架构拆分,要根据当时所的情况,合理的采
所以,对于 SOLID 原则,第其实是说内聚,只是“单责”的提法不好。第第三个说的是继承的问题,这是对象语特性。继承会有很多的坑,会破坏内聚,也不合适。第四第五个,其实说的是访问通道的问题,只要做好访问通道的隔离就不会有问题。如果从这些原则的字的意思去理解,怕是要误区了。
业务内聚与访问通道内聚
当然,很多会提到“聚、低耦合”的原则。这个“低”的说法不够严谨。只要某个业务的周期活动不在类中确定,那么这个类就没有形成内聚,反之就是做到了内聚。只要做到内聚,就没耦合了,就只有依赖关系,这个依赖是树状的结构;只要没做到内聚,肯定耦合了,没有之分,最后都会带来麻烦,区别在于带来麻烦的多少。所以应么没有内聚,只有耦合,要么只有内聚,没有耦合,只有其中情况。
可是要做到业务的内聚,却离不开业务访问通道的隔离,这个原则我把它称作“访问通道不重原则。观察的关系可以发现,重务与重问通道,只能够选。因为重问通道会导致业务无法内聚、也就无法重重务则会导致访问通道无法重如果想两者都达成,那么最后的结果是只成功的重访问通道,务内聚则会被破坏。
为什么会是这样呢为事物对物理空间的占有是独享的,问通道则是事物跨越物理空间的通路。必须确保对事物的访问通道是独享的,才能够保证这个访问通道是内聚的。如果不同类型的共享同访问通道,就意味着访问通道不再是独占的了,这就是对访问通道内聚的破坏,最终这个访问通道就变成不确定的通路,内步冲突不断、阻碍重重,会反应到对业务内聚的破坏。
比如做公共的交通,往往不允许带宠物,这就是要遵守宠物和的访问通道内聚原则,因为宠物和在狭窄的空间存,会产出非常多额外、不必要的冲突。所以我们说“内聚”,绝对不能只提业务的内聚,访问也是独特的业务,也需要达到内聚的原则。也就是说,“访问通道不重原则其实说的就是“访问通道的内聚”。
如果做好了业务的内聚,并隔离不同类型客户端对业务的访问通道,形成访问通道的内聚,基本上程序就不会太差,代码就会很稳定。有了这个基础,再根据运营过程中所产瓶颈点,有针对性的做业务架构拆分或访问通道架构拆分就很容易了。做为架构设计师或者程序员,如果不把“内聚”放在最重要的位置,最终会被需求给淹没的。
因此,架构设计的的核则就是“内聚”,任何架构原则都不能违反此原则。这个“内聚”包括两部分:“业务内聚”,“业务访问通道内聚”。所以,对于我们遇到的任何架构原则都可以这样去判断:如果发现它违反了“业务内聚”原则,我们都要三思,因为会导致业务分散、无法重如果它违反“业务访问通道内聚”原则,也就是“业务访问通道不重原则,我们也要三思,不要去追求访问通道重
“访问通道内聚”原则是软件普遍忽视的,这个原则太不起眼,也太容易被破坏,都忽视了。“访问通道内聚”的缺失会导致“业务内聚”原则的破坏,导致业务无法重于是,系统就开始陷入困境了。
=>更多文章请参考:《中国互联 业务研发体系架构指南》
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!