写了这么多年代码,你真的了解设计模式么?

昨天和同事聊到最近他要做的一个培训,大概的课程是这样的:

第一天:

  • 上午:面向对象原则(OO+SOLID )
  • 下午:设计模式(Design Pattern)

第二天:

  • 上午:简单设计(SimpleDesign)
  • 下午:重构到模式(Refactor to DesignPattern)

面向对象原则,设计模式,简单设计,重构到模式……看起来都是常规操作,但你有想过他们的关系么?

忍不住要山寨一段《写了这么多年代码,你真的了解SOLID吗?》中的发言:

上面提到的每一项大家都耳熟能详,但我发现大部分开发者并没有真正理解。要获得最大收益,就必须理解它们之间的关系,并综合应用所有这些原则。只有把它们作为一个整体,才可能构建出坚实(Solid)的软件。遗憾的是,我们看到的书籍和文章都在罗列每个原则,没有把它们作为一个整体来看,甚至提出这几项的知名大叔们也没能讲透彻。因此我尝试介绍一下我的理解。

面向对象原则(OO+SOLID )

偷懒一下,请大家移步,直接参考文章《写了这么多年代码,你真的了解SOLID吗?》,记得回来呦~

简单理解,面向对象原则给我们提供了一系列面向对象上下文下的最佳实践,指导原则和终极目标,符合这些原则可以帮助我们最大化OO的威力。

如果把面向对象类比成软件开发领域的一个武林门派,面向对象原则就是这个门派的最高心法和目标,打个比方,有点像:心与意合,意与气合,气与力合,肩与胯合,肘与膝合,手与足合……这种。

心法这种东西就是神神秘秘的,真正看起来也简单,字面上也不难理解,很容易用它来挑战别人:“你看你看,你没有做到手与足合!”

但是回到自己,要想做到就难了,知易行难!

归其原因它虽然提供了目标和评价标准,使我们很容易拿他来评价别人,但并没有告诉我们自己如何才能达到这样的目标和标准。

设计模式(Design Pattern)

那设计模式是什么?

很多人,包括我在内,都曾迷陷于23种设计模式之中,初识设计模式,赞叹于其精妙,就像个萌新的江湖小生,偶然间掉到个山洞,一下就集齐了威震江湖的“7种武器”。

然后……就开始了用长生剑切菜,用碧玉刀削瓜的“幸福生活”,不但把简单的事情搞得巨复杂,最不能忍的是还暴殄天物!

回到设计模式,无非是在面向对象原则这些虚无缥缈的“心法”指导原则下,那些前辈大神们留下的“招式”或是“套路”而已。但招式和套路并不能致胜,它只是为我们这等小白提供了一个接近大神,理解心法的途径而已,通过长年累月的模仿去反思去领悟去体会“心法”的本质和精妙,此时脑中不禁浮现《少林寺》中李连杰夏练三九冬练三伏的画面……

这就能解释为什么使用同样的招式,大神们总能一击致命,而我们却总是被按倒在地摩擦的原因了。

简单设计(SimpleDesign)

设计模式是套路和招式,那简单设计是什么?

首先要区分一下简单设计和容易设计:

简单的反义词是复杂,容易的反义词是困难。简单不等于容易,追求简单的过程往往很困难,如果只是追求容易的往往导致系统过于复杂。

这是好多年前一位同事提到过的一句话,很在点儿上,我们经常混淆了简单和容易,对于这点,另一位同事之前也写过一篇文章来阐述做到“简单”的“困难”。

做为结果的简单设计是这么一种设计,它能被几乎所有人理解, 但只有极少数人能做出. 或者反过来说也可以. 简单设计是一种只有极少数人能做出的设计,但设计一旦做出后,能被所有人理解.

可见简单设计是一种只有极少数人能做出的设计,那我们怎么才能成为那“极少数人“呢?

对此,Kent Beck给出了清晰的答案:

  1. 通过所有测试(Passes its tests)
  2. 尽可能消除重复 (Minimizes duplication)
  3. 尽可能清晰表达 (Maximizes clarity)
  4. 更少代码元素 (Has fewer elements)
  5. 以上四个原则的重要程度依次降低。

这组定义被称做简单设计原则。

我们不具体探讨这几个原则,回到最初的问题,如果说面向对象原则是”心法“是”目标“,设计模式是前辈们沉淀下来的”套路”和“招式“的话,那简单设计是什么呢?

我觉得就是实战指导原则,他可以让我们不局限于哪些经典的过往的招式,跳出套路,无招胜有招。

如果说设计模式是一种自上而下,通过不断模仿前辈大神套路达到目标的一条道路的话(有招胜无招);那简单设计原则则是另一条自下而上,忘掉招式和套路,遵循简单的基本原则,随机应变,不断演进,不断浮现,逐步逼近目标的另一条道路(无招胜有招)。

但,记住,殊途同归。

好,这位客官问了,那两条道路都可以帮我们掌握武功最高的心法的目标,我走哪条呢?如果我已经会了一条,还需要学习另一条路径么?

请移步我的《筷子定理》

重构到模式(Refactor to DesignPattern)

说到这里,就不难理解“重构到模式”到底在说什么了吧。

它无非是在解释如何通过自下而上应用简单设计原则,运用重构的技术和手法,浮现出设计模式。我管这个过程叫“重走长征路”,重新体验一下那些大神前辈们创建发现设计模式的过程。

这个过程也再次证明了这两条通往同一个目标的不同的道路也是可以相互转换的,它们只不过是面向对象原则下的两种不同形式的表现而已。

如果说“设计模式”是“有招胜无招”,“简单设计”是“无招胜有招”的话,那“重构到模式”就是“无招生有招”的过程。

总结

总的来说,我们可以把这几个概念作为一个整体框架来思考。

“面向对象原则”是OO领域的终极目标,是面向对象这门武功的心法,很容易用来评判别人,但是自己却很难达到。

为了能达到心法的境界,之前的各任掌门大神们总结并留下了一些套路和招式,什么降龙十八掌之类的,让我等晚辈可以通过日复一日的刻意练习,去不断参透心法的真谛,这些套路和招式就是“设计模式”。

但只有这十八招显然是不够的,于是我们又学到了一些实战指导原则,让我们以另一种方式,自下而上,跳出招式和套路的限制,甚至能不断浮现出新的招式,且通向的是同一个目标,这些实战指导原则就是“简单设计原则”。

最后,不断操练“重构到模式”,通过实际运用实战指导原则和手法,推演招式和套路,帮助我们将这两种方法融会贯通,相互结合,最终领悟那隐藏在一切表象背后的真理,达到无招胜有招,草木竹石皆可为剑,心随意走,人随心动的境界。


