-->

郭东白的架构课_47_36能力维度一如何提升结构化设计的能力

你好,我是郭东白。

上节课我们定义了架构师这个角色,也了解了架构师的五个成长阶段,分别是程序员兼职架构师、跨越架构师。

从架构师和CTO以及跟这五个阶段分别对应的核心能力,也就是结构化设计能力,解决横向问题的能力,解决跨域冲突的能力,正确技术决策和创造生存优势的能力。

从这节课开始呢,我们就来讨论一下程序员到CTO的四次能力变迁。

在分析的过程中呢,我们会分析从一个角色到下一个角色之间的工作挑战,以此导致的能力差异,以及从现有能力到下一个能力所必须跨越的障碍是什么和跨越他们的方法。

最终呢帮你规划自己的职业成长。

我们先来研究一下,从程序员成长为架构师所具备的核心能力,也就是结构化设自己的能力。

我跟很多的资深研发管理者CTO交流过我们的一个共同观察,就是一个缺乏结构化设计能力的程序员,永远都不会成为一个好的架构师。

我先给出软件架构能力的定义,从定义中你就可以看出结构化设计能力的关键作用。

软件架构能力指的是为相对复杂的场景定义,并且引导实施结构化软件方案的能力。

其中结构化代表这个软件在其设计范围内的设计理念、代码结构和实现方式是同质的。

我这里用了同质这个词表示结构化同质是指的是在设计范围内处处一致。

与同质这个词相反的是混乱,也就是缺乏这种一致性。

你会发现在这个定义中,我们并没有锁定适用的人群范围。

适用范围呢可大可小。

也就是说呢,一个刚入行的程序员其实没有做过任何架构规划,也完全可以培养自己的软件架构能力。

举个例子,你可以在自己的决策范围内,也就是自己写的代码里面寻找并给出结构化的解决方案。

这种结构化的实现往往不是产品需求的一部分,也没有人会考核,更不会直接给你发奖金激励。

但是呢你会发现身边有一些程序员,对代码几乎有一些产品偏置的洁癖。

在我看来这就是对结构性的追求。

这种追求呢在一个程序员的职业发展中是具有连贯性的。

从程序员到CTO,刚开始可能是对代码结构性的追求。

随着职业的发展,这种结构性会扩展到更大范围和更广的维度之中,甚至延伸到公司软件研发的各个方面。

最终代码洁癖会演变成对软件系统,甚至是研发组织结构性的执着贯穿整个职业生涯。

与此同时呢这种制着也会固化成对软件结构性的识别、改进和设计能力。

我观察过很多资深的软件工程师和管理者,他们的职接成长过程其实就是把结构性的能力从一个领域迁移到另一个领域的过程。

一开始呢这个领域是写代码,然后呢就到了数据模型。

接着呢是功能模块,然后是团队定位和组织结构。

最终呢到了商业模块,可以说呢一个以架构师为职业追求方向的程序员,结构化设计能力是基础能力。

那么怎么提升程序员的结构化设计能力呢?提升结构化设计能力,起点是代码的结构性。

而在结构性之前,其实还有个更朴素的起点,那就是代码的整洁性。

互联网时代呢代码整结主要来自编程规范的掌握和运用,设计抽象和常见设计模型的采用,对编程规范,各大软件公司一般都有要求,网上也可以检索到我这里,就不再赘述。

这里我简单介绍一下代码抽象,尤其是面向对象编程的设计思想,也就是OOPOOP呢其实是结构化思维的一个重要范式要求。

我们认真思考什么是什么是类,什么是对象,什么是对象所具备的属性,什么是对象可能发生的行为,这个行为的作用,对象是谁等等。

通过这种对模型本质的思考来定义软件结构,就是提升软件结构性设计能力的有效方法。

介绍OOP的国内外数据有很多,我相信每个人都看过几本,有不少人呢甚至对OOP的原理倒为如流。

