文章吧手机版
《A Philosophy of Software Design》读后感锦集
日期:2020-03-27 23:14:02 来源:文章吧 阅读:

《A Philosophy of Software Design》读后感锦集

  《A Philosophy of Software Design》是一本由John Ousterhout著作,Yaknyam Press出版的Paperback图书,本书定价:GBP 14.21,页数:190,特精心网络整理的一些读者读后感希望大家能有帮助

  《A Philosophy of Software Design》精选点评

  ●保卫程序员时间 一切从设计开始

  ●说到底就一句话,多设计

  ●different perspective of view but make sense

  ●非常好的小书 Create Deep module!! Write comments for abstraction and precision!! More to update

  ●这本书超赞。前半部分复杂度与设计的阐发非常有启发性。后半部分主要关于注释优化,虽然不如前一半精彩水准也非常高。

  ●我觉得这本书有价值的都在前16章,后面那几章有点凑数的感觉

  ●通俗易懂凝聚了设计方面宝贵经验

  ●引用最后一章第一句话:This book is about one thing: complexity。虽然标题里带有"philosophy",但其实书里介绍大部分内容还是挺直觉。关于异常和注释的两章反而让我收获最多

  ●重点看了前面10章,特别是关于软件复杂性方面。感受比较深的几点是: 1.对抗复杂性的两个方法代码简洁和封装。 keep these in mind when programming. 2. 尽量战略性编程,而不是战术性编程 3. module设计要有深度, 接口少而简单。less is more。 让复杂性下沉 4. 尽量减少module之间的依赖关系 5. 不同层,不同抽象。 Design it twice 6. 注释应该包含设计者不能在代码(接口)中表达出来的信息。代码和注释是写给别人看的 7. Agile推崇的渐进式开发的应该是抽象,而不是功能

  ●知识点相比Clean Code还是要少一些。

  《A Philosophy of Software Design》读后感(一):减小软件设计过程中的复杂度

  最近刚读完的一本书,书名里面带有philosophy,其实大可不必惊慌,这本书的核心就是关于软件设计中的复杂度。

  作者开门见山提出了本书的两个目标,一个是讲述复杂度的各种特点,另外进一步的目标就是介绍减小软件设计复杂度的各种方法和技巧

  这本书页数不多,一个周末就读完了,前半部分1-11章是精华。从12章讲关于注释的地方开始,如果在别的书中读过类似的内容,那么完全可以跳过。

  对于任意一个函数,类,模块来说,都包含接口和实现两部分。接口应该尽量简单,通用;而实现应该尽量包含更多的独立内容。作者提出了deep module的概念,我简单类比一下,就是如果的module是一个瓶子,那么瓶子口应该尽量小,而瓶子的容积应该尽量大。从视觉上看就是一个很“深”的瓶子。

  这其实是面向对象设计的基本原则,如果你的工程经验丰富肯定已经在实际工作中这么做了。然作者在明确的提出这一点,并且创造了一个deep module 的概念,对于加深印象,和以后跟别人忽悠还是很有用的。

  除了deep module之外,还有几章精华覆盖了general和special module,information hiding,异常处理的一下常见误区。这些都是非常有用的内容。

  总结来说,推荐花时间读这本书,投入的时间绝对是值得的,不论你是学生还是0-5年经验的程序员。

  《A Philosophy of Software Design》读后感(二):代码设计的新视角

  减少软件复杂度是本书的主题为此作者提出了模块深度的概念,并从该角度对编程中的种种进行分析

  第四章 Modules Should Be Deep是比较核的一个章节,我把它的主要内容翻译成了中文,文章是《 软件设计之Deep Module(深模块)》,欢迎阅览和指正。