更多精彩洞见,请关注微信公众号:思特沃克

Share

Defects的启示

在过去的几个月,我做了一些实践,通过整理、讨论和分析项目上的Defects情况,来探索质量管理中的待改进点。最终发现,Defects实际上给质量管理带来了很多的启示。

当然,要讨论Defects,首先要使团队对Defects有一致的理解。我查了很多资料,也没有找到对”Defects”一词的明确定义,大部分人将”Defects”等同于“Bug”。

1947年9月9日,Grace Hopper发现了第一个电脑上的bug。当团队在Mark II计算机上工作时,搞不清楚为什么电脑不能正常工作了。经过深度挖掘,才发现,原来是一只飞蛾误打误撞地飞到了计算机内部,从而引发了故障。从此,人们开始用“Bug”(原意是“虫子”)来称呼计算机中的隐含的错误。

然而,一个好的软件产品,不仅要关注功能本身,还要关注其是否好用、是否安全、是否给用户带来良好的体验、是否帮助用户实现真正的业务价值。因此,从狭义上讲,Defects是指软件程序中存在的某种破坏其正常运行的问题或错误。从广义上讲,Defects还包含那些没有达到客户或用户期望的质量问题。具体来说,Defects可以分为以下几类:

  • 程序错误: 指程序中存在某种错误,比如边界、时区等问题,使得系统无法正常工作。
  • 性能问题:指由于性能瓶颈所导致的系统缺陷。试想,作为用户,如果你想要查看一个报表,却需要花10分钟来等待加载,你是否会放弃?
  • 安全问题:指软件安全漏洞,造成信息泄露、或使得系统数据或功能易受攻击。
  • 兼容性问题:指程序无法在不同的硬件平台、操作系统、网络环境等中正常运行。
  • 功能与用户需求不否:指软件功能与用户期望不匹配。比如,用户期望造一个沙发,却交付了个马扎。
  • 交互体验不佳:指用户使用起来不方便。譬如,电梯控制面板上的“报警”按钮和“关门”按钮紧挨在一起,你是否经常由于”关门”而误触了“报警”按钮?再比如,你在网页中填写了一个长长的表单,点击“提交”按钮后,系统提示输入信息有误,却并没有告诉你错误的哪里,你是会不耐烦地从头查阅,还是干脆放弃?

Defects的产生与应对策略

产品质量是团队共同的责任,软件开发是一个过程,任何环节都有可能产生质量问题,但每个环节的问题都应该选择比较恰当的处理方式。

在敏捷开发中,我们以迭代的形式逐步完成产品的开发,每个迭代都能以一个可交付的软件呈现给用户,从而尽早地获得用户反馈,以保证我们交付的软件是用户真正期望的。在每个迭代中,我们所有的开发都基于用户故事卡(Story),每一张用户故事卡都将经历Analyse、Design、Code、Test、Deploy的过程。

那么,在敏捷软件开发过程中,哪些环节都可能产生Defect呢?

正如上图所示,Defect分别来自于Sprint阶段、UAT用户验收阶段以及真正的生产环境。其中,Sprint阶段又细分为:不合理的需求、不恰当的设计、代码及逻辑错误、Story卡测试过程中发现的问题、回归测试中发现的问题、以及非功能性测试发现的问题。

开发过程中不同阶段的Defects,我们分别采用什么样的敏捷实践来应对呢?

上图以看板的形式展示了Sprint开发中Story卡片流动的过程,以及每个环节的敏捷实践,这些实践有助我们发现和改善质量问题:

  • 不合理的需求: 由于QA往往有不同于BA的视角,提早与BA Pair完善Story AC (Acceptance Criteria)。此时发现的问题要及时补充到Story卡上。这样,不仅能够尽早地发现需求上的不合理或遗漏,还有助于QA深入理解需求、设计测试用例。
  • 不恰当的设计:UX制作出酷炫的设计图,却并不一定是用户真正期望的,或者技术实现的成本过高。因此,一方面,要在开发之前与用户Review设计图,并按照用户的反馈及时更新;另一方面,在每一张Story卡开始开发之前,由BA、UX、QA及Dev一起Kick Off Story,通过讨论和澄清,使得团队成员对需求和设计达成一致。一旦发现问题,要及时更新Story卡和设计图。
  • 代码及逻辑错误:单元测试、Code Review、Desk Check都是用来发现代码及逻辑错误的有效手段。因此,开发提交代码后,要先执行单元测试、只有当单元测试通过之后,才可以将代码部署到QA测试环境;然后按照Story的AC逐条与QA和BA进行Desk check。除此之外,开发团队要每天坚持Code Review,以便发现代码逻辑及编码规范方面的问题。这些过程中发现的Defects都应该尽快修复。
  • Story卡测试中发现的问题:Story卡测试时发现的问题,无论其严重程度如何,基本上都要在当前迭代修复。QA可以与Dev面对面沟通,也可以将Defect添加到Story的Comment里面,再将Story重新拖回In Dev状态,或者在物理看板上添加一张物理卡片。但无论哪种形式,都需要在早会时提及,以便有效地跟踪Defect进度。
  • 回归测试中发现的问题:普遍来讲,回归测试发现的问题,优先级要低于Story的开发。因此,QA需要在电子看板或者Defects管理系统中提交一条Defect记录,然后与BA沟通,在最合适的时间Assign给Dev。但如果该Defect造成系统崩溃或者Block了某些功能的使用,就应该立即修复它。
  • 非功性测试发现的问题:非功能性测试一般是在每个Release上线之前做,发现的问题也要在Release之前修复。同样需要在电子看板或者Defects管理系统中提交Defect记录,但要注意其优先级。
  • UAT用户验收阶段的反馈:在UAT阶段,开发团队向用户Showcase,或者由用户来做用户验收测试。此时,用户会提出一些反馈。由QA和BA对这些反馈进行分析,如果是功能层面的问题,在看板上建成卡片,并在上线前修复。如果是需求层面的问题,就将其添加到需求列表中,以便安排在之后的迭代计划中。
  • 生产上的问题:生产上的问题优先级是最高的。但是与用户反馈一样,功能层面的问题要立即修复,用户体验上的问题要添加在需求列表中。

Defects对质量管理的启示

Defects并不是独立存在的,它或多或少反映出了项目管理和开发过程中存在的问题,这些问题都可能对质量产生影响。比如:线上问题的走势,是否能够反映出产品质量的变化;分析每个迭代Defects的数据及产生的原因,有助于发现开发过程中出现的问题,及时地进行风险把控。

