以下是编写干净代码的注意事项列表,即可维护和可扩展的代码。
命名是编写干净代码最困难也是最重要的部分。名字应该清楚地表达意图,这里的假设是,代码库中涉及的每个人都有相同的文化背景,但在实践中并不总是这样。一些一般提示:
函数或方法是编程的基本构件。事实上,程序的内部操作通常主要由函数在相互调用时将数据推送到堆栈和从堆栈中弹出数据组成。有时,需要在堆上为必须在函数调用中存活的数据分配内存。
调用函数时,会创建一个堆栈帧来支持函数的执行。堆栈帧包含函数的局部变量和由其调用者传递给函数的参数。该框架还包含允许被调用函数(被调用者)安全返回调用者的内务信息。堆栈的确切内容和布局因处理器体系结构和函数调用约定而异。
DrawSquare调用的DrawLine函数的调用堆栈示例。
函数应该很小,它们应该只做一件事:只有1级缩进-高度嵌套的函数应该重构为子例程。
没有副作用!Public int sum(int a,int b){int result=a+b;setGui();//这是不可测试的,并且引入了隐藏的依赖关系!返回结果;}。
不返回NULL-调用者将需要始终检查混乱的p代码,请考虑使用特殊情况的返回值
除可为Null或可选类型的情况外,错误条件的首选异常可用。
参数越少越好-参数越多,需要编写的复杂性和测试用例就越多。
在OOP中需要注意的一个重要但很有说服力的地方是,对象将其数据隐藏在抽象后面,并公开对该数据进行操作的函数,而数据结构则公开它们的数据,并且没有有意义的函数。好的OOP需要知道何时使用对象和何时使用数据结构。考虑以下示例:
公共类点{public Double x;public Double y;}公共接口点{Double getX();Double Gty();void setCartesian(Double x,Double y);Double getR();Double getTheta();void setPolar(Double r,Double theta);}。
在第二点定义中,实现所使用的坐标系是未知的,并且不一定是笛卡尔坐标系或极坐标!
避免使用边界接口,例如,不返回Map,而是将其包装在一个类(传感器)中以封装实现。
注释应该只用于澄清或放大--一般避免使用,让代码来说话!
更喜欢异常而不是错误代码-错误代码有溢出到整个系统的习惯
最后,过程性代码使添加新数据结构变得困难,因为所有函数都必须更改。OO代码使添加新函数变得困难,因为所有类都必须更改。同样,编写干净的代码需要洞察何时使用哪种风格的编程。
单元测试应遵循F.I.R.S.T原则,即它们应快速、独立于任何外部依赖项或手动设置、可重复、自我确认(无手动检查验证)和及时(在编写生产代码之前运行)。编写干净测试的一些一般提示:
测试应该是可读的,这可能意味着放松某些产品代码对性能的限制。
您编写的测试可能不会超过失败的程度,不编译就是失败。
您编写的产品代码不能超过通过当前失败测试所需的数量。
类应该遵循开放-关闭原则--对扩展开放,对修改关闭。考虑以下示例,其中我们编写了一个计算矩形集合总面积的AreaCalculator。Public class Rectangle{public Double Width;public Double Height;}public class AreaCalculator{public Double culateArea(集合矩形){Double Result=0;for(Rectangle r:Rectles){Result+=r.width*r.Height;}返回结果;}}。
现在我们想扩展这个函数来计算圆的面积。我们的新函数现在看起来如下:public抽象类Shape{}public class Rectangle Extended Shape{public Double Width;public Double Height;}Public类Circle扩展Shape{public Double Radius;}Public Class AreaCalculator{public Double culateArea(Collection Shares){Double Result=0;for(Shape s:Shares){if(S Instanceof Rectangle){Rectangle r=(Rectangle)s;Result+=r.width*r.Height;}Else{Circle c=(Circle)s;Result+=c.Radius*c.Radius*Math.PI;}}返回结果;}}。
将其进一步扩展以计算三角形的面积现在需要对culateArea方法进行另一次修改,即它不能进行扩展。我们可以通过在形状数据结构上引入面积方法来改变这一点。我们的代码如下所示:public抽象类shape{抽象Double Area();}public class Rectangle扩展Shape{public Double Width;public Double Height;@Override public Double Area(){return width*Height;}}public class Circle扩展Shape{public Double Radius;@Override public Double Area(){return Radius*RADIUS*Math.PI;}}public class AreaCalculator{public Double culateArea(Collection Shares){Double Result=0;for(Shape s:Shares){result+=s.area();//注意简单}返回结果;}}
在构建大型软件系统时,尽量避免预先进行大型设计-使用依赖项注入容器将事务、日志等横切容器与业务逻辑分开