第2章 有意义的命名
名副其实
避免误导
有意义的区分。不要有废话,不要有仅靠数字的标识,名称之间有区分度。
名称长端应与其作用域大小相对应。
给每个抽象概念选一个词,并且一以贯之。
只要短名称足够清楚,就比长名称好。
第3章 函数
短小,更短小。
只做一件事。
语句要在同一抽象层上。
函数参数尽量不超过2个;不要有标识参数。
无副作用。
使用异常替代返回错误码。
最好把try catch代码的主题部分抽离形成另一个函数。错误处理就是一件事。
不要重复。
第4章 注释
用代码来阐述而不是用注释
好注释:提供信息、对意图的解释、警示、TODO
坏注释:多余、误导、不必都包含javadoc、日志式、废话、信息过多、包含的联系并不明显
能用函数或变量表达含义就别用注释
注释掉的代码,不应该存在。
第5章 格式
垂直格式:概念间垂直方向上间隔、变量声明应尽可能靠近其使用位置、函数间调用则最好将函数放在相近位置
横向格式:使用空格将紧密事物连接到一起,也将相关性较弱的事物区隔开。
第6章 对象和数据结构
public interface Vehicle {
double getFuelTankCapacityInGallons();
double getGallonsOfGasoline();
}
public interface Vehicle {
double getPercentFuelRemaining();
}
过程式代码便于在不改动既有数据结构的前提下添加新函数;面向对象代码便于在不改动既有函数的前提下添加新类。
模块不应了解它所操作对象的内部情形,不过如果是数据结构,可能会了解。
对象暴露行为,隐藏数据,便于添加新对象类型而无须修改既有行为,同时难以在既有对象中添加新行为;数据结构暴露数据,没有明显行为,便于向既有数据结构添加新行为,同时难以向既有函数添加新数据结构。
第7章 错误处理
某种意义try代码块就像是事务,catch代码块将程序维持在一种持续状态。
已检查的代价就是违反开闭原则,使用未检异常RuntimeException
不返回null值,也不传递null值
在业务逻辑和错误处理代码之间有良好的区隔
第8章 边界
避免从公共API中返回边界接口,或将边界接口作为参数传递给公共API
例如在系统中不受限制地传递Map<String, Sensor>的实体,以为着当Map接口被修改时,有许多地方都要跟着改。
// 这样能稍微好一点
publc class Sensors {
private Map sensors = new HashMap();
public Sensor getById(String id) {return (Sensor) sensors.get(id);}
}
编写测试来遍览和理解第三方代码。
边界上的代码需要清晰地分割和定义了期望的测试。应该避免我们的代码过多地了解第三方代码中的特定信息。
第9章 单元测试
测试直达,只用到那些真正需要的数据类型和函数。
每个测试函数中只测试一个概念。
FIRST原则,快、独立、可重复、自足验证(有布尔值输出,不应该通过查看日志文件来确认是否通过)、及时
第10章 类
单一权责,类或模块应该有且只有一条加以修改的理由
如果一个类中的每个变量都被每个方法所使用,则该类具有最大的内聚性
保持内聚性就会得到许多短小的类
为了修改而组织,对类加以组织,降低修改的风险。依赖接口而不是依赖实现。
第11章 系统
将系统的构造与使用分开,每个应用程序都应该留意起始过程 (工厂)
扩充,提到了代理、AOP
最佳的系统架构由模块化的关注面领域组成,每个关注面均用纯Java对象实现。不同的领域之间用最不具有侵害性的方面或类方面工具整合起来。
延迟决策至最后一刻也是好手段。
第12章 迭进
通过迭进设计达到整洁目的。
运行所有测试
递增式地重构代码
不可重复
表达了程序员的意图
尽可能减少类和方法的数量。
第13章
并发防御原则
分离并发相关代码与其他代码
限制临界区数量
使用数据副本避免共享数据
线程应尽可能地独立。每个线程处理一个客户端请求,从不共享的源头接纳所有请求数据,存储为本地变量。
警惕同步方法之间的依赖
保持同步区域微小
很难编写正确的关闭代码
测试线程代码
偶发失败都需要注意,最好假设偶发错误根本不应该存在
不要同时追踪非线程缺陷和线程缺陷。
编写可插拔的线程代码,在不同的配置环境下运行
允许线程数量可调整
运行多余处理器或处理器核心数量的线程
装置代码,增加对Object.wait() Object.sleep()等方法调用,改变代码执行顺序测试并发代码中的缺陷。
第14章
毁坏程序的最好方法之一就是以改进之名大动其结构。
每次修改都必须保证系统能像以前一样工作。
第17章 味道与启发
注释
环境
函数
一般性问题
重复
不正确的边界行为。别依赖直觉,追索每种边界条件,并编写测试。
在错误的抽象层级上的代码。创建分离较高层级一般性概念与较低层级细节概念的抽象模型。
基类依赖派生类
信息过多。限制类或模块中暴露的接口数量。
垂直分隔。 变量和函数应该靠近被使用的地方定义。
前后不一致。命名、约定保持一致。
人为耦合。 不互相依赖的东西不该耦合。
特性依恋。类的方法只应对其所属类中的变量和函数感兴趣,不该垂青其他类中的变量和函数。
晦涩的意图。
位置错误的权责。
不恰当的静态方法。静态方法应当只在当个实体上操作,不在任何特定对象上操作。
理解算法,而不是使用大量的if else
把逻辑依赖改为物理依赖,依赖者模块不应该对被依赖者模块有假定,它应当明确地询问后者全部信息。
public class HourlyReporter {
private HourlyReportFormatter formatter;
private List<LineItem> page;
private final int PAGE_SIZE = 55; // 这是假定知道页面尺寸,是一种逻辑依赖。可以改成getMaxPageSize()的新方法来物理化依赖。
public HourlyRepoter(HourlyReportFormatter formatter) {}
}
用多态替代if else
用命名常量替代魔术数
准确。确认自己足够准确。明确自己为何要这么做。
封装条件。把解释了条件意图的函数抽离出来。
避免否定性条件。
函数值该做一件事。
掩蔽时序耦合。有必要使用时序耦合时,不应该掩蔽它。
封装边界条件。 把处理边界条件的代码集中到一处。
函数应该只在一个抽象层级上。
public String render() throws Exception {
StringBuffer html = new StringBuffer("<hr");
if (size > 0) html.append(" size=\").append(size + 1).append("\");
return html.toString();
}
public String render() throws Exception {
HtmlTag hr = new HtmlTag("hr");
if(extraDashes > 0) hr.addAttribtue("size", hrSize(extraDashes));
return hr.html();
}
在较高抽象层级的默认常量或配置值,不要将它埋藏到较低层级的函数中。
避免传递浏览。 a.getB().getC()
除非里面有数据结构类,否则不应该传递。
名称
采用描述性名称
名称应与抽象层级相符
无歧义名称
为较大作用范围选用较长名称
避免编码
测试
最后编辑于 :2023.07.17 17:53:50
©著作权归作者所有,转载或内容合作请联系作者 平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。