我以自己所在项目为例,说一说Defects给质量管理和团队管理带来的启示。

1. 通过线上问题走势,分析产品质量的变化

2017年8月,我们接到A遗留系统,到10月份累计在生产环境发现历史遗留问题21个。按照优先级,每个月修复一定的数量。截止2018年7月,发现的历史遗留问题高达46个,只剩余2个还未修复。Defects数量在减少,产品质量在逐步提升。

除此之外,我们对历史遗留问题和新引入问题做了对比,这10个月的线上问题中,历史遗留问题占85%,新引入问题占15%,可见仍有部分没能在开发过程中发现,使其流到线上。要对这些问题具体分析:其严重程度如何、产生的原因是什么、为什么在开发过程中没有发现、后续有怎样的改进措施。 当然,最好能对生产上的“运维类问题”和“功能类问题”加以区分,以便采取更恰当的改进措施。

2. 分析迭代Defects情况,讨论改进措施

除了分析线上问题,我还对从2017年10月-2018年7月QA提交的Defects情况做了一个统计,观察每个月提交的Defects和修复的Defects情况。

从统计结果来看,2018年7月发现和修复的Defects数量均呈明显的上升趋势,达到历史最高点。因此,有必要对7月份的Defects情况做一个详细的分析,看看究竟是什么原因导致了这些Defects。

我对这些Defects做了一个初步的分类,并利用Retrospective Meeting的机会,与团队成员一起分析讨论。发现产生问题的原因有以下几个方面:

  • 本次Release的Story Kick Off和Desk Check做的不够好。有时候开发没有Kick Off就直接按照自己的理解开始编码,导致团队成员没有对需求达成一致的理解,做出来的功能出现偏差。有时候Dev将一堆卡垒在一起做Desk Check,这样很难逐条覆盖AC,从而将问题流入QA测试阶段。
  • 本次的需求比较偏技术,BA只能从业务的角度去编写Story卡。开发同学为了追赶工期,没能够添加充分的Tech Task, 也没能够坚持Code Review,导致出现一些逻辑错误。
  • 单元测试覆盖率比较低。作为一个遗留的微服务系统,某些服务在之前从未重构过,代码逻辑比较混乱,添加单元测试的难度大、成本高。因此一些本该单元测试阶段就能发现的问题一直流到QA测试阶段。
  • 本次Release一共一个月时间,UI一直到最后一个礼拜才确定下来,期间反反复复的修改不仅花费了太多成本,还消磨了Dev的意志,导致出现一些本不该出现的Defects。
  • 新人加入,项目工期紧,对上下文信息同步不够,导致新开发的内容破坏了一些已经验证过的功能。

这些原因充分说明了这段时间项目中存在的问题,我们对此逐条提出了具体的改进措施:

  • 坚决执行Story Kick Off和Desk Check敏捷实践,在每日站会时严格跟踪每一张Story卡的进度。
  • 预定一个定期会议,每天下午17:00 – 18:00进行Code Review,并每周一人轮班担任Owner。
  • 将单元测试覆盖率可视化。同时,制定项目标准:对于新开发的内容,必须编写并通过单元测试才能Desk Check;对于历史遗留模块,在技术债墙上添加技术债卡片,并于每周消化一个技术债务。
  • 项目开发前期要加强与客户和用户的沟通,在Story开始开发之前,确定好UI设计,开发过程中尽量避免大的改动。
  • 新人加入项目时,采用结对编程的方式完成开发。除此之外,每周在项目内进行一次技术分享Session。

当然,以上两点只是我基于A项目举的一个例子。实际上,Defects还给了我们很多启示,比如,为什么项目老是加班?为什么有些模块的Defects数量比较多?如何根据团队成员花在Defects上的efforts,制定提升计划?然而,每个项目的情况不一样,我们应该基于自己的项目背景,由团队成员一起分析深层次的原因,共同制定切实可行的改进措施,从而不断地提高产品质量。


更多精彩洞见,请关注微信公众号:思特沃克

Share

区块链在汽车金融行业的应用

[摘要]

本文从区块链在汽车金融行业的应用场景出发,对企业在寻找和确定应用场景时,应该如何进行价值和可行性的分析作出了说明,通过案例分享了ThoughtWorks区块链应用的落地实施经验,希望给其他领域的区块链应用提供方法上的参考和借鉴。

1. 区块链是什么

关于区块链技术的讨论文章已经足够多并且足够深入了,我们可能已经熟知它的各种特性——不可篡改、透明、可追溯、去中心化等等;我们也经常听到对于区块链各种共识机制的讨论——PoW、PoS、DPoS、BFT等等;我们甚至还知道它的不同应用——分布式账本、智能合约、Dapp等等;这些对于我们全面了解区块链技术都非常有帮助。当我们来到具体的商业场景中,想要应用区块链技术时,我们只需要关注区块链技术的两个核心功能就够了:记录保存和交易。

记录保存是一种静态信息的存储,比如在溯源、知识产权确权和专利保护等领域,就是应用区块链技术记录了公开透明且不可篡改的静态信息,保存在不同节点上以进行公开验证。记录保存的功能可以不需要代币实现。

而交易是动态的,例如跨境支付中,应用区块链提升点对点支付的效率,就是应用了交易功能。交易的场景一般需要代币来实现,如果没有代币,那就是资产对资产的交易(代币其实就是一种货币资产),区块链会对所有发生的交易行为进行动态登记。理论上,通过区块链的交易功能能够实现交易即账本,保证交易的有效性和安全性,同时还能免除第三方的背书见证。

2. 区块链的应用

2018年5月,随着工信部正式推出《2018 年中国区块链产业白皮书》,报告务实而系统地分析了中国区块链产业的现状,解析了中国当前区块链产业的生态,并且对区块链的发展趋势给出了坚定乐观的展望。下图勾勒出了目前应用区块链的产业生态(不包括数字货币),可以看出不论是金融领域还是实体经济领域,从底层的服务平台到不同领域的应用场景都已经形成了比较全面的覆盖,昭显出当前区块链技术发展的生机和前景。

(图1 来自工信部《2018 年中国区块链产业白皮书》)

关于数字货币和颠覆式创新的“通证经济(token economy,也有译为代币经济)”,从长期来看,商业模式上是可行的,但是需要人们对代币(token)的认知和心态产生改变,需要法律和监管政策的成熟完善,同时商业模式的过渡和转变也需要时间,因此短期来看,我们更倾向于认为,现阶段,企业应用区块链还是应该导向清晰可落地的价值,通过解决当前的业务痛点持续创造价值,才能进一步推动区块链技术的成熟和普及。

