1.并发是一种解耦策略,它帮助我们把做什么(目的)和何时(时机)做分解开
2.解耦目的与时机能明显地改进应用程序的吞吐量和结构
3.单线程程序许多时间花在等待web套接字I/O结束上面,通过采用同时访问多个站点的多线程算法,就能改进性能
并发总能改进性能:只在多个线程或处理器之间能分享大量等待时间的时候管用
编写并发程序无需修改设计:可能与单线程系统的设计极不相同
在采用web或ejb容器时,理解并发问题并不重要
并发会在性能和编写额外代码上增加一些开销
正确的并发是复杂的,即使对于简单的问题也是如此
并发缺陷并非总能重现,所以常被看做偶发事件而忽略,未被当做真的缺陷看待
并发常常需要对设计策略的根本性修改
1.单一权责原则
2.限制数据作用域
3.使用数据副本
4.线程应尽可能独立
使用类库提供的线程安全群集
使用executor框架(executor framework)执行无关任务
尽可能使用非锁定解决方案
有几个类并不是线程安全的
1.平静关闭很难做到,常见问题与死锁有关,线程一直等待永远不会到来的信号
2.建议:尽早考虑关闭问题,尽早令其工作正常
1.建议:编写有潜力曝露问题的测试,在不同的编程配置、系统配置和负载条件下频繁运行。如果测试失败,跟踪错误。别因为后来测试通过了后来的运行就忽略失败
2.将伪失败看作可能的线程问题:线程代码导致“不可能失败的”失败,不要将系统错误归咎于偶发事件
3.先使非线程代码可工作:不要同时追踪非线程缺陷和线程缺陷,确保代码在线程之外可工作
4.编写可插拔的线程代码,能在不同的配置环境下运行
5.编写可调整的线程代码:允许线程依据吞吐量和系统使用率自我调整
6.运行多于处理器数量的线程:任务交换越频繁,越有可能找到错过临界区域导致死锁的代码
7.在不同平台上运行:尽早并经常地在所有目标平台上运行线程代码
8.装置试错代码:增加对Object.wait()、Object.sleep()、Object.yield()、Object.priority()等方法的调用,改变代码执行顺序,硬编码或自动化
复杂要人命。它消磨开发者的生命,让产品难以规划、构建和测试。
将构造和使用分开的方法:
(1)分解main,将系统中的全部构造过程搬迁到main或者main模块中:main函数创建对象,再将对象传递给应用程序,应用程序只管使用,对构造一无所知;
(2)如果应用程序需要负责确定何时创建对象,可以创建抽象工厂,让应用程序控制实体创建的时机;
(3)依赖注入,控制反转IoC是依赖管理的手段,它将应用需要的依赖对象的创建权责从对象中拿出来,放在一个专注于此事的对象中,并通过依赖注入(赋值器)将依赖对象传递给应用;
扩容:
面向方面编程(AOP),Java中三种方面和类似方面的机制:代理,纯AOP框架,AspectJ。
(1)java代理:适用于简单情况,如在单独对象或类中包装方法调用。代码量和复杂度是代理的两大弱点。
(2)纯Java AOP框架,如Spring AOP、JBoss AOP
(3)AspectJ:提供将方面作为模块构造处理支持的Java扩展
最佳系统架构由模块化的关注面领域组成,每个关注面均用纯Java(或其他语言)对象实现。不同领域之间用最不具有侵害性的方面或类方面工具整合起来。这种架构能测试驱动,就像代码一样。
拥有模块化关注面的POJO系统提供的敏捷能力,允许我们基于最新的知识做出优化的、时机刚好的决策。决策的复杂性也降低了。
领域特定语言(DSL)允许所有抽象层级和应用程序中的所有领域,从高级策略到底层细节,使用POJO来表达。
1.简单设计规则
(1)运行所有测试
不可测试的系统不可验证,不可验证的系统,绝不能部署。
(2)不可重复
通过抽取或是模板方法整合重复代码。
(3)表达力
选用好的名称来表达。
(4)尽可能少的类
遵循标准的JAVA约定,类应该从一组变量列表开始。变量顺序:公共静态常量,私有静态变量,私有实体变量。很少有公共变量。公共函数应该在变量列表后面。公共函数调用的私有工具函数紧随在该公共函数的后面。
类应该短小
类的名称应该描述其权责,类名正是判断类的长度的第一个手段。如果无法为某个类命以精确的名称,这个类大概就太长了。类名越含混,该类越有可能拥有过多的权责。系统应该由许多短小的类而不是少量巨大的类组成。每个小类封装一个权责,只有一个修改的原因,并与少数其他类一起协同成期望的系统行为。
内聚性高:类中的方法和变量相互依赖、互相结合成一个逻辑整体。
当类丧失了内聚性,就需要拆分它。将大函数拆分为许多小函数,往往也是将类拆分为许多个小类的时机。程序会更有组织,也会拥有更为透明的结构。
在整洁的系统中,对类进行组织,以降低修改的风险(通过扩展而非修改现有的代码来添加新的特性)。
隔离修改:遵循依赖倒置原则(DIP),应该依赖于抽象而非具体细节。
作者这里遵循了重要的事情说三遍的原则:可读性,可读性,可读性。如何做到可读,那就是要保证测试代码同其他代码一样,明确,简洁,足具表达力,这也是这本书一直强调的事情。
测试代码和生产代码一样重要。需要被思考,设计和照料。应该和生产代码一样整洁。
在尽可能减少每个概念的断言数量的同时,最好能做到每个测试函数中只测试一个概念。
使用第三方代码:使用第三方代码时,如果有边界接口,可将其保留在类或近亲中,避免从公共API中返回边界接口,或者将其边界接口作为参数传给公共API.
浏览和学习边界:在利用第三方程序包时,没有测试第三方代码的职责,但为要使用的第三方代码编写测试,可能最符合我们的利益。不要在生成代码中实验新东西,而是编写测试来遍览和理解第三方代码。
学习性测试的好处不只是免费:学习性测试是一种精确试验,帮助我们增进对api的理解。当第三方程序包发布了新版本,我们可以运行学习性测验,看看程序包的行为有没有改变。学习性测试确保第三方程序包按照我们想要的方式工作。一旦整合进来,就不能保证第三方代码总与我们的需要兼容。如果第三方程序包的修改与测试不兼容,我们也能马上发现。
整洁的边界:在使用我们控制不了的代码时,必须加倍小心,确保未来的修改代价不会太大。边界上的代码需要清晰的分隔和定义了期望的测试。
隐藏实现关乎抽象,类并不简单地取值器和赋值器将变量推向外间,而是暴露抽象接口,以便用户无需了解数据的实现就能擦做数据本体。
对象把数据隐藏于抽象之后,暴露操作数据的函数。数据结构暴露其数据,没有提供有意义的函数。回头再读一遍,留意这两种定义的本质。他们是对立的,这种差异貌似渺小,但却有深远的意义。对象与数据结构的二分原理:
模块不应了解它所操作对象的内部情形。对象隐藏数据,暴露操作。者意味着对象不应通过存取其暴露其内部结构,因为这样更像是暴露而非隐藏其内部结构。
最为精练的数据结构,是一个只有公共变量,没有函数的类。这种数据结构有时被称为数据传送对象,或DTO。
对象暴露行为,隐藏数据。便于添加新对象类型而无需修改既有行为,同时也难以在既有对象添加新行为。数据结构暴露数据,没有明显的行为。便于向既有数据结构添加新行为,同时也难以向既有函数添加数据结构。
错误处理很重要,但如果它搞乱了代码逻辑,就是错误的做法。
(1)使用异常而非返回码;
(2)在编写可能抛出异常的代码时,先写try-Catch-Finally语句;
(3)使用不可控异常:C使用可控异常的代价是:违反“开放/闭合原则”。对于一般性 应用开发,其依赖成本要高于收益;
(4)给出异常发生的环境说明:应创建信息充分的错误消息,并和异常一起传递出去,以便判断错误的来源和处所;
(5)依调用者需要定义异常类(看异常如何被捕获):如打包调用API确保返回通用异常类型;
(6)定义常规流程:创建一个类或配置一个对象,用来处理特例;
(7)别返回null值;
(8)别传入null值:在大多数编程语言中,没有良好的方法能对付由调用者意外传入的null值。恰当的做法是,禁止传入null值。
代码格式关乎沟通,而沟通是专业开发者的头等大事。
阅读代码都是从上往下,从左往右读的.在一个类中,在封包声明,导入声明,和每个函数之前都应该使用一个空白行来隔开.这可以给阅读带来愉悦的感受.
空白行隔开了概念,每个空白行都是一条线索,标示出一个新的独立的概念,阅读代码的时候,我们的目光总是容易停留在空白行的前一行或后一行.
一行代码应该多宽?应当遵循无需拖动滚动条到右边的原则,每行代码控制在120个字符以内是良好的风格.
注释不能美化糟糕的代码:如果能用代码表达清楚,尽量不要用注释。注释只是在弥补我们在用代码表达意图时遭遇的失败。带有少量注释的整洁而又表达力的代码,比带有大力量注释的零碎而复杂的代码像样的多。与其花时间编写注释糟糕的代码,不如花时间让代码变得更加的整洁。
总之:把系统当作故事讲,不是当作程序写。
# 以前觉得这样命名接口没毛病,一看就知道是接口,读完此文才知道:I被滥用到说好听点是干扰,根本是废话
public interface IUserService {}
糟糕的代码如同沼泽一般,难于逾越,不仅会毁掉一个团队,更可能会毁掉一个公司。因为勒布朗法则:稍后等于永不。故不要想着回头来优化代码,要先设计好,编写的代码经调整优化测试后方可提交。要有专业的程序员素养和态度。做一个“有代码感”的程序员,从混乱中看出其他的可能与变化,选出最佳方案,指导我们制订修改行动计划,按图索骥。
整洁的代码读起来简单令人愉悦,只做好一件事。每个函数,每个类和每个模块都全神贯注于一事,不受四周细节的干扰和污染。果断决绝,没有不必要的细节。在意自己的代码,对自己的代码负责。学习他人的优点,不要故步自封。不断学习,不断实践,方可做到“让营地比你来时更干净”。