[读书笔记] 代码整洁之道(四): 类
第十章 类
-
类的组织
类的编写因符合自顶向下原则,从一组变量列表开始:public static的常量,然后private static 变量,以及private 变量。变量列表之后是公共函数,然后公共函数调用的私有工具函数紧随其后。
封装:保持变量和工具函数的私有性。 -
类应该短小
跟函数一样,类的首要规则是要更短小。
对于函数我们可以通过行数来衡量大小。对于类,我们通过权责来衡量。
类的名称应该描述其权责。
单一权责原则SRP:类或模块应且只有一条加以修改的理由。鉴别权责(修改理由)可以帮助我们在代码中认识并创建出更好的抽象。系统应该由许多短小的类而不是少量巨大的类组成。每个小类封装一个权责,只有一个修改的原因,并于少数其他类仪器协同达成期望的系统行为。
内聚:类应该只有少量的实体变量。类中每个方法都应该操作一个或多个这种实体变量。通常而言,方法操作的变量越多,内聚性越大。内聚性高,意味着类中的方法和变量互相依赖、互相组合成一个逻辑整体。
保持内聚性就会得到许多短小的类:因为将大函数切割为小函数,那么意味着一些函数的私有变量要进行传递,比较简便的方法就是将这些私有变量提升为实体变量,这样不要传递变量就能拆分函数。但是这样就会导致类丧失内聚性,因为多了很多只允许少量函数共享而存在的实体变量。既然有些函数需要共享某些变量,那就应该让它们拥有自己的类,所以当类丧失了内聚性,就拆分,这样程序就会更加由组织,结构更加透明。 -
为了修改而组织
一般系统大多会一直持续修改,如果类组织的不好,会导致每次修改都会影响到其他部分的风险。
这个例子是一个必须打开修改的类:public class Sql { public Sql(String table, Column[] columns) public String create() public String insert(Object[] fields) public String selectAll() public String findByKey(String keyColumn, String keyValue) public String select(Column column, String pattern) public String select(Criteria criteria) public String preparedInsert() private String columnList(Column[] columns) private String valuesList(Object[] fields, final Column[] columns) private String selectWithCriteria(String criteria) private String placeholderList(Column[] columns) }
当要添加新语句类型时,就要修改Sql类。改动单个语句类型时,也要进行修改。所以很显然的违反了SRP原则。
修改:将Sql类的每个接口方法都重构到从Sql类派生出来的类中了。私有方法如valueList,直接移到需要用他们的地方。公共私有行为被划分到独立的两个工具类。
abstract class Sql { public Sql(String table, Column[] columns) abstract public String generate(); } class CreateSql extends Sql { public CreateSql(String table, Column[] columns) @Override public String generate(){}; } class SelectSql extends Sql { public SelectSql(String table, Column[] columns) @Override public String generate(){}; } class InsertSql extends Sql { public InsertSql(String table, Column[] columns, Object[] fields) @Override public String generate(){}; private String valuesList(Object[] fields, final Column[] columns){}; } class SelectWithCriteriaSql extends Sql { public SelectWithCriteriaSql(String table, Column[] columns, Criteria criteria) @Override public String generate(){}; } class SelectWithMatchSql extends Sql { public SelectWithMatchSql(String table, Column[] columns, Column column, String pattern) @Override public String generate(){}; } class FindByKeySql extends Sql { public FindByKeySql(String table, Column[] columns, String keyColumn, String keyValue) @Override public String generate(){}; } class PreparedInsertSql extends Sql { public PreparedInsertSql(String table, Column[] columns) @Override public String generate() {}; private String placeholderList(Column[] columns) } class Where { public Where(String criteria) public String generate() } class ColumnList { public ColumnList(Column[] columns) public String generate() }
这样需要添加update语句时,现存类不需要修改,直接添加Sql子类UpdateSql。经过修改,Sql支持SRP,支持OCP开放闭合原则:类应当对扩展开放,对修改封闭。
隔离修改:
具体类包含实现细节(代码),而抽象类则只呈现概念。依赖于具体细节的客户类,当细节改变时,就会有风险。我们可以借助接口和抽象类来隔离这些细节带来的影响。依赖倒置原则DIP:认为类应该依赖于抽象而不是依赖于具体细节。