重构-改善既有代码的设计,这本书是不少公司要求
JAVA
程序员必读的三本书之一(另外两本书是《Java编程思想》和《Effective Java》)
前言
看到别人的代码时感受就像屎同样,有一种强烈的想重写的冲动,但必定要压制住这种冲动,你彻底重写,可能比原来的好一点,但浪费时间不说,还有可能引入原来不存在的Bug,并且,你不必定比原来设计得好,也许原来的设计考虑到了一些你没考虑到的状况。咱们写的代码,终有一天也会被别人接手,极可能到时别人会有和咱们如今同样的冲动。因此,咱们要作的是重构,从小范围的重构开始
为啥要重构
何为重构:
重构就是对软件内部结构的一种调整,目的是不改变软件可观察行为的前提下,提升其可理解性,下降其可修改为本
通俗的定义:
重构是优化代码结构,使其阅读性更好,扩展性更强的一种高级技术
程序是首先写给人看的,其次才是写给机器看
重构函数
1. 重复代码
编程中不要有大量的重复代码,解决办法就是去提炼到一个单独的函数中
void A() { ..... System.out.println("name" + _name);}void B() { ..... System.out.println("name" + _name);}
更改成↓
void A() { .... }void B() { .... }void printName(String name) { System.out.println("name" + name);}
2. 内联临时变量
若是你对一个变量只引用了一次,那就不妨对他进行一次重构。
int basePrice = order.basePrice();return (basePrice > 100);
更改成↓
return (order.basePrice() > 1000);
3. 尽可能去掉临时变量
临时变量多了会难以维护,因此尽可能去掉所使用的临时变量。
int area = _length * _width;if (area > 1000) return area * 5;else return area *4;
更改成↓
if (area() > 1000) return area() * 5;else return area() *4;int area() { return _length * _width;}
4. 引入解释性变量
跟上面那个相反,若是使用函数变得很复杂,能够考虑使用解释型变量了。
if ((platform.toUpperCase().indexOf("mac") > -1) && (brower.toUpperCase().indexOf("ie") > -1) && wasInitializes() && resize > 0) { ...... }
更改成↓
final boolean isMacOS = platform.toUpperCase().indexOf("mac") > -1;
final boolean isIEBrowser = brower.toUpperCase().indexOf("ie") > -1;
final boolean wasResized = resize > 0;
if (isMacOS && isIEBrowser && wasInitializes() && wasResized) { ...... }
5. 移除对参数的赋值
int discount (int inputVal, int quantity, int yearToDate) { if (inputVal > 50) inputVal -= 2;}
更改成↓
int discount (int inputVal, int quantity, int yearToDate) { int result = inputVal; if (result > 50) result -= 2;}
另外,函数中声明的临时变量最好只被赋值一次,若是超过一次就考虑再声明变量对其进行分解了。
一个函数也不该该太长,若是太长首先影响理解,其次包含的步骤太多会影响函数复用。作法是将里面的步骤提取为不少小函数,而且函数命名要体现出函数作了什么,清晰明了。
重构类
1. 搬移方法
每个方法应该放在她最适合的位置,不能随便乱放,因此不少时候你须要考虑,一个方法在这里是否是最适合的。
class Class1 { aMethod();}class Class2 {}
更改成↓
class Class1 {}class Class2 { aMethod();}
2. 搬移字段
每个字段,变量都应该放到其本身属于的类中,不能随便放,不属于这个类中的字段也须要移走。
class Class1 { aField;}class Class2 {}
更改成↓
class Class1 {}class Class2 { aField;}
3. 提炼一个新类
将不属于这个类中的字段和方法提取到一个新的类中。因此说在你写代码的时候必定要考虑这句话放这里是否是合适,有没有其余更合适的地方?
class Person {
private String name;
private String officeAreaCode; private String officeNumber;
public String getTelephoneNumber() { ..... } }
提炼到新的类中↓
class TelephoneNumber {
private String areaCode;
private String number;
public String getTelephoneNumber() { ..... } }
class Person {
private String name;
private TelephoneNumber _officeNumber; }
上面这种提炼类不必定就是合适的方式,有时候一个类再也不有足够的价值的时候,咱们就须要考虑提炼类的反向操做了。将类变为内联类了。
4. 内容移动
有时候每个子类都有声明一个字段或方法,可是父类里面却没有这个字段或方法,这时候就考虑把这个字段或方法移动到父类里面,去除子类的这个字段和方法。相反状况,若是父类有一个字段或方法,但只是某个子类须要使用,就须要考虑吧这个字段或方法移动到这个特定的子类里面了。
5. 提炼接口
接口也就是协议,如今比较推崇的是面向接口编程。有时候接口将责任分离这个概念能发挥的淋漓尽致,把某些特性功能的方法提炼到接口中也是比较好的作法,这样其余想要这种功能的类只须要实现这个接口就好了。
从新组织数据
1. 自封装字段
在一个类中访问本身的字段是否是应该把字段封装起来呢?这个每一个人的观点是不同的,把字段封装起来的好处就是:若是子类复写这个字段的getter函数,那么能够在里面改变这个字段的获取结果,这样子扩展性可能会更好一点
private int _length. _width;public int area() { return _length * _width;}
更改成↓
private int _length. _width;
public int area() {
return getLength * getWidth();
}
int getLength() {
return _length;
}
int getWidth() {
return _width;
}
2. 以对象取代数值
随着开发的进行,有时候一个数据项表示再也不简单了,好比刚开始只须要知道一我的的名字就好了,但是后来的需求变成了不但要知道这我的的名字还要知道这我的的电话号码,还有住址等。这个时候就须要考虑将数据变成一个对象了。
class Order { private String name;}
更改成↓
class Order { private Person person;}class Person { private String name; private String tel; private String addr;}
咱们有时候须要把Person写成单利类,由于一个Person对象能够拥有不少份订单,可是这个对象只能有一个,因此Person咱们应该写成单利。但有时候换成其余场景咱们不能把他写成单利。这都是要视状况而定的。随意写代码要当心谨慎。
3. 常量取代数字
有时候使用一个固定的数值并非太好,最好使用创建一个常量,取一个有意思的名字来替换这个常量。
double circleArea(int redius) { return 3.14 * redius * redius}
更改成↓
double circleArea(int redius) { return pi * redius * redius;}public final double pi = 3.14;
简化条件表达式
1. 分解条件表达式
有时候看着一个if else语句很复杂,咱们就试着把他分解一下。我想不出好的例子了,就简化一下了,各位莫怪。
if (isUp(case) || isLeft(case)) num = a * b;else num = a * c;
更改成↓
if (isTrue(case))
numberB(a);
}else
numberC(a);
boolean isTrue(case) {
return isUp(case) || isLeft(case);
}
int numberB(a) {
return a + b;
}
int numberC(a) {
return a + c;
}
2. 合并条件表达式
有时咱们写的多个if语句是能够合并到一块儿的。
double disabukutyAmount() { if (_seniority < 2) return 0; if (_monbtdiable > 12) return 0; if (_isPartyTime) retutn 0;}
更改成↓
double disablilityAmount() {
if (isNotEligibleForDisability())
return 0;
}
boolean isNotEligibleForDisability() {
return _seniority < 2 || _monbtdiable > 12 || _isPartyTime;
}
3. 合并重复的条件片断
有时候你可能会在if else 语句中写重复的语句,这时候你须要将重复的语句抽出来。
if (isSpecialDeal()) { total = price * 0.95; send();} else { total = price * 0.98; send();}
更改成↓
if (isSpecialDeal()) total = price * 0.95;else total = price * 0.98;send();
4. 以卫语句取代嵌套表达式
这个可能有点难以理解,可是我感受用处仍是比较大的,就是加入return语句去掉else语句
if (a > 0) result = a + b;else { if (b > 0) result = a + c; else { result = a + d; }}return result;
更改成↓
if (a > 0) return a + b;if (b > 0) return a + c;return a + d;
5. 以多态取代switch语句
这个我感受很重要,用处很是多,之后大家写代码的时候只要碰到switch语句就能够考虑能不能使用面向对象的多态来替代这个switch语句呢?
int getArea() { switch (_shap) case circle: return 3.14 * _r * _r; break; case rect; return _width + _heigth;}
更改成↓
class Shap {
int getArea(){};
}
class Circle extends Shap {
int getArea() {
return 3.14 * _r * _r; break;
}
}
class Rect extends Shap {
int getArea() {
return _width + _heigth;
}
}
而后在调用的时候只须要调用Shap的getArea()方法就好了,就能够去掉switch语句了。
而后咱们还能够在一个方法中引入断言,这样能够保证函数调用的安全性,让代码更加健壮。
简化函数调用
首先要说明的是函数命名必定要有意思,必定要有意思,必定要有意思,重要的事情说三遍,不要随便命名,命名一个函数或者方法的时候必定要能代表这个方法是干什么的。
将参数对象化
函数或方法最好不要有太多的参数,太长的参数难以理解,容易形成先后不一致,最好将参数对象化,传入一个对象而不是几个参数。
public void amountReceived(int start, int end);
该更为↓
public void amountReceived(DateRange range);
固然如今参数仍是比较少,你可能看不出不少好处,可是一旦参数比较多的话,你就能看出好处了,将多个参数变成了一个参数。
总结
下面是我作的一些小注意点的笔记,在文章的末尾顺便粘贴一下,看看加深一下脑子的印象。
- 当添加功能变得比较难的时候,就应该重构代码,先重构代码而后添加功能,重构代码应该一小步一小步的走。
- 方法要放到合适的类里面,找到本身合适的位置
- 尽可能去除多余的临时变量
- 把大方法分割为不少小方法,函数内容越小越容易管理。
- 尽可能使用多态。
- 不要有过长的参数,和过大的类
- 重构时修改接口,要保留旧接口,并让旧接口调用新接口。
- 出现switch就考虑使用多态来替换了。
- 尽量的把大函数提炼成不一样的小函数
- 有时候尽可能使用内联函数
- 将一些临时变量用函数代替
- 当if语句中的判断表达式不少的时候,考虑使用临时变量分解
- 临时变量不该该赋值超过一次,应该使用final表示
- 移除对参数的改变,参数传进函数中不该该被改变自己的值
- 有些难以提炼的函数能够考虑使用函数对象
- 代码尽可能不要过多出现if else语句
文章摘自 http://www.jianshu.com/p/d6ff54d72afbhtml
http://www.cnblogs.com/angeldevil/p/3601730.html程序员
感谢两位做者,学到不少东西
编程