但是呢我发现很多人在写代码的时候呢,很少习惯性的思考OOP方面的问题。

而这种平凡的深度思考习惯,恰恰呢是架构师成长的必须。

举个关于扑克牌游戏的简单例子,如果你去githb翻一下这类游戏的代码,你会发现呢很多程序员都是把每一扑扑克设计计成一个普对对象。

果你在认真思考一下设计,你会发现在普通扑克牌游戏的场景中,其实这么做呢是不太准确的。

一般来说,当一张扑克牌出现在电子游戏这个场景之下的时候,除非游戏允许出老千,每张牌都可有猫腻,否则每张扑克牌都不是一个对象,而是全局状态统一的单例。

对比之下呢,扑克牌呢里面每一手牌呢都是一个普通对象,每个玩家呢也是一个普通对象。

其实和single呢是完全不同的设计,他们的运行成本和代码可读性也完全不同。

其实任何一本OOP的书籍和入门的设计模式书籍呢都会提到类似牌单例更优的强场景。

我相信如果呢你稍稍思考一下,也会做出正确的选择。

但多数程序员呢都缺乏这个思考习惯。

我认为正是这个习惯呢,把一般的程序员和职业的软件工程师区分开了。

不过呢这个思考呢其实还没完成,你要想达到优秀的软件工程师层次,你还要再深度思考一层。

想想看,如果你把一张牌设计成单例之后,会不会带来某些局限呢?所以这个时候呢,你就要问自己在哪些场景中需要把这张牌设计成一个对象。

为了回答这个问题呢,我们举一个稍微阴暗一点的例子啊,就是为了方便。

假设呢你的外包公司在一个外国的赌场做网上的德州扑克的游戏。

这个国家呢赌徒呢很迷信,认为每天晚上每张牌的运气都不一样。

比如说有的牌在今天晚上就会给自己带来好运气。

本来呢一个赌徒手里呢拿了一张黑桃三和红桃吧,应该是主动弃牌。

但是呢你在红桃八上加了一行小字注释,注释的内容是这样的,刚才真武太郎赢牌的那一首呼噜,就是这张红桃吧,加上另外一段儿和和一个小队儿,那次呢他赢了三千个三十,而你前面帮你赢了那手五百一十个金币的,all in的童话里面也有这张红桃吧。

一旦有了这个注释呢,赌场的每天交易额就会增长百分之三十。

在这种商业模式下,虽然你还在设计扑克牌游戏,但是呢每一张牌呢都有了自己的生命周期,因为每张牌会因为经历过某个重大事件而引起了各自的生命周期的变化,因此呢他们就不再是单例了。

至少每一场局局中每张牌牌是一个独立对象。

你作为一个优秀的软件工程师,这时候就要做出判断了,我的公司处在哪个阶段,需要有这么前瞻性的设计吗?如果我不需要,但是未来需要我应该采取什么样的设计,才能让未来的过渡最容易呢?如果日常就在做这种深度思考,那么恭喜你已经养成一个架构师应该具备的思考。

习惯了日积月累,这种思想实验就成为你的核心竞争力。

想想看,别人写代码呢就是依照产品需求文档做个自然语言到计算机程序的转译,而你一出手就不一样了。

是一个基于对问题本质、商业价值和软件结构性思考的长远设计。

另一个对代码结构性帮助很大的呢就是设计模式。

设计模式的主要价值呢有三个方面。

第一呢是经验的沉淀,用其简单话描述,就是如果你遇到这种场景,就应该采用这种设计模式。

可以说呢这个是系统化高效借鉴他人成功经验的过程。

第二呢是传递设计原理,也就是对代码背后思考的一种标准化。

注释。

代码所覆盖的领域呢可能是全新的。

比如说元宇宙中一个新物种,甚至是在现实世界都不一定有对应的存在。

但是如果用factory或者visitor这个尺,别人马上就知道这个设计背后或的含义比注释更深远,也更准确。