金融领域是当前区块链应用最有价值的场景之一,这一点已经取得广泛共识,在此不作过多论述。我们今天想要探讨的是,当我们拿起区块链这把锤子,来到某个具体的行业领域里,比如金融领域里某个非常细分的子领域——汽车金融领域,我们该怎么利用区块链这项技术,解决现有商业/社会环境下的问题和痛点,传递价值。

3 汽车金融行业

随着经济的发展和金融服务的日益成熟,虽然是细分金融领域,但是汽车金融涵盖的场景也已经非常多样化,领域内的参与主体竞争日渐激烈。下图是汽车金融产业内金融产品的简单概览。

(图2 源自华兴资本和逐鹿资本的报告《中国汽车金融研究(2017)》,略作修改)

不同的参与主体的商业模式不同,业务痛点必然也不尽相同,无法一概而论。为了让场景更加清晰,我们选取汽车金融公司作为应用场景来展开分析,这是因为:

  1. 在现在的汽车金融领域,汽车金融公司的发展势头最为迅猛,已经逐渐成为新车市场最主要的金融服务提供商;
  2. 相较于银行、P2P公司等提供通用金融服务的主体,汽车金融公司的业务模式更为专注和单一;
  3. 汽车金融公司拥有独特的优势可以覆盖汽车的全生命周期,可以拓展更多元的应用场景。

汽车金融公司的业务场景

(图3 汽车金融公司的业务场景)

以汽车的生命周期来看:从零配件的生产运输(还可以往上游延伸广阔的供应链金融场景),到主机厂制造整车,然后通过各个区域的销售公司,将整车卖给各区域内下沉的经销商,经销商还可以分为不同层级的二三级经销商,最后才卖到顾客手中。一旦新车完成销售,就迈入了后市场的广阔天地,其中包括二手车再销售、汽车租赁等等交易场景。

(图4 汽车金融公司的业务模式)

汽车金融公司的业务模式比较简单清晰,参考上图,1、2、3是汽车的批发,4、5是汽车的零售。金融公司参与给授信经销商提供贷款进行车辆的批发交易,零售的过程中,金融公司又继续给消费者提供消费金融贷款或融资租赁等服务,缓解用户购车的资金压力,促进汽车销售。因为两次贷款交易,我们可以看到这两条方向相反的资金线,财务上我们管这个叫轧差,也就是债务的互相抵消,而这两次贷款行为的轧差让金融公司以较低的净现金赚取了批发环节和零售环节的两次利息收入。

针对汽车金融公司的应用场景,我们可以简单总结业务痛点:

第一,提升资金利用率是业务关键。金融服务商以提供资金融通服务进行盈利,汽车金融公司的资金很大一部分是来源于汽车集团的财务公司,财务公司需要对现金流进出进行精准预测,以提高资金的利用率。传统的财务记账方式,无法实时透明地彰显资金的实时利用情况:比如有多少现金流即将产生,有多少资金出现了低效的浪费(重复贷款),造成了多少潜在损失(坏账)等等。

第二,财务对账主体数量较多,且效率不高。仅在中国区域内可能就有多家销售公司和金融公司,以及几百家经销商。从会计和审计角度,即使每家公司只有两名财务和审计人员,那么财务审计人员都超过一千名,更别提全球范围内了。

第三,信任主体的审核门槛较高。因为金融贷款要控制风险需要信贷审核,而金融服务机构的信审资源有限,审核流程繁杂且周期较长,经销商的销售网络又比较混乱,因此中小型经销商很容易成为“照顾不过来”的对象,造成经销商融资困难,同时也导致汽车金融公司的业务扩张受限。

区块链切入场景分析

如果从上一节的汽车金融业务痛点继续往下分析,我们从汽车金融交易的场景中可以轻易找出:供应链金融、消费金融、征信、财务审计等应用场景。

可是对于汽车厂商而言,金融并不是唯一的切入点,如果我们从车的视角出发,在车的使用场景上我们还可以发现以下场景同样可以用区块链解决信任成本问题:叫车出行、汽车租赁和共享、为二手车交易提供车史记录、零配件溯源、跟IoT和Big data结合的汽车驾驶行为记录、跟自动驾驶AI结合的智能出行等等。

于是现在很多企业应用区块链都面临这个问题:可用的场景太多了,怎么选择?如何确定这些场景的价值高低和优先级呢?我们可以从价值和可行性两个维度来对场景进行划分,然后选取高价值、高可行性的场景进行优先落地尝试。

(图5 区块链应用场景优先级区分)

随之而来的问题是,价值和可行性要如何度量?对于特定行业和场景,如果已经存在清晰的价值衡量标准和可行性度量方式,可以直接沿用。这里我们也简单介绍一套麦肯锡提供的通用分析框架,用来对多个场景进行比较分析。

我们从以下4个维度来分析价值,相信大家对自己行业的价值分析都不陌生,因此不做过多展开:

  • 收入:可以带来收入多大程度的提升?
  • 成本:可以带来成本多大程度的降低?
  • 资本:具备多大程度的资本效应?
  • 社会:带来多大程度的社会价值?

(图6 区块链的应用场景优先级 – 价值维度)

对于区块链技术可行性的判断,我们从以下四个维度略作说明:

  • 资产:资产数字化的可行性
  • 技术:当前技术可行性
  • 标准和监管:面临多大程度的标准化和监管,标准化和监管要求越高,可行性越低
  • 生态:是否可以帮助构建生态圈

(图7 区块链的应用场景优先级 – 可行性维度)

资产

资产类型决定了该项资产是否能够通过区块链来提升记账或交易的可行性,这里衡量的关键因素是资产的数字化潜力,比如原本就是数字化记录和交易的股票类资产,就可以轻松在区块链系统上实现端到端的管理,或是通过API与现有系统集成;像黄金这样的实物类金融资产就难以用数字化的身份来进行登记和转移。我们曾接触过一个木材行业的溯源项目,希望能用区块链实现行业内从家具到木材再到原木的溯源和证明,技术上第一个难以迈过去的关卡就是如何给树木一个唯一真实且不可篡改的数字化身份。

从某种程度上来说,货币确实是技术上最容易实现的上链资产。

实物资产上链是现在很热门的研究领域,通过IoT(物联网)技术和生物识别等技术将区块链的交易网络延伸至物理世界,这一步研究至关重要,但同时也有了更多的技术依赖。比如要如何保证通过传感器等物联网设备获得的数据在上链之前不被篡改?因此,即使能够很好地实现数字化,实物类资产的上链的可行性整体比数字资产更低一些。另外,还有一个经常与资产上链相提并论的研究领域,就是数字身份的建立。这对于数字资产的确权至关重要,只有当这两头并进延伸到现实中来,以区块链技术作为价值底座的数字世界才具备规模化扩张的基础。

