上周發的blog上,談及Vs.net與Eclipse的比較,在“快速由欄位產生屬性”的問題與兄弟們發生了爭執,有兄弟認為在Vs.net2005重構中“Encapsulate Field...”,可以做到這一功能。我覺得這是一個概念上的錯誤,因為“封裝欄位“這個重構方法的本質並不是幹這事。
算了,我乾脆就談談“重構”吧
讓我從頭說起吧,當然俺的知識有限,肯定有說錯的地方。
如果說錯了,還請各位海涵。最重要的是指出俺的錯誤,不讓俺一直錯下去。
說到了“重構(refactor)”,也不得不說說源(Source),看看他們的比較。
一、Source
Source並不是一個標準的概念,目前來說只出現在Eclipse 中,他可以說是一堆方便寫代碼的功能的集合,來看看Eclipse中的Source菜單
(菜單內容就不仔細解釋了,看英文就懂了)
Source的功能大多數是為了方便寫代碼之用,即他是在產生代碼,也就是說,他的作用發生在“編寫代碼時”,他是“產生代碼結構,而非改變代碼結構”
Source的功能很多,在實際操作中,可大大加快寫代碼的速度,大家可以自行看功能表項目理解(強烈建議自己動手一試),我這裡僅僅談談,上次發生爭論的“Generate Getters and Setters..”的功能。
這裡我圖例一次,操作,希望大家不要閑煩。
這個“Generate Getters and Setters..”就是我上次說的,“快速產生屬性..”。
我隨便寫一個類, 寫了三個欄位,讓他幫我同時產生這三個屬性:
/**
* @author duliang
* @version 1.0
* <p> </p>
*/
public class TestField
{
private String name = "";
private String address = "";
private boolean isNew = false;
}
操作如下:
OK,一切搞定,連注釋都有了,自動產生如下代碼:
/**
* @author duliang
* @version 1.0
* <p> </p>
*/
public class TestField
{
private String name = "";
private String address = "";
private boolean isNew = false;
/**
* @return Returns the address.
*/
public String getAddress()
{
return (address);
}
/**
* @param address The address to set.
*/
public void setAddress(String address)
{
this.address = address;
}
/**
* @return Returns the isNew.
*/
public boolean isNew()
{
return (isNew);
}
/**
* @param isNew The isNew to set.
*/
public void setNew(boolean isNew)
{
this.isNew = isNew;
}
/**
* @return Returns the name.
*/
public String getName()
{
return (name);
}
/**
* @param name The name to set.
*/
public void setName(String name)
{
this.name = name;
}
}
實際上我要說的就是這麼一個簡單的功能。
我圖例的原因,就是告訴大家,這些操作發生在“代碼編寫時”。哈哈,很羅嗦吧:)
二 Refactor
OK,再來說說重構,這也是主題。
重構是指:在不改變代碼功能的基礎上,改變代碼的結構。
舉幾個重構的簡單例子:
1. 重新命名某個類,某個欄位或者是本地變數,你則需要“rename“操作。
2. 某函數代碼太長,你就需要將一個函數分成多個小函數,則你需要“Extract Method(抽取方法)”
3. 你的類,需要增加基類(介面),以更好的符合裡氏代換、依賴轉置原則,則你需要 “Extract Interface”
4. 某個方法要移至基類或子類,你則需要使用“Pull UP” 或 “Pull Down”
5. 。。。。。
重構是一種思想,當然你可以用手動改代碼直接實現,但是你面對大項目時,你就會發現手動重構是多麼痛苦,比如你在項目中,修改一個被其它人廣泛使用的類名(方法名),如果你手動修改,必須得一個個的手動尋找、修改,你認為不痛苦嗎?
如果只需要點幾下滑鼠,就能搞定這一切,那多爽?
這就是開發工具提供重構的意義。
下面來看看Eclipse中提供的重構功能:
Vs.net 2005提供的有:
顯然,Eclipse提供的要多得多。
當然就算是eclipse,提供的重構功能也並不是全部,慢慢加強吧。
OK,現在我們都知道“重構”是什麼了,我們再來看看有兄弟指出的“Encapsulate Field...”方法到底是幹什麼。
從上兩圖中可以看出,這功能在Eclipse與Vs2005中都提供,可以說這方法是比較重要,比較常見的。
當然,首先我得承認,使用“Encapsulate Field...”確實能使用單個欄位產生屬性 ,似乎是實現了我們上次爭論的功能。但是它既然稱為重構的一種方法,則他的本質作用便不是這個,。
重構是幹什嗎?他是要改變代碼的結構,以讓代碼更便閱讀,代碼結構更合理。
因此“Encapsulate Field...”的作用是“封裝欄位的可見度”,發產生“代碼編寫後”
初級程式員,經常幹這種事情,就是為了訪問方便,把一些欄位設成public 或者說是protected、internal這些可能被外部直接修改的類型,擁有這種修飾符欄位的類,是不安全的,因為有些欄位我們經常是不允許外部更改的。
類設計的基本原則就是盡量減少提供修改類內部結構的方法。這樣隨意讓別人就有權力更改內部欄位,顯然是不合理的。
因此這個時候就需要使用“Encapsulate Field...”這個重構方法,將代碼重構,使這些欄位通過get,set來訪問,以控制欄位的可見度,保證類的安全。
“Generate Getters and Setters..”與“Encapsulate Field...”還有個很大的區別,也可以說是Source與Refactor的區別:
“Generate Getters and Setters..”只是會改變當前的類的代碼,而“Encapsulate Field...”則應改變與之相關的其它類的代碼。
舉個簡單的例子,有A,B,C三個類,其它A類中有一個public欄位field,B實現了 對這個欄位的引用,C則設定這個欄位。
重構前的代碼:
public class A
{
public String field = "HI, I am feidao";
}
public class B
{
public B()
{
A a = new A();
String str = a.field;
}
}
public class C
{
public C()
{
A a = new A();
a.field = "";
}
}
對A類的field欄位進行“Encapsulate Field...”重構,重構後增加了get,set方法(在.Net中就是屬性),且欄位的public修飾符,改為private。重構後代碼為:
public class A
{
private String field = "HI, I am feidao";
/**
* @param field The field to set.
*/
public void setField(String field)
{
this.field = field;
}
/**
* @return Returns the field.
*/
public String getField()
{
return field;
}
}
重構後,B類的引用代碼,則需要改為getField(),才能保證原有功能,當然這一切,開發工具,幫你自動幫你做了。
public class B
{
public B()
{
A a = new A();
String str = a.getField();
}
}
同理C類,改為setField(),以改變此欄位的值:
public class C
{
public C()
{
A a = new A();
a.setField("");
}
}
大家注意紅色字型,相信很直觀的了瞭解“Encapsulate Field...”真正作用。
重構是非常有用的東西(其實Source也是),相信2005出來後,會增加.Net兄弟們對重構的認識。
重構的內容很多,理解運用好重構,可以讓大家寫出一手漂亮的代碼。
有一本經典好書,就叫《重構》,英文原版忘記叫什麼名字了,中文版是由候大師翻譯的,裡面的內容都是Java寫的,但是 Java與C#文法差別不大,.Net的兄弟們不會存在看不懂的問題,當然極度反感Java的兄弟除外。
VB我就不太會,用VB.Net的兄弟們就不要挑我的刺了。
btw,運用,學好重構,首先還是得以設計模式為基礎,否則你都不知道重構的目標。
Source與重構: 前者是產生代碼結構,後者是改變代碼結構;一個方便快速寫代碼,另一個使代碼更加強壯,聞不到“壞味道”。