阅读本文有两种原因:第一,你是个程序员;第二,你想成为更好的程序员。你如果想成为更好的程序员,那就请细细品味文章内容,它绝不会让你失望。
代码整洁之道教给大家如何编写整洁的代码,而不仅仅是能运行的代码,这对于编程者而言很重要。我在读这本书的第一遍时没什么感觉,但在读第二遍时觉得它确实挺不错的,如果有机会的话我还会读第三遍。下面是我在读书过程中摘录的精华内容,希望大家认真对待。各位看官如果读完本文觉得书中的精华内容挺合自己的胃口,那就可以抽出时间认真地读一下这本书。
1、对象把数据隐藏于抽象之后,暴露操作数据的函数。数据结构暴露其数据,没有提供有意义的函数。回头再读一遍,留意这两种定义的本质。他们是对立的,这种差异貌似渺小,但却有深远的意义。
2、对象与数据结构的二分原理:
过程式代码(使用数据结构的代码)便于在不改动既有数据结构的前提下添加新函数。面向对象代码便于在不改动既有函数的前提下添加新类。
反过来讲也说得通:
过程式代码难以添加新的数据结构,因为必须修改所有函数。面向对象代码难以添加新函数,因为必须修改所有类。
过程式代码示例:
public class Square{
public Point topLeft;
public double side;
}
public class Rectangle{
public Point topLeft;
public double height;
public double width;
}
public class Circle{
public Point topLeft;
public double radius;
}
public class Geometry{
public double PI = 3.1415926;
public double area(Object shape)throws NoSuchShapeException
{ if(shape instanceof Square){
Square s = (Square)shape;
return s.side*s.side;
}
else if(shape instanceof Rectangle){
Rectangle r = (Rectangle)shape;
return r.width*r.height;
}
else if(shape instanceof Circle){
Circle c = (Circle)shape;
return PI*c.radius*c.radius;
}
throw new NoSuchShapeException();
}
}
如果此时想在Geometry类中添加一个primeter()函数,这实现起来很简单,现有形状不会因此而受影响。
public class Geometry{
public double PI = 3.1415926;
public double area(Object shape)throws NoSuchShapeException
{ if(shape instanceof Square){
Square s = (Square)shape;
return s.side*s.side;
}
else if(shape instanceof Rectangle){
Rectangle r = (Rectangle)shape;
return r.width*r.height;
}
else if(shape instanceof Circle){
Circle c = (Circle)shape;
return PI*c.radius*c.radius;
}
throw new NoSuchShapeException();
}
public double primeter(Object shape)throws NoSuchShapeException
{ if(shape instanceof Square){
Square s = (Square)shape;
return 4*s.side;
}
else if(shape instanceof Rectangle){
Rectangle r = (Rectangle)shape;
return r.width*2+r.height*2;
}
else if(shape instanceof Circle){
Circle c = (Circle)shape;
return 2*PI*c.radius;
}
throw new NoSuchShapeException();
}
}
如果此时想添加一个新形状,那Geometry类中的area()和primeter()都要修改,如果Geometry类中有其他方法的话,那要修改的类可能会更多。
所以说过程式代码难以添加新的数据结构,因为必须修改所有函数。
面向对象代码示例:
public class Square implements Shape{
private Point topLeft;
private double side;
public double area(){
return side*side;
}
}
public class Rectangle implements Shape{
private Point topLeft;
private double height;
private double width;
public double area(){
return height*width;
}
}
public class Circle implements Shape{
private Point topLeft;
private double radius;
public double PI = 3.1415926;
public double area(){
return PI*radius*radius;
}
}
如果此时想添加一个新形状,这实现起来很简单,现有形状不会因此而受影响。
public class Square implements Shape{
private Point topLeft;
private double side;
public double area(){
return side*side;
}
}
public class Rectangle implements Shape{
private Point topLeft;
private double height;
private double width;
public double area(){
return height*width;
}
}
public class Circle implements Shape{
private Point topLeft;
private double radius;
public double PI = 3.1415926;
public double area(){
return PI*radius*radius;
}
}
public class NewShape implements Shape{
private Point topLeft;
......
public double area(){
......
}
}
如果此时想添加一个primeter()函数,那就要修改Shape接口,继承了Shape接口的现有形状也全部需要修改。
所以说面向对象代码难以添加新函数,因为必须修改所有类。
3、著名的德墨忒尔律认为:模块不应该了解他所操作对象的内部情况。对象隐藏数据,暴露操作,这意味着对象不应该通过存取器暴露其内部结构。
德墨忒尔律认为,类C的方法f只应调用以下对象的方法:
- C
- 由f创建的对象
- 作为参数传递给f的对象
- 由C的实体变量持有的对象
如果数据结构只简单地拥有公共变量,没有函数,而对象拥有私有变量和公共函数,那这个问题就不容易混淆。
4、每个单元(对象或方法)应当对其他单元只拥有有限的了解。
举例说明:
a. 假设我在便利店购物。付款时,我是应该将钱包交给收银员,让她打开并取出钱?还是我直接将钱递给她?
b. 人可以命令一条狗行走,但是不应该直接指挥狗的腿行走,应该由狗去指挥控制他的腿如何行走。
抛开所有细节不不谈,代码整洁之道总体来说可以分为以下7点:
- 运行所有测试
- 减少重复代码
- 提高表达力
- 提早构建简单抽象
- 类和方法都只做好一件事
- 尽量减少类和方法的数量
- 努力,让营地比你来时更干净。努力,让世界比你来时更干净。努力,让代码比你签出时更干净。