技术

区块链技术的发展目前还处于起步阶段,我们甚至可以认为商业变革走在了技术发展的前面,这有利也有弊。好处在于,追逐商业变革的资本带来的资源投入,能够促进区块链技术的发展和突破;而硬币的另一面则是,追逐短期收益的资本,严重局限了资源在区块链行业内的分配,炒高了技术成本,甚至形成劣币驱逐良币的行业氛围。

区块链技术发展目前最大的挑战就是下面这个“不可能三角”:

(图8 区块链的“不可能三角”)

面对不同的场景,我们需要在这三者之间进行配置和平衡,同时还可以对上链数据进行适当的裁剪,以确保交易的效率和性能。不过我们相信,随着技术不断发展,这些约束将会越来越少。

通常来说,去中心(或者一定程度上去中心)的公链,在保证安全的前提下,只能牺牲性能,这也是比特币、以太坊现在的交易瓶颈所在。而对于支撑商业场景的联盟链来说,在去中心这个角上受到的约束较小,因此通过合适的配置,可以使得区块链在商业应用上变得可行。

当前各个国家对区块链的政策不同,所以不同地区对区块链技术的发展重心也不一样。国内政策倾向于联盟链的发展,腾讯、阿里、京东等领军企业目前也正专注于研发给企业提供的BaaS(区块链即服务)服务,从工信部的白皮书也可以看出大量创业公司正在进行各行各业联盟链的搭建,相信在短期内,联盟链的发展会比公链更快速有效。因此,在判断技术可行性时,底层链平台的成熟度是非常重要的衡量标准。

标准和监管

缺乏共同标准和明确的法律规定很大程度上限制了区块链应用能力的扩张。不过,现在不论是政府还是各个行业间组建的联盟都已经认识到这个问题,并且正在着手解决。例如,今年九月,最高人民法院发布的新法规中明确说明,在能够证明使用技术合法性的前提下,中国互联网法院承认区块链作为存储和认证数字证据的合法性。而如果政府机构可以授权法律地位,那么标准可以相对容易地建立起来。 例如,政府可以让区块链记录的土地登记成为合法的记录。

当需要多个参与者之间共同合作时,这个标准的建立就变得比较复杂,但也更加重要。比如 R3 联盟与全球70多家银行合作开发的金融开源区块链平台Corda,这些平台可以建立起特定行业内区块链系统所需的通用标准。

对于缺乏标准和监管的行业,可行性衡量的是建立标准的难度。而对于某些已经存在严格固定的标准和监管的行业,在这一点上面临的挑战可能会更大。比如会计和审计行业,早已存在清晰的会计和法律要求,我们都知道区块链对于审计行业的颠覆性影响,可是在法律对区块链账本审计有明确的规定和认可之前,企业必须维持原来的财务系统不变,那么投入在区块链财务记账上的成本很长时间内都不会取得任何回报,这对于企业来说也就更难推广,当然,会计师事务所不在此列,相反,他们是现在最积极推动区块链技术的企业之一。

生态

区块链的主要优势在于网络效应,但是随着网络规模的增加,其潜在收益会增加,协调的复杂性也会随之增加。例如,用于数字媒体、许可证和版税支付的区块链解决方案,就需要在数字内容的各个生产者和消费者之间进行大量的协调。如何构建竞争者和合作者之间的竞合关系,构建一个完整的价值激励的闭环,共同促进整个生态的发展,是衡量生态可行性的标准。如果竞合关系越复杂,那么构建生态的不确定因素就越多,可行性就越低。

应用这样的分析框架,我们就可以对不同场景的价值和可行性有一个相对清楚的认知。这套框架对于大多数追求经济价值的行业都是适用的,比如这里我们就可以筛选出供应链金融、征信和二手车交易三个场景进行优先尝试。

(图9 区块链应用高优先级的切入场景)

在此,我们建议企业在进行区块链应用的尝试时:

  1. 从降低成本、提高收益或者是解决信任问题的角度出发;
  2. 进行价值-可行性分析,对价值产生的周期形成清晰的认知;
  3. 快速验证,快速反馈,尽量缩短价值产生到获得回报的周期。

4 落地实施

当我们找出了合适的场景,开始着手进行区块链的尝试,我们面临的问题更大可能是在原有的业务上增加新的技术支持(如果商业模式不变的前提下)。对于怎么实施落地区块链,我们选取一个项目案例来作为落地实施的方法说明。

确定场景和参与主体

以我们在汽车金融领域实施的一个项目为例,场景是搭建横跨多品牌的汽车销售网络的联盟链,涵盖汽车从主机厂制造出厂到完成最终销售。汽车销售是交易行为,直接用区块链的交易功能实现汽车所有权/占用权的转移在流程体验上更为顺滑自然,但是现阶段想要推广落地则会面临政策上的风险,因此我们将实施分为两步:

第一步建链,主要包括:

  • 实现汽车资产上链
  • 交易节点的登记和身份确认
  • 记录汽车交易过程中产生的债
  • 集成银行系统,模拟交易主体之间的现金交易来更新债的信息

第二步则是等到可合法流通的电子货币产生后,再接入联盟链直接完成交易。

场景确认之后需要确定参与主体,也就是联盟链第一步需要部署的节点。这更多是一个业务问题,比如在我们这个案例中,我们需要梳理出所有参与汽车销售的主体,包括:不同品牌的主机厂、各区域销售公司、经销商、金融公司,考虑到汽车的运输,还可能会加上海运和物流公司。

上链数据识别

确认了参与主体,接下来我们要确认将哪些数据上链。对于汽车的销售来说,我们需要分析清楚的问题是车在什么时候转移,车在什么主体之间用什么样的规则转移,车在转移的过程中伴随了什么数据的变化。在分析这块业务的时候,我们尝试了事件风暴,分析了在各个法律参与实体之间发生车转移的业务事件,然后进行了事件排序,通过事件析出数据,包括交易参与方,车的详细信息,车的所有权和占用权以及债等等。

(图10 上链数据识别)

智能合约的设计

智能合约的概念最早在1994年由尼克萨博提出,是一种旨在以信息化方式传播、验证或执行合同的计算机协议。随着以太坊的流行,智能合约的概念也逐渐为大众熟知,未来随着技术的发展,围绕智能合约的想象空间将会越来越大。