可以说呢你通过规范化的设计,语言表达了自己的思想。

第三呢是降低理解和维护代码的门槛。

如果实践中经常采用常见的设计模式,就会大幅降低其他人的理解和维护你代码的成本。

在代码中呢,这些设计模式就好比旅游景点里面供游人休憩的座椅一样,让另一个在你的代码高山里艰难攀行的路人,突然间呢找到了一个熟悉的小憩之地。

设计模式呢强调趋同性,用广泛传播的知识来标准化代码的实现,从而降低实现手段和命名的多样性,也就是提升代码的一致性。

当然也有人呢非常反对设计模式,认为设计模式多数时间是被用烂了。

很多人为了引用设计模式而得出了错误的设计,这种现象呢的确存在。

但呢这不是设计模式的问题,而是使用者应用不当的问题。

在设置门式的基础之上呢,一旦开始模块设计,就要对事件做建模。

这时候呢还需要学习问题与建模,也就是通过领域模型帮助我们跟所有的需求方在某个问题上达成共识这个过程模块二已经有了详细解释,这里就不重复了。

确定了问题之后呢,接下来呢可以设计服务API,也就是如何以解决明了的方式包装要解决的问题,让使用者能够轻松且长期的使用你的服务,而不需要耗费大量的时间去理解学习集成测试、维护和升级系统。

那么在日常工作中呢,想做到结构化设计具体应该关注什么呢?我认为主要是三点。

第一呢设计理念整体的设计呢需要跟公司和部门的理念保持一致。

比如说整个公司都采用分层结构,那你的设计也尽量要采用分层架构。

整个公司呢都采用微服务的设计理念。

那么你的设计也要采用微服务的设计理念,否则写出来的代码既难维护又难理解,也难以被公司的其他人复用。

第二呢是API的结构性软件模块的对外界面,要有比较明显的语义结构,也就是说暴露给其他人调用的API要有条理,表达准确且易于理解。

否则呢API在被使用的环境中,会让调用方的代码变得晦涩难懂,结构混乱。

否体呢API的结构性体现在三个方面。

首先呢是语义表达的结构性。

如果把API呢理解成一段结构,那么这段文字呢需要有内在的顺序和结构,也就是我们强调的语义结构。

这种定义呢要具有一定的内在含义,逻辑顺序,因此呢也具有宏观的结构性。

比如说呢你一个实体的API呢要反映出这个实体的状态和行为。

因为这些状态和行为随着实体的生命周期而发生变化。

所以API呢就要围绕实体的生命周期来组织。

而围绕一个流程定义的API呢,就要根据流程的前后顺序来组织api甚至在API命名上的语言的结构性也会大幅提升API的可读性和可维护性。

其次呢是功能组织上的结构性。

举个例子呢啊一个API呢可能为多个用户角色服务。

每个用户角色呢有多个场景,每个场景呢还可能有多个功能。

那么这些功能应该组织成三层的结构,也就是角色场景和功能。

不同的用户角色和不同的使用场景都应该有不同的设计力度。

比如说订单服务可能为用户商家运营、审计这四个角色服务。

那么面向用户的订单查询和面向商家的订单服务的力度和呃,内容呢就完全不同。

最后是数据模型的结构性API呢会暴露数据给调用方。

那么暴露出来的数据呢就要清晰的结构,遵守一定的规范。

假如说你在使用jasson传递数据,那么jason呢要做到可读且合理。

所谓可读呢,就是只看这项呢,你就能很快明白这些语句的含义及及其内部对象之间的关系。

所谓合理,就是包含在项中的数据结构,要符合最小必要的原则。

说到这几点呢,很不容易,哪怕是所有程序员都熟悉的get up,你也能从他们的API和数据模型中挑出很多毛病来。

第三呢是模块内部的结构性,也就是程序的结构性。

这种结构性呢就是我们前面提到的程序员的设计能力的具体体现。

