8.1.8 伪创新
有的人(国内国外都有)没有掌握相应技能,也不愿意认真学习已有的知识,凭着一些朦胧的“领悟”,就“发明”了一些“新”方法,这就是伪创新。这样的人,国内国外都有。
软件开发的一些伪创新前些年打的是“敏捷”的旗号,最近几年打的是“领域驱动设计”的旗号。仔细观察,背后推动的人很多是重叠的。
8.1.8.1 伪创新例一
图8-17摘自2017年出版的某本名字中带有“Domain-Driven Design”的书,我们来挤一挤这张图里面的水分。
图8-17 摘自2017年出版的某本名字中带有“Domain-Driven Design”的书
首先,我们分析一下图8-17中各个元素的词性,如图8-18。图形的边上流动的“事件”实际上是“事件记录”或“事件信息”,是“数据”(名词)。
图8-18 图中各元素的词性
咦?这不就是UML的活动图吗?直接用活动图表达不是更省事,干嘛非得自己“发明”一套?
我们用UML活动图“复刻”一下图8-17,得到图8-19。
图8-19 用活动图“复刻”图8-17
注意,“Workflow”、“event”、“command”这几个词,作者有的大写开头,有的小写开头,可能是为了通过差异化保持百花齐放的“创新”感吧。
借助UML的语义,“Workflow”可以用活动表示,“event”可以用输出针脚表示,“command”可以用输入针脚表示,这样,“Workflow”、“event”、“command”等后缀就可以删掉了,得到图8-20。
图8-20 删掉后缀
接下来再看“queue”,意思是事件通过一个队列传递(目的是解耦发送和接收),这个做法适用于各种不同核心域的系统,和“Place Order”、“Ship Order”所在的下单配送并不直接相关。把这个知识混进来,结果必然是废话刷工作量,如图8-21。
图8-21 批量刷queue
把非核心域知识“queue”删去,得到图8-22。
图8-22 删去非核心域知识
我们再观察图8-22,发现原来它在玩颠来倒去的文字游戏。用三个词:Place、Ship、Order,再加一个横杠符号“-”,换着花样组合,刷出了一批工作量。
组合技巧一:动词-名词 “Workflow”,放在活动(结点)处。得到“Place-Order Workflow”和“Ship-Order Workflow”。
组合技巧二:名词-动词ing,放在泳道处。得到Order-Taking和Shipping。注意,该图作者在此处做了“创新”——换词和删词,不用Order-Placing,而是用Order-Taking(换词),不用Order-Shipping,而是用Shipping(删词),这样看起来就有了“新意”,不太像刷工作量了。
组合技巧三:名词动词ed(注意,此处没有“-”了,又有了“新意”) “event”,放在活动的输出针脚处。得到“OrderPlaced event”。
组合技巧四:动词名词(注意,此处没有“-”了,又有了“新意”) “command”,,放在活动的输入针脚处。得到“ShipOrder command”。
剥去各种包装后,我们可以把这张图简化为图8-23。
图8-23 图8-17的终极简化
然后这样约定:
约定1:必要时可以按照以上归纳的四个组合技巧刷工作量。
约定2:通过队列传递事件。
这两条约定可以应用于各个领域。我们还可以进一步“创新”,给这两条约定起一个高大上的名字,例如“分布式区块链云计算智能大数据智能平台敏捷领域驱动设计芜湖起飞架构”,最好找网红老外起名,然后出口转内销。
了解了这个套路,我们就可以熟练地摸鱼了。
首先,拍脑袋“功能分解”,得到类似图8-23;然后,按照套路刷工作量,把图8-23刷成图8-17。
我们以核酸检测为例,按照以上技巧演变看看:
图8-24 按照套路刷工作量示例
8.1.8.2 伪创新例二
图8-25摘自2019年出版的另外一本名字中带有“Domain-Driven Design”的书。展示的就是打着“领域驱动设计”旗号的伪创新之一:事件风暴(EventStorming)。
图8-25 摘自2019年出版的某本名字中带有“Domain-Driven Design”的书
我把图8-25里提到的概念提炼出来,画了1个类和4个小人,如图8-26。数一数,包括类名称在内,图8-26一共有16个概念。
8-26 我提炼图8-25的概念画的图
有了图8-26,可以准备开车……不,准备刷工作量了!
(1)创建对象,销毁对象,刷2个蓝色纸片,就是图8-25中的Createan ad和Remove ad(怎么前面没有an了,刷得不整齐,重刷!)。
这个步骤刷出2个蓝色纸片。
(2)多重性为1的属性(从图8-25看应该是title、text和sell price),每个刷1个蓝色纸片,就是图8-25中的Change the ad title(怎么不和后面两个一致都用Update,刷得不整齐,重刷!)、Update the ad text和Update ad sell price(怎么前面没有the了,刷得不整齐,重刷!)。
这个步骤刷出3×1=3个蓝色纸片。
另外,本来这些都是Ad的属性,直接称title、text和sell price即可,不必再加前缀,但不加怎么能刷出工作量呢?一定要加!
(3)多重性为多的属性(从图8-25看应该是picture和category),每个刷2个蓝色纸片,Add***和Remove***。
这个步骤刷出2×2=4个蓝色纸片。
另外,本来这些都是Ad的属性,图8-25中写Add***和Remove***即可,不用加to Ad、from Ad,但不加怎么能刷出工作量呢?一定要加!
(4)每个操作刷1个蓝色纸片。
这个步骤刷出6×1=6个蓝色纸片。
咦?这些改变状态的操作看着怎么和属性没有什么关系呢?是属性漏了,还是操作错了?估计作者也不太了解操作、状态和属性的关系吧。
(5)把(1)-(4)步产出的所有的蓝色纸片变换词序,每个旁边加一个橙色纸片。
这个步骤结束后,得到(2 3 4 6)×2=30个方框形状的纸片,或者说,15对。
(6)把图8-25中的4个小人和每对方框任意组合,得到大约15个黄色小人。
最终,我们从往墙上贴了30 15=45张小纸片,数量和内容正好和图8-25中的纸片相同。
信息浓度=16/45×100%=36%。再考虑到那些刷上去的Ad,估计差不多33%的浓度。或者说,图8-25的“事件风暴”中,有67%的水分内容。
要注意,这仅仅是其中一个类Ad,其他类照此办理,今年的工作量可算是有交代了。
是不是创始人英明神武,只不过其他人把经念歪了?感兴趣者可以去自行看“事件风暴”的作者Alberto Brandolini的书,看看书里面讲了什么。
8.1.8.3 伪创新为什么受欢迎
比起严谨的建模方法,伪创新更受欢迎,因为它迎合了“广大开发人员”呆在舒适区的需要。
如本书所言,软件开发的一个迭代周期中的四个工作流:
A-业务建模——定位需要改进的目标组织(人群或机构)以及该组织接下来最需要改进的问题。
B-需求——描述为了改进组织的问题,所引入的信息系统必须具有的整体表现。
C-分析——提炼为了满足功能需求,所引入的信息系统需要封装的核心域机制。
D-设计——考虑质量需求和设计约束,将核心域机制映射到选定非核心域上实现。
很多开发人员只有D的知识,当岗位发生变化,需要他做A、B、C的工作时,按道理应该去认真学习A、B、C的技能才对。
但是,很多人并不愿意走出自己的舒适区,甚至还会有意无意地把其他人拉到自己的舒适区。
例如,在和涉众讨论需求时,频频蹦出一些“技术潮词”,目的就是以自己的“所长”来碾压涉众,从而掩盖自己业务建模和需求技能的不足。
例如,在讨论核心域的类模型时,动不动就谈到如何实现或者质疑“会不会有性能问题”,从而掩盖自己抽象能力的不足。
于是,投其所好的伪创新就登场了。
最开始的伪创新极力贬低A、B、C的重要性。例如,想那么多有啥用,最后不是还得写代码?张嘴就是Linus Torvalds的“Talk is cheap. Show me the code.”。
这就是之前的“敏捷”论调,高喊“砸烂一切枷锁”来吸引热血程序员。
但是,事实是残酷的。“发明家”及其追随者慢慢发现砸烂一切是不行的,追随者的信念开始动摇。
于是,新一代伪创新就登场了。
新一代伪创新不再贬低A、B、C,而是从D来臆想A、B、C,得到的A、B、C“方法学”非常“简单易学”。
不过,伪创新往往不会直接宣传自己“简单易学”,而是会说自己很高深。宣传中往往带有“艺术”、“禅”、“道”等字眼,有意无意地朝宗教、艺术、玄学方向引导——比起枯燥的数学理论和逻辑推理,这些东西可是太好下嘴了。
开发人员上手一学,发现其实不难!就像上面举的两个刷工作量的例子一样,投资少,见效快,产量大,而且仪式感十足。
最妙的是,不用走出舒适区辛苦学习,就得到了“方法学”,这可太符合只掌握D的开发人员的胃口了!开发人员有捡到了便宜的感觉,心中豪气顿生——不愧是我!别整三岁的,有能耐你整四岁的!
8.1.8.4 伪创新特点
造词
为了防止有人怀疑“天下哪有免费的午餐”,伪创新往往会造一个新名字,让人有“取得突破”的感觉,以进一步坚定追随者的信心。
造词手段可以是换词。例如,把“术语表(Glossary)”换成“通用语言(Ubiquitous Language)”。
造词手段可以是拼词。例如,在“愿景(Vision)”前面加上“领域(Domain)”,得到“领域愿景”(Domain Vision)。
如果人们得知一个东西曾经存在过,那么当这个东西再次被拿出来宣传时,人们会对宣传持有较多的理性,“这东西如果真的这么厉害,那之前怎么……”,宣传的人也会收敛,不至于那么夸张。
如果换一个名字,就会给人一种“全新”的感觉,人们可能就会给一个机会,毕竟是“新”的,没准人家真的有这么牛呢。
例如,说青霉素可以治愈肝癌,大众肯定不信,要是真的可以治愈肝癌那么多年不早就验证了嘛;如果把青霉素改个名字叫“K9527-α”,说可以治愈肝癌,可能就会有患者给它一个机会,买了试一试。
一个机会就够了!大不了这个词不好使了再换个词呗!
并不是说青霉素没有价值,杀灭细菌总是可以的。就算是淀粉做的假药,还可以起到充饥的作用呢。
危害在于,被误导的肝癌患者可能会将宝贵的金钱和时间资源优先用在了“K9527-α”上,耽误了获得更好治疗方案的机会。
割裂历史
伪创新会有意无意地忽略掉自己出现之前的某段历史,来证明自己的“进步”。
例如,“敏捷”的一些宣传中,一开始会描述“瀑布”如何如何糟糕,然后说“敏捷”如何如何比“瀑布”好,如图8-27:
图8-27 网络截图
把“敏捷”直接和“瀑布”对比,似乎在“敏捷”出现之前就是“瀑布”。
实际上,增量迭代开发、持续集成等软件工程实践早已有之,甚至于图8-27中提到的著名“瀑布”论文,Winston W. Royce发表于1970年的“Managing the Development of Large Software Systems”,也并不提倡以顺序模型作为团队的过程模型。图8-28是该文的一个截图,注意图中的“Iterative(迭代)”一词。对全文感兴趣的读者可自行搜索阅读。
图8-28 摘自Managing the Development of Large Software Systems(Royce W. W. , 1970)
这样的宣传,抹杀了造词“敏捷”之前的软件开发过程所取得的进展,给不了解情况的开发人员留下“这是敏捷发明的,所以嘴上喊着敏捷的人最懂”的印象。
“领域驱动设计”的宣传也有这种误导,如图8-29。
图8-29 网络截图
把“面向过程”、“CRUD”直接和“领域驱动设计”对比,似乎“领域驱动设计”之前就是一片空白。
包括Eric Evans也在《领域驱动设计》的前言中说:
Leading software designers have recognized domain modeling and design as critical topics for at least 20 years,yet surprisingly little has been written about what needs to be done or how todo it. Although it has never been formulated clearly, a philosophy has emerged as an undercurrent in the object community, a philosophy I call domain-driven design.
领先的软件设计人员认识到领域建模和设计的关键性已经有至少20年,然而令人惊讶的是,关于需要做到什么或者如何做,一直以来几乎没人写点什么。不过,一种哲学像一股暗流已经在对象社群出现,虽然还从来没有被清晰确切地表述出来。我把这种哲学叫作“领域驱动设计”。
Eric Evans说的20年,指《领域驱动设计》出版时间2003年前面的20年,大约是1983-2002年。
事实和Eric Evans所说的在这个期间“几乎没人写点什么”恰好相反,那个年代的“领域驱动”味道比今天还要浓,如果没有涌现各种面向对象方法学,UML是怎么来的?
关于面向对象方法学和UML的历史,可参见本书第1章。对Eric Evans以上言语的批评,可参见我写的文章《DDD浮夸,Eric Evans开了个坏头》(https://mp.weixin.qq.com/s/fzRG27uyDSWtNN9thi6Lrw)。
*忽略自己出现之前的进展,把对比的参照物拉回到很多年前,让人联想到穿越小说,主人公回到古代发明肥皂香水水泥火药来碾压对手。*
实在绕不开历史,需要引用文献时,伪创新也会优先把引用停留在“圈子”内,例如Rob*** Series、Mar*** Series,对“圈子”外的贡献所知甚少或视而不见。感兴趣的读者可以看看你手上的带有“敏捷”、“领域驱动设计”名字的书,关注一下它的参考文献。
图8-30 封闭圈子引用
例如,Eric Evans《领域驱动设计》的参考文献共25篇,图8-31是前17篇。
图8-31 摘自《领域驱动设计》(EvansE.,英文原版出版于2003年)
可能有的读者会说,咦,这些好像都挺有名的呀!是的,很多都在网红圈子中。
可惜,软件开发不是娱乐业,流量为王。某句话、某篇文章或某本书在某个领域“最有名”,不代表它在该领域“最正确”或“最深刻”。
门槛低
对伪创新圈子而言,“创新”来得非常容易。不用认真学习前人的成果,把自己的一些朦胧感悟写出来,换个新词,就是“创新”了。
有的人甚至是这样“创新”的:我和公司同事某某(也是网红)讨论了什么什么,就悟到了某某创新。
*你的问题在于读书不多,而想得太多。(杨绛)*
了解前人已有的成果,前人曾经想过什么,做过什么,哪些成功了,哪些失败了,才能提出并解决真正的问题。
不了解前人成果,蒙头就得到的,只能叫“学习体会”、“学习感悟”——这些文字里,常会见到“我发现”、“我悟到了”等字眼。
图8-32 学习体会和创新的区别
发表学习体会,包括错误的学习体会,当然没问题,这是个人自由,只要不把学习体会包装成“创新”就好。
***********
初中数学里要学习全等三角形、相似三角形、SSS、SAS……,到了高中以后学了正弦定理、余弦定理等解三角形的知识……就不会再回去用初中的方法解题了。
但是,不是所有人都能学会高中的知识,比如说张三。
张三可能会这样解释:
“我这个人能力比较弱,只能掌握全等三角形、相似三角形的方法。”
这样的说法没有问题。
张三还可能会这样解释:
“这个题目比较简单,用全等三角形、相似三角形的方法做足够了,而且这样更方便广大人民群众理解。”
这样的说法也可以。不过,竞争对手不是傻子,市场中哪里有什么"简单题目"!能带来利润的题目都很复杂。
但是,张三如果这样说:
“全等三角形、相似三角形的知识比高中三角函数的知识更深刻。”
这就是自欺欺人了。
更要警惕的是,有一个李四,也许和张三一样没有掌握高中方法,也许掌握了高中方法但是为了忽悠张三们,偷偷把"全等三角形"改名为"叠合三角形",然后和张三宣传:
“我发明了"叠合三角形"新方法,比高中的三角函数有用,三角函数过时了。”
这就是可恶了。
***********
8.1.9 本书使用的分析方法
分析模型描述系统要封装的核心域知识。
至于用什么建模概念来思考和描述核心域知识,可以有很多种选择。例如,“人”用不同的建模概念描述,可以说它是一个“类”,也可以说它是一个“类型”、一个“实体”。
本书使用面向对象的建模概念来描述分析模型,从三个视角来描述:
分析类模型:描述系统中各个类以及类之间的关系。
分析状态机模型:描述某个类的各个行为的逻辑。
分析交互模型:描述某些类在实现某个用例时的协作。
而面向对象的分析模型的表达形式,也可以有多种选择:语音、文本、图形等。
有的人觉得当前许多编程语言的表达能力已经很强,认为用文本已经足够,抗拒用图形来表达领域知识。
但是,和只有自上而下顺序的文本相比,能够朝四个方向扩展的平面图形(如果有三维模型就更好了)更容易让建模人员看出领域概念之间的联系。例如,图8-33和8-34的内容,如果没有图形的帮助,直接用文本一行一行地构造分析模型,人脑的负担非常重。
图8-33 餐饮领域的类图
图8-34 计算器的状态机图,摘自PracticalUML Statecharts in C/C (Samek M. , 2008)
说到这里,又不可避免地要提醒,故意选择文本的形式来表达领域知识,有可能也是一种遮羞利器。图8-33和8-34的内容如果用文本表达,可能会得到很多页文本——这就有了理由:因为工作量太大了,所以很多地方无法做深入的思考,可以原谅!
本书使用类图、状态机图和序列图三种UML图形来表达面向对象的分析模型。UML类图表达分析类模型,UML状态机图表达分析状态机模型,UML序列图表达分析交互模型。
图8-35 本书的分析方法所使用的UML图形
需要说明的是,虽然我们用的是面向对象的分析方法,也就是说,用面向对象的概念来剖析核心域知识,但不意味着你的系统一定要用特定的“面向对象”编程语言、特定的存储方式或物理分布形式来实现。
也许你使用的编程语言是面向过程语言,例如C;也许你使用的编程语言是函数式语言,例如F#;也许你使用的存储系统是关系数据库系统,例如SQL Server;也许你使用的存储系统是非关系数据库系统,例如MongoDB;也许你的系统运行在同一台机器上,也许是分布在很多台机器上……
不管你的系统的实现方式和运行形态如何,从分析过渡到设计时,变化的只是分析到设计的映射套路。如果设计所使用的非核心域比较“面向对象”,那么映射套路会比较直观一些,否则,就需要一定的转换。但无论如何,如前文所说,这个套路和具体的核心域知识没有关系,我们并不需要针对每一个核心域概念逐一花费脑力去思考它。
我们之所以选择在分析工作流使用面向对象的分析方法,是因为从思考深度和表示的严谨程度来看,面向对象的分析方法以及UML表示法目前仍然是剖析和整理核心域逻辑的最佳选择。
本书在设计工作流的内容,会展示分析模型和各种实现方式的映射套路。
自测题
扫码或访问http://www.umlchina.com/book/quiz8_1_1.html完成在线测试,做到全对以获得答案。