智能合约是对上链数据演化过程的封装。对于复杂的金融类合约,往往涉及多种数据的同时改变,以经销商通过汽车金融公司贷款批发车为例,一个可能的合约模板是规定车辆转移的同时产生两笔债,一笔由经销商指向汽车金融公司,另一笔由汽车金融公司指向区域销售公司,以及注明还款截止日期。这个合约强制链上数据变化的同时,交易多方节点必须参与合约合法性的验证和签名。

可以看出,智能合约和现实场景下的买卖合约有很强的相似性。因此,业务上我们所定义的“如果……,当……时,就发生……”这样的场景,就可以通过智能合约来实现。

平台架构

区块链平台一般位于底层(技术上也可以将区块链理解成另一种数据的存储方式),上层应用所产生的数据通过API传送到区块链平台层,也通过API读取区块链平台上记录的账本。设计上,我们对区块链平台也同样进行了分层:上层是rest API层,将平台的能力进行抽象暴露给业务;中间是合约层,提供合约执行各种业务;最底层是账本层,分布式账本记录每笔交易发生的事实,可追溯、不可篡改。

(图11 平台分层架构)

传统的平台,通过API的方式暴露服务从而获得价值输入,但是区块链平台的核心资产其实在最底层的账本中。基于这些交易事实和债务或者支付记录,我们可以很方便清算各个法律实体的数字资产,计算实时的债务信息,进行车辆的价值溯源,而且未来结合大数据分析和AI,更有可能打造出一个完整的供应链生态。

5. 总结

通常来说,中心化解决方案相对于区块链这种去中心化的解决方案,既便宜又高效。那么也就意味着,区块链技术必定是用于传统中心化技术方案难以真正解决的商业场景,又或者是它的发展能带来更好的社会秩序。只有这样,区块链技术才能真正产生价值。我们希望通过这篇文章,分享我们对区块链在汽车金融这样一个细分行业和场景中的经验,给同样在尝试或想要尝试应用区块链的企业提供一些参考。

参考文档:

  • 工信部,2018 年中国区块链产业白皮书
  • Brant Carson, Giulio Romanelli, Patricia Walsh, Askhat Zhumaev (2018). Blockchain beyond the hype. Mckinsey.
  • 华兴资本&逐鹿资本,中国汽车金融研究(2017)

更多精彩商业洞见,请关注微信公众号:ThoughtWorks商业洞见

Share

领导梯队在ThoughtWorks中国

我们常宣称ThoughtWorks是一个扁平组织,并为此而自豪。不过最近有一位离职的ThoughtWorker写了一篇颇有影响的blog,文中表达了对ThoughtWorks的喜爱,却又不乏苦涩地诉说了这个扁平组织缺乏制度和执行力带来的“混沌”(Chaos)状态。ThoughtWorks从文化诉求到组织设计都希望尽量减少层级,然而扁平并不意味着应该缺乏领导力。

我按照领导梯队模型(The Leadership Pipeline: How to Build the Leadership Powered Company),梳理了一下我们的几类角色。有人可能会说领导梯队和管理层级看上去是如此相似,前者是不是只是用来美化后者的华丽包装?我的理解,这两者从责任上讲确有相似之处,但是在行为模式上,管理层级强调的是汇报线(Command Chain),领导梯队强调的是工作理念,而最后是人们的行为模式定义了一个组织的文化。

ThoughtWorks是一个有自己使命的组织,在履行使命的路途上更是需要有各种不同类型的优秀领导人物不断涌现出来。随着规模的增长、业务的多样化,我们看到更多这样的机会正在涌现。这些机会要求不少ThoughtWorker承担不同的领导角色,期望他们具备与之前不同的能力和行为模式。

个人贡献者

ThoughtWorks跟所有组织一样,大多数人必然都是个人贡献者。在ThoughtWorks这样的专业服务公司,一个人的贡献和对公司的重要性并不一定通过承担管理性质的角色来体现。如果看看ThoughtWorks话语体系里提到的各种牛人,他们有特定领域的技术专家或是阅历丰富的架构师,也有曾经的公司高管,今天的管理教练和组织转型顾问,其实大多都是个人贡献者。这也是为什么一直以来我们的职级体系并不会跟岗位挂钩。

大部分ThoughtWorker都希望能专注在专业服务领域做出一番成绩,但也有同事由于这样或是那样的原因,主动或是被动的进入到了一个“领导梯队”当中。

一线Lead (领导或经理)

一线lead通常是刚刚从个人贡献者的角色转变而来,其中很多都是第一次带团队。在我们的场景下,交付中心的Team lead,项目经理,运营部门functional团队的lead都是典型的一线lead。

ThoughtWorks的一线lead大多在作为个人贡献者的时候,既展现了出色的专业技能,同时又发挥了辅导小伙伴和与外部stakeholder沟通协调的能力,因而被选为团队的领导者。相对个人贡献者,这时仅仅依赖超强的专业技能或是个人解决问题的能力已不足够。一线lead要帮助团队定目标,做计划,保执行,还要通过持续的反馈和其它团队建设手段来提升团队的效能和氛围。

另一个容易被察觉的不同是时间管理。他们相当的时间会用于各种不同的沟通活动,比如一对一辅导团队成员、团队计划、回顾会议,还有跟客户、销售、分公司总经理等外部相关方的沟通,一不小心就发现自己陷入到了讨厌的文山会海当中。然而如果回避这些沟通活动,却又很难履行Lead的责任。与此同时,工作理念上的一个转变是需要把工作的重心从自己胜任专业任务更多地转到帮助小伙伴的成功上。

很多同事在这样的角色上尝试锻炼了一段时间后,会根据自己的热情所在,决定是以领导管理角色作为职业发展方向,还是转向业务专家。一线Lead是ThoughtWorks领导梯队的后备军。

部门Head

ThoughtWorks的部门Head一般包含分公司总经理(Office Principal),成熟BU的Head,功能部门Head。他们要负责本部门的发展策略、资源配置、人员的招聘和培养,以及跟其它业务线和部门的协调,更要确保本部门的发展符合公司战略和运营的规划和期望。

我们的一线经理通常不会脱离一线具体业务工作,PS的同事仍然战斗在具体项目上,运营的同事大多数时间也还在处理着相关业务的日常事务。但是对于部门Head来说,他们一般是全职的运营管理人员,只是在特殊时刻或是在一些关键业务上,才会直接承担业务工作,就好像有时候我们看到OP也会过渡性地承担战略客户的交付经理。