Module Depth

  有人说书中不少内容是老调重弹,在其他书上也可以看到。这诚然不错,但也并不代表本书的价值不高,一般来说任何书籍只要有10%的创新就已经不错,要求样样都新,未免太苛责。作者能够从模块深度这一新角度提供软件复杂度处理的方法论,已经十分不错。书中语言浅白流畅,也有充足的例子,对英文苦手来说很友好。

  下面是几个印象比较深的地方,记在这里。

  1,注释:注释不仅仅是对程序语言无法描述的部分进行拾遗补缺的工具,同时也是一种设计工具。在写代码前先写注释,可以保证注释有效反映了编写代码时的思路,也有助于提高代码设计的水平。注释过多往往意味着接口复杂、耦合度高;注释占代码的比例高,意味着模块浅。

  注释先行的另一个好处是可以给人带来快乐和自豪感,因为这样的注释记录了程序员的的创作过程。

  2,异常:异常是接口的一部分,它的增加会增加系统的复杂度。要控制异常的数量,办法之一是从概念上消除异常,比如把“删除”模块变化为“确保不存在”模块;办法之二是隐藏异常,比如TCP协议通过重传将包丢失隐藏在低层;办法之三是在高层进行统一的异常管理;最后的办法是通过不处理异常来避免增加额外的异常。

  3,敏捷:迭代和增量的开发方式有利于产生好的设计,但“快速”的要求容易催生战术式的编程,而非战略性的编程。需要将抽象而非特性作为增量的对象,这样才能避免它的弊端。

  《A Philosophy of Software Design》读后感(三):Review

  这是本月看的第三本书,也是第四篇读书回顾。

  最初注意到这本书大概是18年底在Hacker News看到的一则帖子,里面有一个YouTube链接,是斯坦福大学的教授,也就是本书作者在Google内部的一个演讲,关于软件设计和它刚写的这本书。不懂设计的我,觉得他讲的颇有大师风范,一些观点真是余音绕梁。大概从那时起,我也开始更多地关注设计。当时苦于国内没有这本书,出于对这本书对喜爱购买了原版书。

  一年半过去了,中间陆陆续续翻看,最近又过了一遍,回过头来,这本书可以作为是很多同学软件设计的启蒙书。里面提炼的设计原则和典型的设计缺陷,在日常的开发中,以及在很多开源项目里,看别人的代码,就特别能够感受得到。好的设计赏心悦目,解决复杂的问题看起来却比较简单,这里面有很多东西都值得思考和学习。

  如何评价代码好坏?以前可能觉得代码写得简洁漂亮,使用了很多流行的技术,完成需求且bug少,现在看起来都不是必要条件,好的设计才是关键。在Google code review的guideline中,列了很多条规则,比如有功能性,测试,文档,代码规范,其中第一条也是最重要的一条:良好的设计。

  好了,说了这些,开始聊下本书吧。

  在计算机科学中最基本的问题是,问题分解(problem decomposition):如何把复杂的问题拆分成若干个可以独立解决的小问题。这道理几乎每个学过分治算法的同学都懂,但知易行难,设计能力是普通和高质高效程序员的分水岭。

  设计的本质是管理(降低)复杂度,这比其他任何东西都重要。复杂的表现有很多,比如代码难以理解,没有测试,关系混乱,改动牵一发而动全身等,作者给复杂下了一个通用的定义:任何让系统难以理解和改动的都可以称之为复杂。具体的表现可以总结为三种:

  1. 改动很多地方

  2. 认知负担

  3. 有很多不清楚的东西

  问题间的关联太多,就像一张网一样纷繁复杂,这就造成了1,2;而代码写得太晦涩,难以理解导致了2,3。尽可能地减少问题间的关联关系,简洁易懂,可读性好的代码是解决复杂的关键。

  复杂度是可以量化的,作者给出了公式,每个模块本身的复杂度乘以在该模块付出的时间,最后求和。所以从数学的角度,想要降低复杂度,要么降低模块的复杂度,要么减少花费的时间。前者通过良好的设计解决,后者可以将复杂抽象封装起来,比如借助成熟的组件,这样可能就不需要自己去解决了。

  作为开发人员,首先心里得有杆秤:只写能用的代码是不够的。原因在于现在的软件开发都是在上一个版本上基础迭代的,我们每次提交的代码,如果设计没有变得更好,那多半就变坏,复杂度会随着迭代一直增加,代码库的质量也就越来越坏。在日常开发中,不应该只关注于完成功能,而是先考虑如何更好的设计。长期来看,带来的收益是巨大的,不仅是个人和项目本身。

  说的都是大道理,那么该如何做呢?

  首先模块应该被设计成深模块,可以把模块抽象成矩形,面积表示模块提供的功能,而最上的那条边可以认为是模块对外暴露的接口,越短表示接口越简单。什么样的接口才是简单的呢?相当简单的接口和极其复杂的功能。因为这样接口的结果,隐藏了对使用者不重要的息,那么模块间的依赖就少了,复杂的改动和认知这些复杂度也就少了。这里作者用unix的io函数作为例子,unix仅仅用了5个简单的io函数就实现了复杂的io(底层有硬件的交互,磁盘寻址,系统调用等,很多时候调用者只是想读一个文件成string,仅此而已),对比Java的io,嗯。

  后续的章节都是针对如何设计深模块展开。比如信息的隐藏,避免暴露不重要的信息而增加依赖,否则很多地方都要去了解处理这些信息,这就造成认知负担和改动很多地方。此外,接口的设计应当要简单通用,尽管当前我们只是实现一个具体的功能。不同的层应该有不同的抽象,拿常见的MVC举栗,经常看到不同的层都是直接调用下一层,接口签名几乎都是一样的,这种设计应该保持警惕。接下来就是应该尽可能地将复杂在模块内部处理掉,而不是简单的抛给上层,大多数情况下,底层的模块了解的细节信息更多,内部处理可以使得接口更简单,调用者的心智负担小了也就更开心。再者就是关于模块应该分离还是合并的讨论,总的来说原则就是降低复杂度,从这原则出发,对于需要共享信息,相互依赖,减少重复的,大多数时候会选择合并,这样使得模块的接口更简单。最后一个是错误的处理,关键是需要减少错误处理的地方,避免代码里到处都是抛出异常和错误处理代码,这是编程中我们最不想面对也最容易出错的地方。作者列举了几个方法,包括通过定义某种语义,错误就会不复存在。比如说=unsetEnv(key)=,定义为调用后将不存在key这个变量,如果key不存在,直接返回成功即可,它本来就不存在嘛。这个规则看起来简单,但是实际中经常发现一些接口,会把临界值作为异常返回的。

  后续章节是关于如何写可读性好的代码的,可读性好的代码可以降低认知和减少不清楚的地方。代码的可读性好是由读者来评判的,不以作者的意志为转移(这也说明code review 的重要性)。这就要求我们在命名风格,注释上要保持一致性,简单性,这里就不展开说了。

  最后还有一点,虽然设计重要,但是也不能过度,要在设计和具体实现间做取舍,不应该时间花费了,取得的收益却很小,甚至是负的。

  《A Philosophy of Software Design》读后感(四):架构设计的方法论

  最近看到很多人都在标记 《A Philosophy of Software Design》 这本书,这本书的作者是大名鼎鼎 Raft 和 Tcl 的发明者 John Ousterhout。

  还没来得及看书,但是看了下 YouTube 上 John Ousterhout 关于 A Philosophy of Software Design 在 Google 做的 talk,觉得很不错。我简单做下重点摘要,并且加上些自己的理解,为了防止被我的理解误导,还是极力推荐大亲自去 YouTube 上观看。