举个例子,假如你现在使用java呢,那你对package hierarchy、 package interface、 class function等等的实现呢就体现了你的设计能力。

其中package hierarchy和package的设计,你体现了你对领域的理解和对领域的分装。

而interface的设计呢体现你对一个领域对外服务的思解。

Class的设计呢代表你对实体状态和数据模型的理解。

而function的设计呢体现你对计算模装的理解。

你可以说呢假设我是个职场新人,怎么一上来就能想出完美的设计呢?事实上呢我们日常设计的很多领域都有现成的国际标准,不需要再去发明创造。

除了我们前面提到的设计模式之外,使用常见的spring、 boot等服务框架依赖主流,而不是小众的技术,遵守WCC的发布的最佳实践等等,都是提升代码结构性的好办法。

我这里呢有一个常见的问题,如果队友水平不高,那是不是要降低我的标准来和他们对齐呢?在的观点是这样的啊,在模块内部的结构性上完全不需要这样做。

因为这是你的私欲,有更好的结构性呢,不但可以帮助到自己,而且还能帮助到未来,接替自己维护模块的人。

在API的结构性呢可以尽量追求API的结构性,前提呢是不能跟整个研发团队现有的设计规范冲突。

哪怕你认为现有的规范不合理,也应该先建议调整规范,从而引入更先进、更可读、更容易维护的标准,而不是直接越过规范,尤其呢是API的命名。

Jason schema的定义上,如果跟团队的主流选择相冲突,那么你的设计会渗透到其他人的代码里影响他人代码的一致性和可读性。

在整体的设计理念上呢,你没有权利做任何的发明创造,否则呢你会增加公司的架构混乱度。

这个时候更好的选择是想办法影响决策者。

好了,今天内容呢就这么多。

我简单总结一下呢,我们这节课呢简单聊了程序员的结构化设计能力。

说句实在的,我刚做程序员的时候呢,这项能力就很一般。

一方面呢自己当时的能力不够,另一方面呢是自己的主动意愿也不强。

不过后来有了转变,我分享出来,希望你对你有帮助。

我的第一份的全职工作在甲骨文,刚开始呢,我主要写算法代码,不需要跟其他人合作太多。

我后来发起了一个内部创业项目,最终呢做的比较成功。

现在oracle数据库的一个主要功能,由于项目商业上成功呢,所以有更多的人参加到了项目中来。

这时候呢我才发现自己的设计里有很多瑕疵,也收到了很多来自内外部的批评。

对于这种批评呢,我刚开始很抵触不到最初的代码,被重构两三次以后,我终于意识到有些设计失误其实是完全可以避免的。

于是呢我开始重新阅读消化软件架构方面的书籍,开启了习惯性的讲思考。

我的能力呢因此有了非常大的提升。

因为我发现了自己,其实不是一个邋遢的人,只是懒得照镜子而已。

是的呢,在我看来呢,习惯性的对自己所设计的软件的结构性进行反思。

这个过程呢就像是照镜子,你看到设计中那些污垢,然后再去清除。

能做到这一点呢,结构化设计的能力自然会提升。

最后呢我给你留了三个作业,你可以选择其中一道作答。

和之前一样呢目标不是做的多,而是做的深。

这节课呢我们简单的提到了美,并且隐含的把架构之美和结构性画上了等号。

你认可这种说法吗?如果不认同你的观点是什么?第二题,API的延续性是API结构中非常头疼的问题。

如果能为了保证现有用户的习惯,新的系统呢必须要提供跟过去兼容的借口和设计。

你可以举几个例子吗?你碰过类似的场景吗?是怎么平衡的呢?第三题,在你的程序员职业生涯中,追求结构化设计的最大阻力是什么?你是怎么克服的?有什么经验可以分享给大家吗?欢迎把你的思考和想法呢留在留言区,我会和你交流,也欢迎你把接课程呢分享给你的朋友,同时呢我们一起进步。

我也请你关注我的抖音号郭东白。

好,我们下节课再见。