判断一个部门Head是否成功的依据,通常要么是直接对标其它Region的相同部门或是业内优秀公司,实现卓越水准。要么就要寻求创新模式,制造跳跃式的差异化优势。我个人的观察是我们的部门Head似乎更倾向于后者的方式。People团队运用微信提供员工支持,协同拉勾网发动创意招聘攻势。本地交付团队积极拓展新型业务开拓新的细分高端市场。UX团队发展出了设计中心服务包。海外交付中心则发挥我们学习型组织的优势,用各种创新和能力提升活动,积极为客户提供更具价值的服务。

部门Head常会面临一个矛盾是局部优化和全局优化。实现部门的直接目标容易遮蔽部门Head另一个不是那么明显的责任,就是作为部门当中拥有公司上下文最多的人,打破部门边界,促进信息和想法在部门间的流动,跟其他部门head以及MD一起做出全局最优的判断和决定,哪怕这样的决定会增加本部门的工作难度,甚至对自己的工作目标有所伤害。不少同事肯定都有这样的记忆,客户要求紧急启动项目,各个办公室、客户团队和业务线不管项目是否跟自己直接相关,都愿意承受巨大压力,左右腾挪,把需要的人员送到现场。

事业部MD

现在ThoughtWorks的事业部基本是按照地区来划分的,大多数事业部的MD都是各个国家和地区 MD。事业部MD的长期目标是树立本事业部的业务愿景,通过愿景的实现来履行公司的使命。我们去年梳理了2022年中国区的愿景,并期望从全新的业务模式带动高速增长、更具影响力的品牌、资源丰富的周边生态系统三个维度达成这个愿景,最后我们以路演的方式在各个office和大家面对面交流这些内容。

MD日常的关注点则是推动变革来达成愿景里设计的To-Be状态。胡凯和我已经开始通过一系列的活动来逐步落实2022愿景里的那些内容,这些活动会覆盖组织结构和协作机制的设计,不过更重要的是发现和发展各种带头人,启动和推进相应的各项投资。

要做到上面的几点,MD还有一个最基本的工作就是要确保本region的业务安全。业务安全的一个条件是盈利目标和增长目标的达成。由于公司全球投资战略和运营规划依据的是各个Region的盈利计划,如果任何一个成熟Region的MD如果没能达成业务目标,都有可导致整个公司花钱的速度快过赚钱的速度,从而给可持续运营带来风险。

最后,不管是个人职业发展需要,希望通过借助更大的团队和平台做些一个人干不了的事情,还是为了达成公司的期望和要求,我希望更多的ThoughtWorker勇敢地站出来,抓住机会,承担起更大的责任。

MD心声

过去几次,我发现总会从大家的讨论中得到很多启发,所以这次还是抛出三个我还在思考的问题,希望听到大家的看法:

  1. 承担领导角色的一个重要特质是有担当,如何鼓励在公司里形成有担当的氛围?
  2. 卓越的专业能力和贡献跟领导角色是否冲突?
  3. 鼓励大家当领导是否会形成官本位的倾向?

更多精彩洞见,请关注微信公众号:思特沃克

Share

写了这么多年代码,你真的了解SOLID吗?

尽管大家都认为SOLID是非常重要的设计原则,并且对每一条原则都耳熟能详,但我发现大部分开发者并没有真正理解。要获得最大收益,就必须理解它们之间的关系,并综合应用所有这些原则。只有把SOLID作为一个整体,才可能构建出坚实(Solid)的软件。遗憾的是,我们看到的书籍和文章都在罗列每个原则,没有把它们作为一个整体来看,甚至提出SOLID原则的Bob大叔也没能讲透彻。因此我尝试介绍一下我的理解。

先抛出我的观点: 单一职责是所有设计原则的基础,开闭原则是设计的终极目标。里氏替换原则强调的是子类替换父类后程序运行时的正确性,它用来帮助实现开闭原则。而接口隔离原则用来帮助实现里氏替换原则,同时它也体现了单一职责。依赖倒置原则是过程式编程与OO编程的分水岭,同时它也被用来指导接口隔离原则。关系如下图:

单一职责原则(Single Responsibility Principle)

单一职责是最容易理解的设计原则,但也是被违反得最多的设计原则之一。

要真正理解并正确运用单一职责原则,并没有那么容易。单一职责就跟“盐少许”一样,不好把握。Robert C. Martin(又名“Bob大叔”)把职责定义为变化原因,将单一职责描述为 ”A class should have only one reason to change.” 也就是说,如果有多种变化原因导致一个类要修改,那么这个类就违反了单一职责原则。那么问题来了,什么是“变化原因”呢?

利益相关者角色是一个重要的变化原因,不同的角色会有不同的需求,从而产生不同的变化原因。作为居民,家用的电线是普通的220V电线,而对电网建设者,使用的是高压电线。用一个Wire类同时服务于两类角色,通常意味着坏味道。

变更频率是另一个值得考虑的变化原因。即使对同一类角色,需求变更的频率也会存在差异。最典型的例子是业务处理的需求比较稳定,而业务展示的需求更容易发生变更,毕竟人总是喜新厌旧的。因此这两类需求通常要在不同的类中实现。

单一职责原则某种程度上说是在分离关注点。分离不同角色的关注点,分离不同时间的关注点。

在实践中,怎么运用单一职责原则呢?什么时候要拆分,什么时候要合并?我们看看新厨师在学炒菜时,是如何掌握“盐少许”的。他会不断地品尝,直到味道刚好为止。写代码也一样,你需要识别需求变化的信号,不断“品尝”你的代码,当“味道”不够好时,持续重构,直到“味道”刚刚好。

开闭原则(Open-closed Principle)

开闭原则指软件实体(类、模块等)应当对扩展开放,对修改闭合。这听起来似乎很不合理,不能修改,只能扩展?那我怎么写代码?

我们先看看为什么要有开闭原则。假设你是一名成功的开源类库作者,很多开发者使用你的类库。如果某天你要扩展功能,只能通过修改某些代码完成,结果导致类库的使用者都需要修改代码。更可怕的是,他们被迫修改了代码后,又可能造成别的依赖者也被迫修改代码。这种场景绝对是一场灾难。

如果你的设计是满足开闭原则的,那就完全是另一种场景。你可以通过扩展,而不是修改来改变软件的行为,将对依赖方的影响降到最低。

这不正是设计的终极目标吗?解耦、高内聚、低耦合等等设计原则最终不都是为了这个目标吗?畅想一下,类、模块、服务都不需要修改,而是通过扩展就能够改变其行为。就像计算机一样,组件可以轻松扩展。硬盘太小?直接换个大的,显示器不够大的?来个8K的怎么样?

