精华内容
下载资源
问答
  • 训练思考能力的数学书》讲述你害怕数学吗?你觉得数学是天才发明来找人麻烦的东西吗?1+2+3+4+……+9899100=?如果是你,会怎么计算这条算式?当老师说:“请算出1到100的总和”,7岁的天才高斯不用几秒钟就说出了...
  • 学习者通过不断练习使用思考工具,能够逐步掌握思考方法和培养思考的习惯,最终提高思维能力。全球的海量实践证明,经过训练的学生无论是思考问题和解决问题的能力,还是学习成绩都有显著提高。 在全球范围内,...
  • 创造思维方法训练

    2018-11-10 09:42:49
    《创造思维方法训练(第2版)》不同于普通的教科书。在写作时,作者有意把最新科学技术、文学知识和历史故事融为一体,具有趣味性和可读性。并且,每一章后边附有训练思考题,便于学习者进行自我训练方法比知识重要...
  • 创新思维能力训练.pptx
  • 创新思维能力训练课件.pptx
  • 物理解题思维能力训练论文.doc
  • 力学解题思维能力训练论文.doc
  • Educoder题目:Python 计算思维训练——公式编程答案解析.md
  • 第二部分创新思维能力训练.pptx
  • 五年级数学下册思维能力训练题精选.doc
  • 小学六年级奥数教育练习六尖子班综合思维能力训练(四) 小学六年级奥数教育练习六尖子班综合思维能力训练(四)
  • 小学六年级奥数教育练习六尖子班综合思维能力训练(四) 小学六年级奥数教育练习六尖子班综合思维能力训练(四)
  • 面向培养实用型、技术型移动应用开发软件人才的需求,从教学内容、学习方式、职业能力等几方面探讨如何在移动开发教学中进行学生的编程思维能力训练,介绍培养学生编程思维能力的几种方法
  • 逻辑思维训练500题.pdf

    2014-02-28 15:19:29
    逻辑思维训练500题.pdf
  • 逻辑思维训练500题(附答案)
  • 情感能力塑造思维训练题.doc
  • 创新思维训练500题

    2011-12-15 01:22:55
    教你怎么样考虑问题 培养创新能力 成功者最大的特点就是思维方式和一般人不同!!!
  • 《C语言程序设计》课程对学生思维能力训练.pdf
  • 创新思维训练及创新能力培养教材.pptx
  • 最近的一些工作感受,让自己觉得,抽象能力(或者说,思考问题本质的能力)的训练的确不是一件容易的事 情。 也许,对有些人来说,得天独厚生具的优势使得他们更擅长在抽象层面处理问题,他们也更容易看到问题的...
    最近的一些工作感受,让自己觉得,抽象能力(或者说,思考问题本质的能力)的训练的确不是一件容易的事
    

    情。

    也许,对有些人来说,得天独厚生具的优势使得他们更擅长在抽象层面处理问题,他们也更容易看到问题的

    本质。但是对于绝大多数人类个体来说,由于在人类的漫长进化史上, 我们的感性思维能力的进化时长要

    远远大于理性思维能力的进化时长
    ,所以,从本能适应性的角度来看,更多人类个体,会更愿意(擅长)运用

    自己的感性思维能力而非理性思维能力来处理问题。很多的Computational Biases就是随着人类的进化历程

    出现并固化为类本能的思维习惯,进而影响我们的思考决策过程,限制我们认识事物本质的能力。

    而在问题思考过程中,过度依赖感性思维能力的一种体现,就是 局限于具体细节层面思考,分析问题

    这是因为,对具体问题的处理往往有着一个看得见,摸得着,比较具体形象的参照系,有着大量的具体细节

    特征可以协助自己理解,分析问题。某个思维环节的不严密比较容易就可以通过对具体细节的触碰来获得相

    应的校正,会容易建立起一种“ 看得见,摸得着”的感觉,自己总可以通过外界的具体细节的反馈来获得思维前

    进的线索。所以,很多人,也包括现在阶段的自己,喜欢站在更具体一些的层次来处理问题,以通过与大量

    的具体细节的互动来减少自己在问题处理过程中的负担。

    但是,站在具体层面处理问题也会有一些局限。

    比如说,具体层面的信息量往往比较大,联系也相应繁杂,对大脑记忆的负担会比较大。另外,随着问题场

    景的变化,具体细节往往会产生较大的变化波动。在某些具体细节能够立即触碰得到的场景A下形成的认识和

    理解,换到一个不能立即触碰到相应具体细节的场景B下,因为缺少了原始具体细节的支撑,往往就不容易完

    整,精确地再现,回复出来。换句话说, 站在具体层面把握问题,对具体细节的依赖较大,而这些具体细

    节的存在往往依赖于外部环境,一旦具体细节因外部环境的变化而消失或减少,自己基于这些细节建立

    起来的问题理解和把握程度就有所下降了
    。也许,这在一定程度上能够解释为什么在问题出现的场景下,

    我们往往能够凭着"Trial-and-Error"的方式解决问题,但真让自己把解决问题的思路清晰地描述出来,却往

    往又作不到了。因为 解决具体问题的过程中,具体细节可以在相当大程度上辅助我们的思考决策,为我

    们提供很多线索,在一定程度上弥补因思维冗余及不精确对问题解决带来的负面影响
    。而描述解决问题

    的思路时,思维的冗余和不精确就完全暴露出来,具体细节能够发挥的辅助作用相应打了折扣。简言之,

    在具体细节层面处理问题,容易让我们对问题形成朴素但未必深刻的认知,这种认知因为跟表层细节联

    系过于紧密,容易随着时间的推移,场景的变化而出现大的衰减
    。也就不利于我们对知识,经验的系统化

    整理,进而不利于日后的经验提取了。

    站在具体层面思考问题可能带来的另一个局限是,由于表层细节跟问题本质并不总是契合的,所以当我们在

    问题场景 A基于表层细节形成了认知,在问题场景 B,遇到了同样的表层细节, 而在场景B下,该表层细节

    实际上跟问题本质不完全契合,甚至发生背离的时候,很可能就会诱导我们形成错误的认知,提取与问

    题本质不尽匹配的经验,作出不当的认知类比
    。而对于人类个体来讲,基于表层细节建立起经验提取线索

    的确要更容易,更省劲一些,而透过表层现象,抽出现象背后的本质则是一个比较耗费精力的事情,所以在

    更多场景下个体会倾向于通过表层现象来建立起经验提取的线索,甚至一些行业专家也经常如此行事。(其实

    从某种角度上来说, 基于问题的表象建立起经验提取线索也具备相当程度的正面意义,因为基于表面细

    节,个体可能以更快的速度高效的提取出相关的经验,如果表面细节跟问题本质又是基本契合的,那么,这

    种基于表面细节思考问题的方式的效率还是相当高的。所以, 遇到问题,总是放弃所有表面细节,试图透

    过现象抽出本质,也未必是件整体划算的事情,适当的利用表层细节的线索来加速自己思考,决策的过

    程,并适时地对自己的思考方向进行检验和校正,这样才更能够在思考问题的准确度和速度之间获得一

    种效率平衡点。
    )

    另外要注意的是,提取问题本质的目标往往不是可以一蹴而就的。在透过表象,形成了自以为本质的认知以

    后,不能就停步于此,以为这个对本质的认识理解就是无懈可击的了,

    还要在讨论,思考过程中 经常有意识地检验已经提取出来的关于某个问题本质认识的正确性。保持一个开

    放的心态和自我怀疑精神,这样才可能不断地深化自己对问题的认知,更契合问题背后的客观规律
    展开全文
  • 程序员必备的思维能力:抽象思维

    万次阅读 多人点赞 2021-02-12 18:03:28
    若想捉大鱼,就得潜入深渊。深渊里的鱼更有力,也更纯净。硕大而抽象,且非常美丽。——大卫·林奇抽象思维是我们工程师最重要的思维能力。因为软件技术 本质上就是一门抽象的艺术。我们的工作是存思维...

    若想捉大鱼,就得潜入深渊。深渊里的鱼更有力,也更纯净。硕大而抽象,且非常美丽。——大卫·林奇

    抽象思维是我们工程师最重要的思维能力。因为软件技术 本质上就是一门抽象的艺术。我们的工作是存思维的“游戏”,虽然我们在使用键盘、显示器,打开电脑可以看到主板、硬盘等硬件。但我们即看不到程序如何被执行,也看不到0101是如何被CPU处理的。

    我们工程师每天都要动用抽象思维,对问题域进行分析、归纳、综合、判断、推理。从而抽象出各种概念,挖掘概念和概念之间的关系,对问题域进行建模,然后通过编程语言实现业务功能。所以,我们大部分的时间并不是在写代码,而是在梳理需求,理清概念,当然,也包括尝试看懂那些“该死的、别人写的”代码。

    在我接触的工程师中,能深入理解抽象概念的并不多,能把抽象和面向对象、架构设计进行有机结合,能用抽象思维进行问题分析、化繁为简的同学更是凤毛麟角。

    对于我本人而言,每当我对抽象有进一步的理解和认知,我都能切身感受到它给我在编码和设计上带来的质的变化。同时感慨之前对抽象的理解为什么如此肤浅。如果时间可以倒流的话,我希望我在我职业生涯的早期,就能充分意识到抽象的重要性,能多花时间认真的研究它,深刻的理解它,这样应该可以少走很多弯路。

    1.1 什么是抽象

    关于抽象的定义,百度百科是这样说的:

    抽象是从众多的事物中抽取出共同的、本质性的特征,而舍弃其非本质的特征的过程。具体地说,抽象就是人们在实践的基础上,对于丰富的感性材料通过去粗取精、去伪存真、由此及彼、由表及里的加工制作,形成概念、判断、推理等思维形式,以反映事物的本质和规律的方法。
    实际上,抽象是与具体相对应的概念,具体是事物的多种属性的总和,因而抽象亦可理解为由具体事物的多种属性中舍弃了若干属性而固定了另一些属性的思维活动。[1]

    Wikipedia的解释是:

    抽象是指为了某种目的,对一个概念或一种现象包含的信息进行过滤,移除不相关的信息,只保留与某种最终目的相关的信息。例如,一个皮质的足球,我们可以过滤它的质料等信息,得到更一般性的概念,也就是球。从另外一个角度看,抽象就是简化事物,抓住事物本质的过程。[2]

    简单而言,“抽”就是抽离,“象”就是具象,字面上理解抽象,抽象的过程就是从“具象”事物中归纳出共同特征,“抽取”得到一般化(Generalization)的概念的过程。英文的抽象——abstract来自拉丁文abstractio,它的原意是排除、抽出。

    为了更好的方便你理解抽象,让我们先来看一幅毕加索的画,如下图所示,图的左边是一头水牛,是具象的,右边是毕加索画,是抽象的。怎么样,是不是感觉自己一下子理解了抽象画的含义。 

    可以看到,抽象牛只有几根线条,不过这几根线条是做了高度抽象之后的线条,过滤了水牛的绝大部分细节,保留了牛最本质特征,比如牛角,牛头,牛鞭、牛尾巴等等。这种对细节的舍弃使得“抽象牛”具有更好的泛化(Generalization)能力。可以说,抽象更接近问题的本质。也就是说所有的牛都逃不过这几根线条。

    1.2 抽象和语言是一体的

    关于抽象思维,我们在百度百科上可以看到如下的定义:

    抽象思维,又称词(概念)的思维或者逻辑思维,是指用词(概念)进行判断、推理并得出结论的过程。抽象思维以词(概念)为中介来反映现实。这是思维的最本质特征,也是人的思维和动物心理的根本区别。[3]

    之所以把抽象思维称为词思维或者概念思维,是因为语言和抽象是一体的。当我们说“牛”的时候,说的就是“牛”的抽象,他代表了所有牛共有的特征。同样,当你在程序中创建Cow这个类的时候,道理也是一样。在生活中,我们只见过一头一头具象的牛,“牛”作为抽象的存在,即看不见也摸不着。

    这种把抽象概念作为世界本真的看法,也是古希腊哲学家柏拉图的最重要哲学思想。柏拉图认为,我们所有用感觉感知到的事物,都源于相应的理念。他认为具体事物的“名”,也就是他说的“理念世界”才是本真的东西,具体的一头牛,有大有小,有公有母,颜色、性情、外形各自不同。因此我们不好用个体感觉加以概括,但是这些牛既然都被统称为“牛”,则说明它们必然都源于同一个“理念”,即所谓“牛的理念”或者“理念的牛”,所以它们可以用“牛”加以概括。尚且不论“理念世界”是否真的存在,这是一个哲学问题,但有一点可以确定,我们的思考,对概念的表达都离不开语言。[4]

    这也是为什么,我在做设计和代码审查(Code Review)的时候,会特别关注命名是否合理的原因。因为命名的好坏,在很大程度上反应了我们对一个概念的思考是否清晰,我们的抽象是否合理,反应在代码上就是,代码的可读性、可理解性是不是良好,以及我们的设计是不是到位。

    有人做过一个调查,问程序员最头痛的事情是什么,通过Quora和Ubuntu Forum的调查结果显示,程序员最头疼的事情是命名。如果你曾经为了一个命名而绞尽脑汁,就不会对这个结果感到意外。

    就像Stack Overflow的创始人Joel Spolsky所说的:“起一个好名字应该很难,因为,一个好名字需要把要义浓缩在一到两个词。(Creating good names is hard, but it should be hard, because a great name captures essential meaning in just one or two words)。”

    是的,这个浓缩的过程就是抽象的过程。我不止一次的发现,当我觉得一个地方的命名有些别扭的时候,往往就意味着要么这个地方我没有思考清楚,要么是我的抽象弄错了。

    关于如何命名,我在《代码精进之路》里已经有比较详尽的阐述,这里就不赘述了。

    我想强调的是语言是明晰概念的基础,也是抽象思维的基础,在构建一个系统时,值得我们花很多时间去斟酌、去推敲语言。在我做过的一个项目中,就曾为一个关键实体讨论了两天,因为那是一个新概念,尝试了很多名字,始终感觉到别扭、不好理解。随着我们讨论的深入,对问题域理解的深入,我们最终找到了一个相对比较合适的名字,才肯罢休。

    这样的斟酌是有意义的,因为明晰关键概念,是我们设计中的重要工作。虽然不合理的命名,不合理的抽象也能实现业务功能。但其代价就是维护系统时,极高的认知负荷。随着时间的推移,就没人能搞懂系统的设计了。

    1.3 抽象的层次性

    回到毕加索的抽象画,如下图所示,如果映射到面向对象编程,抽象牛就是抽象类(Abstract Class),代表了所有牛的抽象。抽象牛可以泛化成更多的牛,比如水牛、奶牛、牦牛等。每一种牛都代表了一类(Class)牛,对于每一类牛,我们可以通过实例化,得到一头具体的牛实例(Instance)。 

    从这个简单的案例中,我们可以到抽象的三个特点:

    1. 第一,抽象是忽略细节的。抽象类是最抽象的,忽略的细节也最多,就像抽象牛,只是几根线条而已。在代码中,这种抽象可以是Abstract Class,也可以是Interface。

    2. 第二,抽象代表了共同性质。类(Class)代表了一组实例(Instance)的共同性质,抽象类(Abstract Class)代表了一组类的共同性质。对于我们上面的案例来说,这些共同性质就是抽象牛的那几根线条。

    3. 第三,抽象具有层次性。抽象层次越高,内涵越小,外延越大,也就是说它的涵义越小,泛化能力越强。比如,牛就要比水牛更抽象,因为它可以表达所有的牛,水牛只是牛的一个种类(Class)。

    抽象的这种层次性,是除了抽象概念之外,另一个我们必须要深入理解的概念,因为小到一个方法要怎么写,大到 一个系统要如何架构,以及我们后面第三章要介绍的结构化思维,都离不开抽象层次的概念。

    在进一步介绍抽象层次之前,我们先来理解一下外延和内涵的意思:

    抽象是以概念(词语)来反映现实的过程,每一个概念都有一定的外延和内涵.概念的外延就是适合这个概念的一切对象的范围,而概念的内涵就是这个概念所反映的对象的本质属性的总和.例如“平行四边形”这个概念,它的外延包含着一切正方形、菱形、矩形以及一般的平行四边形,而它的内涵包含着一切平行四边形所共有的“有四条边,两组对边互相平行”这两个本质属性。

    一个概念的内涵愈广,则其外延愈狭;反之,内涵愈狭,则其外延愈广。例如,“平行四边形”的内涵是“有四条边,两组对边互相平行”,而“菱形”的内涵除了这两条本质属性外,还包含着“四边相等”这一本质属性。“菱形”的内涵比“平行四边形”的内涵广,而“菱形”的外延要比“平行四边形”的外延狭。

    所谓的抽象层次就体现在概念的外延和内涵上,这种层次性,基本可以体现在任何事物上,比如一份报纸就存在多个层次上的抽象,“出版品”最抽象,其内涵最小,但外延最大,“出版品”可以是报纸也可以是期刊杂志等。

    1. 一个出版品

    2. 一份报纸

    3. 《旧金山纪事报》

    4. 5 月 18 日的《旧金山纪事报》

    当我要统计美国有多少个出版品,那么就要用到最上面第一层“出版品”的抽象,如果我要查询旧金山5月18日当天的新闻,就要用到最下面第四层的抽象。

    每一个抽象层次都有它的用途,对于我们工程师来说,如何拿捏这个抽象层次是对我们设计能力的考验,抽象层次太高和太低都不行。

    比如,现在要写一个水果程序,我们需要对水果进行抽象,因为水果里面有红色的苹果,我们当然可以建一个RedApple的类,但是这个抽象层次有点低,只能用来表达“红色的苹果”。来一个绿色的苹果,你还得新建一个GreenApple类。

    为了提升抽象层次,我们可以把RedApple类改成Apple类,让颜色变成Apple的属性,这样红色和绿色的苹果就都能表达了。再继续往上抽象,我们还可以得到水果类、植物类等。再往上抽象就是生物、物质了。

    你可以看到,抽象层次越高,内涵越小,外延越大,泛化能力越强。然而,其代价就是业务语义表达能力越弱。 

    具体要抽象到哪个层次,要视具体的情况而定了,比如这个程序是专门研究苹果的可能到Apple就够了,如果是卖水果的可能需要到Fruit,如果是植物研究的可能要到Plant,但很少需要到Object。

    我经常开玩笑说,你把所有的类都叫Object,把所有的参数都叫Map的系统最通用,因为Object和Map的内涵最小,其延展性最强,可以适配所有的扩展。从原理上来说,这种抽象也是对的,万物皆对象嘛。但是这种抽象又有什么意义呢?它没有表达出任何想表达的东西,只是一句正确的废话而已。

    越抽象,越通用,可扩展性越强,然而其语义的表达能力越弱。越具体,越不好延展,然而其语义表达能力很强。所以,对于抽象层次的权衡,是我们系统设计的关键所在,也是区分普通程序员和优秀程序员的关键所在。

    1.4 软件中的分层抽象无处不在

    越是复杂的问题越需要分层抽象,分层是分而治之,抽象是问题域的合理划分和概念语义的表达。不同层次提供不同的抽象,下层对上层隐藏实现细节,通过这种层次结构,我们才有可能应对像网络通信、云计算等超级复杂的问题。

    网络通信是互联网最重要的基础实施,但同时它又是一个很复杂的过程,你要知道把数据包传给谁——IP协议,你要知道在这个不可靠的网络上出现状况要怎么办——TCP协议。有这么多的事情需要处理,我们可不可以在一个层次中都做掉呢?当然是可以的,但显然不科学。因此,ISO制定了网络通信的七层参考模型,每一层只处理一件事情,低层为上层提供服务,直到应用层把HTTP,FTP等方便理解和使用的协议暴露给用户。 

    编程语言的发展史也是一个典型的分层抽象的演化史。

    机器能理解的只有机器语言,即各种二进制的01指令。如果我们采用O1的输入方式,其编程效率极低(学过数字电路的同学,体会下用开关实现加减法)。所以我们用汇编语言抽象了二进制指令。然而汇编还是很底层,于是我们用C语言抽象了汇编语言。而高级语言Java是类似于C这样低级语言的进一步抽象,这种逐层抽象极大的提升了我们的编程效率。 

    1.5 重复代码是抽象的缺失

    如果说抽象的本质是共性的话,那么我们代码中的重复代码,是不是就意味着抽象的缺失呢?

    是这样的,重复代码是典型的代码坏味道,其本质问题就是抽象的缺失。因为我们Ctrl+C加Ctrl+V的工作习惯,导致没有对共性代码进行抽取,或者虽然抽取了,只是简单的用了一个Util名字,没有给到一个合适的名字,没有正确的反应这段代码所体现的抽象概念,都属于抽象不到位。

    有一次,我在Review团队代码的时候,发现有一段组装搜索条件的代码,在几十个地方都有重复。这个搜索条件还比较复杂,是以元数据的形式存在数据库中,因此组装的过程是这样的:

    • 首先,我们要从缓存中把搜索条件列表取出来;

    • 然后,遍历这些条件,将搜索的值填充进去;

    //取默认搜索条件
    List<String> defaultConditions = searchConditionCacheTunnel.getJsonQueryByLabelKey(labelKey);
    for(String jsonQuery : defaultConditions){
    	jsonQuery = jsonQuery.replaceAll(SearchConstants.SEARCH_DEFAULT_PUBLICSEA_ENABLE_TIME, String.valueOf(System.currentTimeMillis() / 1000));
    	jsonQueryList.add(jsonQuery);
    }
    //取主搜索框的搜索条件
    if(StringUtils.isNotEmpty(cmd.getContent())){
        List<String> jsonValues = searchConditionCacheTunnel.getJsonQueryByLabelKey(SearchConstants.ICBU_SALES_MAIN_SEARCH);
        for (String value : jsonValues) {
    		String content = StringUtil.transferQuotation(cmd.getContent());
    		value = StringUtil.replaceAll(value, SearchConstants.SEARCH_DEFAULT_MAIN, content);
        	jsonQueryList.add(value);
    	}
    }
    

    简单的重构无外乎就是把这段代码提取出来,放到一个Util类里面给大家复用。然而我认为这样的重构只是完成了工作的一半,我们只是做了简单的归类,并没有做抽象提炼。

    简单分析,不难发现,此处我们是缺失了两个概念:一个是用来表达搜索条件的类——SearchCondition;另一个是用来组装搜索条件的类——SearchConditionAssembler。只有配合命名,显性化的将这两个概念表达出来,才是一个完整的重构。

    重构后,搜索条件的组装会变成一种非常简洁的形式,几十处的复用只需要引用SearchConditionAssembler就好了。

    public class SearchConditionAssembler {
        public static SearchCondition assemble(String labelKey){
            String jsonSearchCondition =  getJsonSearchConditionFromCache(labelKey);
            SearchCondition sc = assembleSearchCondition(jsonSearchCondition);
            return sc;
        }
    }
    

    由此可见,提取重复代码只是我们重构工作的第一步。对重复代码进行概念抽象,寻找有意义的命名才是我们工作的重点。

    因此,每一次遇到重复代码的时候,你都应该感到兴奋,想着,这是一次锻炼抽象能力的绝佳机会,当然,测试代码除外。

    1.6 强制类型转换是抽象层次有问题

    面向对象设计里面有一个著名的SOLID原则是由Bob大叔(Robert Martin)提出来的,其中的L代表LSP,就是Liskov Substitution Principle(里氏替换原则)。简单来说,里氏替换原则就是子类应该可以替换任何父类能够出现的地方,并且经过替换以后,代码还能正常工作。

    思考一下,我们在写代码的过程中,什么时候会用到强制类型转换呢?当然是LSP不能被满足的时候,也就是说子类的方法超出了父类的类型定义范围,为了能使用到子类的方法,只能使用类型强制转换将类型转成子类类型。

    举个例子,在苹果(Apple)类上,有一个isSweet()方法是用来判断水果甜不甜的;西瓜(Watermelon)类上,有一个isJuicy()是来判断水分是否充足的;同时,它们都共同继承一个水果(Fruit)类

    此时,我们需要挑选出甜的水果和有水分的习惯,我们会写一个如下的程序:

    public class FruitPicker {
    
        public List<Fruit> pickGood(List<Fruit> fruits){
            return fruits.stream().filter(e -> check(e)).
                    collect(Collectors.toList());
        }
        
        private boolean check(Fruit e) {
            if(e instanceof Apple){
                if(((Apple) e).isSweet()){
                    return true;
                }
            }
            if(e instanceof Watermelon){
                if(((Watermelon) e).isJuicy()){
                    return true;
                }
            }
            return false;
        }
    }
    

    因为pick方法的入参的类型是Fruit,所以为了获得Apple和Watermelon上的特有方法,我们不得不使用instanceof做一个类型判断,然后使用强制类型转换转成子类类型,以便获得他们的专有方法,很显然,这是违背了里式替换原则的。

    这里问题出在哪里?对于这样的代码我们要如何去优化呢?仔细分析一下,我们可以发现,根本原因是因为isSweet和isJuicy的抽象层次不够,站在更高抽象层次也就是Fruit的视角看,我们挑选的就是可口的水果,只是具体到苹果我们看甜度,具体到西瓜我们看水分而已。

    因此,解决方法就是对isSweet和isJuicy进行抽象,并提升一个层次,在Fruit上创建一个isTasty()的抽象方法,然后让苹果和西瓜类分别去实现这个抽象方法就好了。 

    下面是重构后的代码,通过抽象层次的提升我们消除了instanceof判断和强制类型转换,让代码重新满足了里式替换原则。抽象层次的提升使得代码重新变得优雅了。

    public class FruitPicker {
    
        public List<Fruit> pickGood(List<Fruit> fruits){
            return fruits.stream().filter(e -> check(e)).
                    collect(Collectors.toList());
        }
        
        //不再需要instanceof和强制类型转换
        private boolean check(Fruit e) {
            return e.isTasty();
        }
    }
    
    

    所以,每当我们在程序中准备使用instanceof做类型判断,或者用cast做强制类型转换的时候。每当我们的程序不满足LSP的时候。你都应该警醒一下,好家伙,这又是一次锻炼抽象能力的绝佳机会。

    1.7 如何提升抽象思维能力

    抽象思维能力是我们人类特有的、与生俱来的能力,除了上面说的在编码过程中可以锻炼抽象能力之外,我们还可以通过一些其他的练习,不断的提升我们的抽象能力。

    多阅读

    为什么阅读书籍比看电视更好呢?因为图像比文字更加具象,阅读的过程可以锻炼我们的抽象能力、想象能力,而看画面的时候会将你的大脑铺满,较少需要抽象和想象。

    这也是为什么我们不提倡让小孩子过多的暴露在电视或手机屏幕前的原因,因为这样不利于他抽象思维的锻炼。

    抽象思维的差别让孩子们的学习成绩从初中开始分化,许多不能适应这种抽象层面训练的,就去读技校了,因为技校比大学会更加具象:车铣刨磨、零部件都能看得见摸得着。体力劳动要比脑力劳动来的简单。

    多总结沉淀

    小时候不理解,语文老师为什么总是要求我们总结段落大意、中心思想什么的。现在回想起来,这种思维训练在基础教育中是非常必要的,其实质就是帮助学生提升抽象思维能力。

    记录也是很好的总结习惯。就拿读书笔记来说,最好不要原文摘录书中的内容,而是要用自己的话总结归纳书中的内容,这样不仅可以加深理解,而且还可以提升自己的抽象思维能力。

    我从四年前开始系统的记录笔记,做总结沉淀,构建自己的知识体系。这种思维训练的好处显而易见,可以说我之前写的《从码农到工匠》和现在正在写的《程序员必备的思维能力》都离不开我总结沉淀的习惯。

    命名训练

    每一次的变量命名、方法命名、类命名都是一次难得的抽象思维训练机会,前面已经说过了,语言和抽象是一体的,命名的好坏直接反应了我们的问题域思考的是否清晰,反应了我们抽象的是否合理。

    现实情况是,我们很多的工程师常常忽略了命名的重要性,只要能实现业务功能,名字从来就不是重点。

    实际上,这是对系统的不负责任,也是对自己的不负责任,更是对后期维护系统的人不负责任。写程序和写文章有很大的相似性,本质上都是在用语言阐述一件事情。试想下,如果文章中用的都是些词不达意的句子,这样的文章谁能看得懂,谁又愿意去看呢。

    同样,我一直强调代码要显性化的表达业务语义,其中命名在这个过程中扮演了极其重要的角色。为了代码的可读性,为了系统的长期可维护性,为了我们自身抽象思维的训练,我们都不应该放过任何一个带有歧义、表达模糊、意不清的命名。

    领域建模训练

    对于技术同学,我们还有一个非常好的提升抽象能力的手段——领域建模。当我们对问题域进行分析、整理和抽象的时候,当我们对领域进行划分和建模的时候,实际上也是在锻炼我们的抽象能力。

    我们可以对自己工作中的问题域进行建模,当然也可以通过阅读一些优秀源码背后的模型设计来学习如何抽象、如何建模。比如,我们知道Spring的核心功能是Bean容器,那么在看Spring源码的时候,我们可以着重去看它是如何进行Bean管理的?它使用的核心抽象是什么?不难发现,Spring是使用了BeanDefinition、BeanFactory、BeanDefinitionRegistry、BeanDefinitionReader等核心抽象实现了Bean的定义、获取和创建。抓住了这些核心抽象,我们就抓住了Spring设计主脉。

    除此之外,我们还可以进一步深入思考,它为什么要这么抽象?这样抽象的好处是什么?以及它是如何支持XML和Annotation(注解)这两种关于Bean的定义的。

    这样的抽象思维锻炼和思考,对提升我们的抽象能力和建模能力非常重要。关于这一点,我深有感触,初入职场的时候,当我尝试对问题域进行抽象和建模的时候,会觉得无从下手,建出来的模型也感觉很别扭。然而,经过长期的、刻意的学习和锻炼之后,很明显可以感觉到我的建模能力和抽象能力都有很大的提升。不但分析问题的速度更快了,而且建出来的模型也更加优雅了。

    1.8 小结

    • 抽象思维是程序员最重要的思维能力,抽象的过程就是寻找共性、归纳总结、综合分析,提炼出相关概念的过程。

    • 语言和抽象是一体的,抽象思维也叫词思维,因为抽象的概念只能通过语言才能表达出来。

    • 抽象是有层次性的,抽象层次越高,内涵越小,外延越大,扩展性越好;反之,抽象层次越低,内涵越大,外延越小,扩展性越差,但语义表达能力越强。

    • 对抽象层次的拿捏,体现了我们的设计功力,视具体情况而定,抽象层次既不能太高,也不能太低。

    • 重复代码意味着抽象缺失,强制类型转换意味着抽象层次有问题,我们可以利用这些信号来重构代码,让代码重新变的优雅。

    • 我们可以通过刻意练习来提升抽象能力,这些练习包括阅读、总结、命名训练、建模训练等。

    [1] https://baike.baidu.com/item/抽象/9021828

    [2] https://zh.wikipedia.org/wiki/抽象化

    [3] https://baike.baidu.com/item/抽象思维

    [4] https://www.sohu.com/a/359915387_260616

    展开全文
  • 基于悬搁的美术与设计创造思维训练和创作能力培养.docx
  • 图形语言是目前世界上公认的人际...掌握和利用图形语言是右脑的功能,通过对学生进行看、想、绘、用4个环节的图形语言训练,有利于开发学生右脑的功能,强化形象思维训练,从而达到使学生学会使用全脑进行思维的目的。
  • 创新思维训练与阻碍创新能力的因素.pptx
  • 逻辑思维训练1200题,对训练逻辑思考能力非常有帮助。赶快下载。
  • 初中语文语文论文浅议在阅读训练中培养思维能力
  • 逻辑思维训练相关题看看逻辑能力是否过关
  • 综上所述,本书——《思维训练500题》就是本着这样一种理念,运用各种方法,如分析法、观察法、类比法、归纳法、演绎法、递推法、倒推法、综合法等,有目的、有计划地训练人们的逻辑思维能力。相信,这550个题做过完...
  • 适合所有软件开发和从事设计方面的人员,主要可以提高自己的思维能力,希望对大家能有 一个好的帮助。
  • 有感于最近的技术分享,觉得思维训练越来越重要了,思维训练到位后,就可以用最高效的方式在最短的时间内取得最大的成果(符合自我高效获取高性价比价值的价值观定位),所以开启本系列。就以超哥分享的《系统思维》...

    有感于最近的技术分享,觉得思维训练越来越重要了,思维训练到位后,就可以用最高效的方式在最短的时间内取得最大的成果(符合自我高效获取高性价比价值的价值观定位),所以开启本系列。首先明确下自己的思维方式:

    经过荣格心理学测试,我的结果是INTJ的思维模式中,关于INTJ型人格(由I(内向)+N(直觉)+T(思考)+J(判断)),关于人格的优点发挥和缺点规避需要再花时间去讨论。

    关于MBIT荣格性格测试,传送门

    https://baike.baidu.com/item/MBTI%E4%BA%BA%E6%A0%BC%E7%90%86%E8%AE%BA/5136752?fr=aladdin

    关于INTJ型人格,传送门:

    INTJ型人格:https://baike.baidu.com/item/INTJ/10945114?fr=aladdin

    那么就以超哥分享的《系统思维》里学到的东西作为开篇吧。经过自己的学习消化和分析之后,总结而成一篇博文:值得注意的是两种思维没有优劣,应对不同问题和在不同场景下调用合适的思维来解决问题而已。所以,目标是把这两种思维都训练的特别好

    直觉思维很好理解,这里的直觉并不是INTJ里的直觉人格N,而是思维模式,思维模式分为直觉思维和理性思维,直觉思维较为简单也比较好理解,但和理性思维其实没有优劣之分。直觉思维的特点是及时处理,也就是;

    • 麻烦出现—下达结论
    • 危机出现—马上行动
    • 问题堆积—开始处理

    这种思维的好处是行动敏捷,可以快速响应,并且解决方案和经验可以随着时间积累,响应速度会越来越快,并且在响应过程中不断优化,产生创造性的解决方案。但缺点也很明显,那就是不一定可靠,这一点很容易理解,因为拿到问题就快速响应,没有充分的分析和讨论,所以响应的结果页会有偏差。

    直觉思维很好理解,我们通常用来处理一些紧急的但又不负责的事情,类似hotfix类型问题,而理性思维则比较复杂,需要认真锻炼

    我们的主题是理性思维:什么是理性思维勒:说得简单些理性思维就是一种建立在证据和逻辑推理基础上的思维方式

    构建框架

    首先抛出一个问题:目前大家对平台的熟悉程度不够,客开不能及时的确定平台是不是有这个能力去满足客户的需求,而新员工都是张口就问,导师都无暇正式工作,得花很大精力去培训新员工,甚至很多老员工都不熟悉平台,怎样才能提高平台的效率呢,提升平台的影响力,更新最新平台特性?

    刚开始拿到这个问题的时候感觉非常空泛,把这个问题放入构建框架里来梳理一遍看看:

    依赖信息(证据)

    依赖信息就是思维所需要的证据啦,也就是我们需要拿到完整的问题,注意,是完整的问题,在开始分析前一定要拿到全部的问题和问题所处环境等,有两个重要原则:

    • 1.明确问题性质程度、以及相关信息
    • 2.必须以数据和事实为基础,而非假设和推断。

    两个字,一个全,一个真。验证问题:1,拿到的信息全么?

    流程分析(逻辑推理)

    然后随着案例向下,按照如下流程剥开问题。
    接下来进入分析流程:描述问题—界定问题—分析问题—解决方案

    描述问题

    第一步就是快速捕捉问题的关键点,也就是能够完整的明确及理解问题:

    • 1 准确的描述问题:拿到问题时候一定要拿到最全的问题描述
    • 2 明确问题的构成要素:七问分析法:Why(为什么),What(事、物),Who(人)、When(时机)、Where(地点、位置)、How(方式、程度)、How Much(预算)

    Why:为什么要我们来解决,我们是平台,客开,新员工,实施等都是我们的客户
    What: 问题是什么: 效率低下,上手成本高(改进指标,需要良好,也用来出成果)
    Who: 客开,新员工,实施(需要找这些人调研详细问题)
    When:什么时候做,现在就开始,明年9月底完成(需要分期去做)
    Where:在公司完成
    How:如何完成,开调研会议,商讨解决方案(接下来进入调研)
    How Much: 需要多少成本,人力资源,需要平台产品经理协助
    首先拿到最全的问题描述,使用5W2H来快速掌握问题,一次问清楚比问多次效果好。问题获取全面后才可以着手去解决,别想到一点儿是一点儿,到时候不停的打补丁。

    界定问题—调研

    问题一开始都是模糊的,必须进行具体客观的描述,只有明确目标后才能应对目标达成一致。要经过以下几个步骤:

    1. 分析现状,达成一致,也就是和大家对齐处境
    2. 考量,明确目标:通过几个角度多维度的考量一下目标:
    3. 解决方案
    4. 结论、资源分配

    ================按照上述案例,我们要出一本书

    分析现状:文档混乱,各个业务线培训需求大,培训成本大,具体量化(找平台及各个业务线的人开会)。分析现状也是为了让所有人明确问题的存在,为之后资源分配获取好处

    考量明确目标:套用进去那几步:

    立场考量

    1. 谁:谁是决策者:大产品经理,总监 为谁解决问题:客开,新员工
    2. 关注点:决策者的关注点:成本真的能降低?和现有文档有啥区别,有必要去做这件事么?值得投入这么多精力么?如何平衡各利益方:产品经理时间抽调

    战略考量

    1. 边界:将不考虑哪些:太复杂的文档结构,只做流程和入门。挑战指标:对外出版 必达指标:内部使用,大幅降低培训成本和人力物力,卡好边界非常重要
    2. 时间::一期Q4配置初稿,少数人反馈迭代,二期Q1全文初稿,小范围反馈迭代,三期Q2内部推出,大范围反馈和迭代,四期Q3推出给实习和即将校招的新员工全范围推广,提供给客开。

    价值考量

    1. 标准:成功解决问题的主要标准:新员工基本没有问基础简单问题问mentor,并且可以独立搭建应用,客开能及早了解平台最新特性,方便承诺
    2. 准确度:需要在80%左右,风险点在明年的任务规划是否对主流程执行有影响,和各方的协调,其实准确度就是风险点。

    解决方案:依据目标来确定解决方案(解决方案如何确定后续介绍)

    结论、资源分配:要资源!开会的目的不仅仅只是宣讲,而是对齐目标,要与会者参与进去。 目前的困惑是(参与度不高。)

    分析问题

    界定完问题之后开始分析问题,分析问题用这几个工具去分析:感觉思维导图可以包含以下:逻辑树,MECE,5Why的全部功能,也就是思维导图可以干以下三种的事情。

    思维导图

    思维导图很常见,有如下价值点:

    1. 从中心向四周层层放射的结构有利于激发联想,通过一个节点上的关键词激发出更多的关键词
    2. 形象的图示和丰富的色彩可刺激人的大脑,起到激发思维的作用
    3. 通过将知识以放射状结构和可视化呈现,可大幅提升个人的感受力、理解力和记忆力。

    发散核心问题(联想和发散)

    逻辑树

    对问题/议题/假设层层有序分解,有助于理清思路,不做重复和无效的思考,可有效分解复杂问题/议题,从而简化问题/议题的处理,通过将问题/议题分解为更细的问题/议题,有助于团队的协作和分工。
    在这里插入图片描述

    逻辑树的关键是:每一个选项都层层分解
    将大问题化简为小问题(分解)

    MECE

    MECE是“Mutually,Exclusive,Collectively,Exhaustive”(相互独立、完全穷尽)的英文首字母缩写,价值点:对复杂的问题分类时,首先要考虑如何让各种分类不相互重叠或包含,避免分类相互重叠,防止重复分析工作。而全面地考虑问题,确保所提出的分类是够完整,可以确保没有遗漏任何项目,涵盖问题的每个方面, 只有在一开始做到不重、不漏,才可以在一开始就避免以偏概全的误区。
    分析复杂问题的最全分类(组成)

    5Why穷尽问题法

    循着一个问题不停的向下发问!最终达到方案。如何把握关键,找到真正重要的问题,而不是“误入歧途”,是非常关键的。因此,在使用这一工具时,不要直接跳入更深的问题,尽量问直接的问题,保持较窄的聚焦范围,寻求详尽的答案

    ================继续上述案例,我们要出一本书,遇到的问题有哪些
    问题陈述:书写不下去了,越写越像文档-----为什么?------因为不规范-----为什么?------因为没有标准,没有严格要求------为什么?------因为没有开会统一思想,没有学习标准------为什么?--------因为觉得没必要,不好意思。没时间,so,从现在开始:迅速开会推动这件事并且去调研写书的标准
    分析单一细节问题根源(追溯)

    解决方案

    解决方案分为行动前的决策方案和计划行动方案,主要在于计划行动方案:

    决策制定步骤

    使用FCAR分析方法:
    1, Focus-决策的目的是什么,目标的明确,有助于对标准的细化,标准实际就是对目标的贡献程度。
    2,Criteria-标准如何列举和划分:分为两大类

    • 重要的,刚性标准。一定要完成的:内部员工,客开支持
    • 相对的,可以进行比较。比较完成的,外部推动的时候

    3,Alternations-选择项的优劣,为比较标准设定权重就可以保证对目标有最大的影响力的因素,权重:把最重要的设计为10,其他参照重要性递减。得分:已经搜集到的信息;创造力;专家的帮助。总分 = 权重 * 得分 ,总分高的入选。
    4,Risks-可能面临的风险

    • 设法找出可能出现的问题并事先安排应对方法
    • 风险影响程度比较标注你和权重一样。
    • 确定了风险的影响程度后,根据各项发生概率,进行相对的评估,发生概率高的得分高,概率低的得分低,最后根据分数来决定是停止还是继续。
    • 总分 = 影响程度 * 概率

    通过该决策步骤可以制定出标准的解决方案

    多项方案

    多项方案并举,要给领导做选择题,不要给领导做填空题。这一点我认为非常重要,多做预案,多做调研,少白痴式发问,问问题遵循前面的问题分析流程:描述问题–界定问题–分析问题,然后给出多项解决方案(分期迭代思维)。

    启发

    有两个比较重要的启发:

    • 把思考的速度放慢,习惯质疑与反思
    • 如果你想要解决问题,你就必须用80%的精力去拆解和定位这个问题,剩下20%的精力去寻找解决方案,其实就足够了

    慢点儿开始,考虑全面。

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 75,240
精华内容 30,096
关键字:

思维能力训练方法