软件设计

软件设计是艺术,关于软件设计一直没有本质化的讨论,所以并没有方法论来描述什么是好的软件设计,我们讨论的真是一些具体的工程技术。一定要保持写代码,并且不断地进行 reiview 才能提高自己的水平,John Ousterhout 每年至少写 5000 行代码,一共写了 25 - 30 万行代码设计一个好系统最重要的是拥有的良好的设计观念,设计方法论所有计算机系统中如果只选一个最重要的概念,problem decomposition -- John Ousterhout ,layers of abstraction -- Donald Knuth

  给出的建议

“Working” isn’t good enough: must minimize complexityComplexity comes from dependencies and obscurityStrategic vs. tactical programmingClasses should be thickGeneric classes are simplerNew layer, new abstractionComments should describe things that are not obvious from the codeDefine errors out of existenceThe Martyr Principle

Classes should be deep/thick

模块的 interface 必须被调用方知道的,所以 interface 会带来系统的 complexityinterface 本身应该很简洁,把复杂的功能隐藏在简洁接口的下边,这才是一种好的抽象小接口隐藏了大量的细节,如下图第三种是好的设计,第二种最差这种抽象的意义不大,甚至增加了系统的复杂性private void addNullValueForAttribute(String attribute) { data.put(attribute, null); }这种抽象考虑了太多的细节,抽象/接口的设计应该为为常规的使用场景考虑,不要为了全面在接口上暴露所有的细节FileInputStream fileStream = new FileInputStream(fileName); BufferedInputStream bufferedStream = new BufferedInputStream(fileStream); ObjectInputStream objectStream = new ObjectInputStream(bufferedStream);好的接口设计* Unix file I/O: int open(const char* path, int flags, mode_t permissions) ); int close(int fd); ssize_t read(int fd, void* buffer, size_t count); ssize_t write(int fd, const void* buffer, size_t count); off_t lseek(int fd, off_t offset, int referencePosition;

Define Errors Out of Existence

异常是系统 complexity 的重要来源,很可能会更带来更多的 bug 误区:抛出更多的异常,代表更好的防御 minimize the number of places where exceptions must be handled, normal behavior always do the right thing(减少异常的抛出,正常的行为一直做正确的事情)当尝试创建一个完全避免用户出错的系统时,你通常会给使用系统的每个人带来复杂,例如 Java 的 Exception 机制,每个函数都要声明自己可能抛出的异常

  Example

删除正在使用的文件(Unix 的设计时好的设计),Windows 不能删除,并且会报错,现在可以删除文件了,但是删除正在使用的目录还是会报错;Unix 会把文件从文件系统直接删除,正在使用的进程还能继续使用(文件驻留在内存),当最后一个使用该文件的进程退出之后,文件就会在内存中消失真正被删除 数组切片(Python 是好的设计),Python list 切片超出范围不会报错,而是拿取所有的元素;Golang Slice 切片超出范围会报错

Working Code isn’t enough

系统不仅仅是 working,还要有好的设计,深入考虑要在系统设计上投入 10% - 20% 的时间频繁的做小的系统重构写新代码,需要小心的设计,良好的文档一直发现可以改善的地方不要只修改最少的代码,而是勇于修改,重构,优化修改之后,修改的部分就像一开始就使用了好的设计一样

  《A Philosophy of Software Design》读后感(五):All about complexity

  很早就耳闻这本书,通过作者的演讲视频了解过里边的主要观点。确实是很薄的小书,很快就看完了,总体觉得还是挺不错的,但也算不上石破天惊。

  书中前半部分1-10章重点讲软件复杂度以及本书核心观点(modules should be deep),是精华所在。后面几乎花了7-8章来讲注释和命名风格,当然内容不错,但是没什么新意。19章聊了一下对于OO/敏捷/UT/TDD等的观点,一个有意思的观点是说TDD是典型的战术编程(tactical programming)。20章简单说了一下性能设计。

  照着书中给出的设计原则说说我的理解

1. Complexity is incremental: you have to sweat the small stuff。翻译:软件复杂度是慢慢积累起来的,你必须锱铢必较。

  软件复杂度都是慢慢累积起来的。初始设计多少都是能看的,但是真实开发中有太多的场景,我们没有搞懂原有设计,或者是搞懂了也不敢改,而是通过打补丁的方式支持一个新的功能,因此迭代几轮之后几乎都变得面目全非无法理解了。这个是作者所谓“战术编程” 的方式。

  而要达成“战略编程”,就要求我们必须在平日的每一次迭代中都“锱铢必较”,维护整体的设计,在完整的框架下来扩展功能。当然这要求设计要有前瞻性,能够满足一般的功能扩展需求。更多也是对于我们软件工程的挑战,要求整个团队都清晰认识到软件质量的问题,并且持续投入。

2. Working code isn't enough.

  无需翻译了,这就是个态度问题,和第1条是一样的。

3. Make continual small investments to improve system design. 通过持续的小投入来改进系统设计。

  和第1条的要求是一样的,但是出发点不一样。这条说的是,别想着一次搞定完整的设计,而是在开发过程中逐步改进。书里说了,一次搞定就是瀑布流的思想,逐步改进就是敏捷思想。如果我们发现原有的框架不能很好的支持新功能,应当是持续的去优化原有设计框架,而不是打补丁。同样的,说起来容易做起来难啊。

4. Modules should be deep5. Interface should be designed to make the most common usage as simple as possible. 翻译:接口设计应当使得最常用的路径越简单越好。6. It's more important for a module to have a simple interface than a simple implementation. 翻译:相比起实现上的简单,一个模块接口的简单更加重要。

  这三条我感觉是本书最核心的观点,就是把接口做的简单,可以允许实现比较复杂;然后确保常用的路径非常简单易用。我非常同意这几条是减少系统复杂度的关键,使用者只需要面对一组简单直接的接口,而不用去管背后的复杂性。以书中讨论的unix和Java IO接口设计来说,Java IO更加严谨和强大,但是也明显带来了使用不易的缺点,而且实际上可能更加容易导致错误的用法,因此成为了书中的反面例子。

  但是这个思路也不是没有代价,因为真实世界是复杂的,追求简单的接口总是会有意无意的隐藏了一些信息。虽然作者也提到了我们要确保让重要的信息可见,但是重要与否还是一个依赖个人判断的事情。另外的问题就是简单的接口可能会导致实现更加复杂,甚至不可能。因此还是需要根据具体情况来权衡。

  总体而言我还是比较同意作者观点,相信大部分情况deep module是合理的,尤其是在和复杂系统打交道的时候。相对而言异常情况总是极少数的,也是容易特殊处理的。

  有意思的是,这个观点和写unix的新泽西派(参见the rise of 'worse is better')恰恰相反,新泽西派认为接口可以复杂一点,但是尽量追求实现简单。正是这样的思想可以让他们更加容易的把unix写出来,占领了市场。所以代码写的再好,也需要考虑历史的进程:-D

7. General-purpose modules are deeper.

  作者认为模块应当尽量设计的通用,但是因为需求的多变性,也不需要盲目的追求通用。一个最佳实践就是在基本不影响当前需求的实现复杂度的同时,把接口设计的尽量通用。

8. Seperate general-purpose and special-purpose code. 翻译:分离通用的代码和特定需求的代码。

  这其实是一个常见的实践了,往小了说就是抽取公共函数或者公共类,往大了说就是建立共享服务或者中台系统,感觉都是一个意思。

9. Different layers should have different abstractions. 翻译:不同的层次应当有不同的抽象。

  好像也挺容易理解的,如果不同层次的抽象是一样的,那必然会导致大量的重复代码和没有意义的类。

  我觉得作者在书中比较反对pass-through方法(没什么逻辑而只是调用其他方法)和装饰者模式,还是有一些适用的场景。有一种情况就是虽然操作是一样的,但是处在不同层次的抽象级别;另外一种是虽然当前没什么操作,但是有可能会在将来有变动。当然肯定不能滥用,尽量保持简单还是第一前提。

10. Pull complexicity downward. 翻译:把复杂性放在底层

  和第6条是一样的。

11. Define erros (and special cases) out of existence. 翻译:通过定义让错误(和特殊场景)没有机会发生。

  很有启发性的观点。其实就是因为错误处理非常困难,最好就是让接口的设计更加包容,从而在定义上就无需处理错误。我觉得这个还是对于正确性和易用性的权衡,对于非常重要的接口,还是要对使用条件检查的更加严格,避免严重的错误。而不太重要的接口应当尽量能够处理宽泛的场景,减少使用者的心智负担。

  作者特别指出了自己在Tcl语言中对unset操作的设计,要求只有在set状态下才能调用,这就导致了很多额外处理的代码和报错,其实完全可以在没有set的情况下忽略这个操作。出发点也是担心使用者可能会用错这个方法。但实践证明这个设计导致了很多额外的检查代码却没什么好处,因此是得不偿失的。

12. Design it twice.

  无需翻译了,感觉这个更多是一种态度和工作方式。我们不要拘泥于初始想法,而是要考虑多个方案,从中选择或者整合最好的方案,并且应当在项目开发之后进行复盘。这肯定是提高软件设计质量的好方法,恐怕也是提升任何能力的好方法。

13. Comments should describe things that are not obvious from the code. 翻译:注释要描述那些在代码中不明显的内容。

  作者在书中花了大量篇幅来描述注释,认为注释应当是接口重要的一部分,而且普通用户应当可以通过注释来完整的理解接口。我悲观的认为在大部分实际应用开发中难以推行,尤其是快速迭代的应用中。相比较而言我觉得保持代码整洁可能是更加实用的。当然一些设计文档肯定还是必须的。

14. Software should be designed for ease of reading, not ease of writing.

  就是要尽量保持代码清晰可读,哪怕写的时候会稍微麻烦一点。完全同意。而且,代码可读重点是针对人,而不是针对机器。

15. The increments of software development should be abstractions, not features. 翻译:软件开发中的增量应当是抽象而不是特性。

  这是作者讨论敏捷开发中提到大家要注意的地方。这个观点初读起来有点拗口,我理解就是要在设计的过程中尽量采用通用的设计,不但可以满足当前特性的需求,也应当能够满足(可预见的)相关特性的需求。和第7条也是呼应的。如果每次的增量都只是一些特性开发,没有努力的建立统一模型,那么系统中肯定就是会充满各种补丁,也就是作者所说的战术编程。

  最后,我觉得真如书中所述,所有的这一切工作都是为了控制软件的复杂度。而复杂度本身是一个很复杂的事情,在不同的团队、不同的场景、不同的任务下,复杂度会受到不同因素的影响。因此不要拘泥于书中讨论的方法,记得“控制复杂度”才是真正重要的。

评价:中立好评差评
【已有2位读者发表了评论】

┃ 《A Philosophy of Software Design》读后感锦集的相关文章

┃ 每日推荐