什么时候应该应用开闭原则,怎么做到呢?没有人能够在一开始就识别出所有扩展点,也不可能在所有地方都预留出扩展点,这么做的成本是不可接受的。因此一定是由需求变化驱动。如果你有领域专家的支持,他可以帮你识别出变化点。否则,你应该在变化发生时来做决策,因为在没有任何依据时做过多预先设计违反了Yagni

实现开闭原则的关键是抽象。在Bertrand Meyer提出开闭原则的年代(上世纪80年代),在类库中增加属性或方法,都不可避免地要修改依赖此类库的代码。这显然导致软件很难维护,因此他强调的是要允许通过继承来扩展类。随着技术发展,我们有了更多的方法来实现开闭原则,包括接口、抽象类、策略模式等。

我们也许永远都无法完全做到开闭原则,但不妨碍它是设计的终极目标。SOLID的其它原则都直接或间接为开闭原则服务,例如接下来要介绍的里氏替换原则。

里氏替换原则 (The Liskov Substitution Principle)

里氏替换原则说的是派生类(子类)对象能够替换其基类(父类)对象被使用。学过OO的同学都知道,子类本来就可以替换父类,为什么还要里氏替换原则呢?这里强调的不是编译错误,而是程序运行时的正确性。

程序运行的正确性通常可以分为两类。一类是不能出现运行时异常,最典型的是UnsupportedOperationException,也就是子类不支持父类的方法。第二类是业务的正确性,这取决于业务上下文。

下例中,由于java.sql.Date不支持父类的toInstance方法,当父类被它替换时,程序无法正常运行,破坏了父类与调用方的契约,因此违反了里氏替换原则。

package java.sql;

public class Date extends java.util.Date {
    @Override
    public Instant toInstant() {
        throw new java.lang.UnsupportedOperationException();
    }
}

接下来我们看破坏业务正确性的例子,最典型的例子就是Bob大叔在《敏捷软件开发:原则、模式与实践》中讲到的正方形继承矩形的例子了。从一般意义来看,正方形是一种矩形,但这种继承关系破坏了业务的正确性。

public class Rectangle {
    double width;
    double height;

    public double area() {
        return width * height;
    }
}

public class Square extends Rectangle {
    public void setWidth(double width) {
        this.width = width;
        this.height = width;
    }

    public void setHeight(double height) {
        this.height = width;
        this.width = width;
    }
}

public void testArea(Rectangle r) {
    r.setWidth(5);
    r.setHeight(4);
    assert(r.area() == 20); //! 如果r是一个正方形,则面积为16
}

代码中testArea方法的参数如果是正方形,则面积是16,而不是期望的20,所以结果显然不正确了。

如果你的设计满足里氏替换原则,那么子类(或接口的实现类)就可以保证正确性的前提下替换父类(或接口),改变系统的行为,从而实现扩展。BranchByAbstraction绞杀者模式 都是基于里氏替换原则,实现系统扩展和演进。这也就是对修改封闭,对扩展开放,因此里氏替换原则是实现开闭原则的一种解决方案

而为了达成里氏替换原则,你需要接口隔离原则。

接口隔离原则 (Interface Segregation Principle)

接口隔离原则说的是客户端不应该被迫依赖于它不使用的方法。简单来说就是更小和更具体的瘦接口比庞大臃肿的胖接口好。

胖接口的职责过多,很容易违反单一职责原则,也会导致实现类不得不抛出UnsupportedOperationException这样的异常,违反里氏替换原则。因此,应该将接口设计得更瘦。

怎么给接口减肥呢?接口之所以存在,是为了解耦。开发者常常有一个错误的认知,以为是实现类需要接口。其实是消费者需要接口,实现类只是提供服务,因此应该由消费者(客户端)来定义接口。理解了这一点,才能正确地站在消费者的角度定义Role interface,而不是从实现类中提取Header Interface

什么是Role interface? 举个例子,砖头(Brick)可以被建筑工人用来盖房子,也可以被用来正当防卫:

public class Brick {
    private int length;
    private int width;
    private int height;
    private int weight;

    public void build() {
        //...包工队盖房
    }

    public void defense() {
        //...正当防卫
    }
}

如果直接提取以下接口,这就是Header Interface:

public interface BrickInterface {
    void buildHouse();
    void defense();
}

普通大众需要的是可以防卫的武器,并不需要用砖盖房子。当普通大众(Person)被迫依赖了自己不需要的接口方法时,就违反接口隔离原则。正确的做法是站在消费者的角度,抽象出Role interface:

public interface BuildHouse {
    void build();
}

public interface StrickCompetence {
    void defense();
}

public class Brick implement BuildHouse, StrickCompetence {
}

有了Role interface,作为消费者的普通大众和建筑工人就可以分别消费自己的接口:

Worker.java
brick.build();

Person.java
brick.strike();

接口隔离原则本质上也是单一职责原则的体现,同时它也服务于里氏替换原则。而接下来介绍的依赖倒置原则可以用来指导接口隔离原则的实现。

依赖倒置原则 (Dependence Inversion Principle)

依赖倒置原则说的是高层模块不应该依赖底层模块,两者都应该依赖其抽象。

这个原则其实是在指导如何实现接口隔离原则,也就是前文提到的,高层的消费者不应该依赖于具体实现,应该由消费者定义并依赖于Role interface,底层的具体实现也依赖于Role interface,因为它要实现此接口。

依赖倒置原则是区分过程式编程和面向对象编程的分水岭。过程式编程的依赖没有倒置,A Simple DIP Example | Agile Principles, Patterns, and Practices in C#这篇文章以开关和灯的例子很好地说明了这一点。

上图的关系中,当Button直接调用灯的开和关时,Button就依赖于灯了。其代码完全是过程式编程:

public class Button {   
    private Lamp lamp;   
    public void Poll()   {
        if (/*some condition*/)
           lamp.TurnOn();   
    } 
}

如果Button还想控制电视机,微波炉怎么办?应对这种变化的办法就是抽象,抽象出Role interface ButtonServer:

不管是电灯,还是电视机,只要实现了ButtonServer,Button都可以控制。这是面向对象的编程方式。

总结

总的来说,单独应用SOLID的某一个原则并不能让收益最大化。应该把它作为一个整体来理解和应用,从而更好地指导你的软件设计。单一职责是所有设计原则的基础,开闭原则是设计的终极目标。里氏替换原则强调的是子类替换父类后程序运行时的正确性,它用来帮助实现开闭原则。而接口隔离原则用来帮助实现里氏替换原则,同时它也体现了单一职责。依赖倒置原则是过程式编程与OO编程的分水岭,同时它也被用来指导接口隔离原则。


更多精彩洞见,请关注微信公众号:思特沃克

Share