目录
1.6 童子军军规
光写好代码可不够,必须时刻保持代码整洁,如果每次签入时,代码比签出时干净,那么代码就不会腐坏,清理并不一定会花时间,也许只是改好一个变量名,拆分一个有点过长的函数,消除一点代码重复度,清理一个嵌套的if语句。
第二章:有意义的命名
2.2 名副其实
我们应该选择指明了计量对象和计量单位的名称:
int elapsedTimeInDays;
int daysSinceCreation;
2.4 做有意义的区分
废话是另一种没有意义的区分。假设有一个product类,如果还有一个productinfo或productdata类,那么他们的名称虽然不同,意义却无区别,info和data就像a,an,the一样,是意义含混的废话。
注意,只要体现出有意义的区分,使用a和the这样的前缀就没错。如果缺少明确约定,变量moneyAmout和money没有区别,customerinfo和customer没有区别,accountData和account没区别,theMessage和message没区别。
2.16 添加有意义的语境
只有一个孤零零的state变量,没人知道这表示什么意思,添加语境-》addrState,这能让读者知道这是某个更大结构的一部分,当然更好的解决方案是创建名为Address的类,这样即使是编译器,也知道这是率属于某个更大的概念了。
上述图示,可以变量在这边完全不知道是做什么的,但是我们可以给他们封装成一个类,把三个变量做成该类的成员字段
第三章:函数
3.2 只做一件事
代码清单3-3中,看上去这个函数是有3件事情,注意,这三件事都是该函数名下同一个抽象层上。如果函数只是做了函数同一抽象层上的步骤,则函数还是只做了一件事情。
如何判断是否是一个抽象层? 看函数是否能再拆出一个函数。
3.3 每一个函数一个抽象层
自顶向下读代码:向下规则
我们想要让代码拥有自顶向下的阅读顺序,我们想要让每个函数后面都跟着位于下一抽象层级的函数,这样一来,在查看函数列表时,就能循抽象层向下阅读了。
3.4 switch语句
我们总无法避开switch语句,不过能够确保每个switch都埋在较低抽象层级,而且永远不重复。当然,我们利用多态来实现这一点。
该函数违反了单一职责原则,开闭原则,每当添加新的类型的时候,必须修改它。该问题的解决方案,是将switch埋到抽象工厂下面,不让任何人看到,该工厂使用switch语句问Employee的派生物创建适当的实体,而不同的函数,如calculatePay,isPayday和deliverPay等,则是由Employee接口多态地接受派遣。
3.7 无副作用
副作用是一种谎言,函数只承诺做一件事情,但还是会做其他被隐藏起来的事情,有时,它会对自己类中的变量做出未能预期的改动。如下图所示,checkPassword中,副作用就是session.initialize(); checkpassword顾名思义是检查密码,该名称并未暗示它会做初始化这次会话的操作。
输出参数:在面向对象的编程岁月里,有时也需要输出参数,然而,面向对象语言对输出参数额大部分需求已经消失了,因为this也有输出函数的意味在内,换言之,可以使用类似report.appendFooter();普遍而言,应该避免使用输出参数,如果函数必须要修改某种状态,就修改所属对象的状态吧。
第六章:对象和数据结构
6.1 数据抽象
6-2代码漂亮之处在于,你不知道该实现会是在矩形坐标中还是在极坐标中,可能两个都不是,然而,该接口还是明白无误的呈现出了一种数据结构。
隐藏实现并非只是在变量之间放上一层函数那么简单,隐藏关乎抽象,类并不简单地调用取值器和赋值器将变量推向外间,而是暴露抽象接口,以便用户无需了解数据结构的实现就能操作数据本体。
6.2 数据,对象的反对称性
下面两个例子说明了对象与数据结构之间的差异,对象把数据结构隐藏于抽象之后,暴露操作数据的函数。数据结构暴露其数据,没有提供有意义的函数。
代码清单6-5 形状都是简单的数据结构,没有任何行为,所有行为都是在Geometry类中。
代码清单6-6,area()方法时多态的,不需要有Geometry类,如果添加一个新的形状,现有的函数都不会受到影响,而添加新函数时,所有类都得修改。
从上面的例子可以看出,对象和数据结构是二分的:过程是代码在于不改动现有数据结构的情况下,添加新函数。 面向对象代码在于不改动既有函数的前提下添加新类
6.3 得墨忒耳定律
模块不应该了解它操作对象内部的事情。如上节所述,对象隐藏数据,暴露操作。更准确的说,德墨忒尔定律认为,类C的方法f只应该调用以下对象的方法:
- 对象C的属性
- 对象C持有的对象
- 方法参数传入对象
- 在方法f中定义的临时对象
6.4 数据传送对象
Active Record是一种特殊的DTO形式,它们拥有公共变量的数据结构。经常发现开发者往往会将这类数据结构中塞进业务规则方法,把这类数据结构当成对象来用。这将数据结构和对象混为一体了。
当然了,解决办法就是把Active Record当做数据结构,并创建包含业务规则、隐藏内部数据的独立对象。
第十章:类
10.1 类的组织
类应该从一组变量开始,如果有公共静态常量,应该先出现,然后是私有静态变量,以及私有实体变量,很少会有公共变量。
公共函数跟在变量之后,并且应该将公共函数调用的私有函数紧随在该公共函数之后,这符合了自顶向下原则。
10.2 类应该短小
10.2.1 单一职责原则
类的名字应当描述其权责。实际上,命名正是帮助判断类的长度的一个手段。如果无法为某个类命以精确的名称,这个类大概就太长了。类名越混淆,该类就越有可能拥有过多的权责。
有大量短小类的系统并不比有少量庞大类的系统拥有更多移动部件,其数量大致相等。问题是:你是想把工具规整到许多抽屉,每个抽屉中装有定义和标记良好的组件箱中呢,还是想要少数几个随便能把所有东西扔进去的抽屉?
再强调一下:系统应该由许多短小的类而不是少量而巨大的类组成,每个小类封装一个权责,只有一个修改的原因,并与少数其他类一同达成期望的系统行为。
10.2.2 内聚
类应该只有少量的实体变量。类中的每个方法都应该操作一个或多个这种变量。通常而言,方法操作的变量越多,就越粘聚到类上,如果一个类中的每个变量都被每个方法使用,则该类具有最大的内聚性。
10.2.3 保持内聚就会得到许多短小的类
10.3 为了修改而组织
代码清单10-9中,这个类还没有写完,目前还不支持update语句,当需要添加update语句的时候,我们就得“打开”这个类进行修改。
这样的代码违背了SRP原则,代码清单10-9如何修改呢?sql类中的每个方法都重构到了Sql的派生类中