某個類作了應該由兩個類作的事。創建一個新類,將相關字段和函數從舊類搬移到新類。 java
咱們都知道,一個類應該是一個清楚的抽象,處理一些明確的責任,可是在實際工做中,類會不斷成長擴展,隨着責任不斷增長,這個類會變得過度複雜。若是某些數據和某些函數老是一塊兒出現,某些數據常常同時變化甚至彼此相依,這就表示應該將它們分離出去。一個有用的測試就是問本身,若是搬移了某些字段和函數,會發生什麼事?其餘字段和函數是否所以變得無心義? 程序員
另外一個每每在開發後期出現的信號是類的子類化方式。若是你發現子類化隻影響類的部分特性,或若是你發現某些特性須要以一種方式來子類化,某些特性則須要以另外一種方式子類化,這就意味着你須要分解原來的類。 併發
1.決定如何分解類所負的責任。 函數
2.創建一個新類,用以表現從舊類中分離出來的責任。 測試
若是舊類剩下的責任與舊類名不符,爲舊類改名 spa
3.創建「從舊類訪問新類」的連接關係 code
有可能須要一個雙向連接。可是在真正須要它以前,不要創建「重新類通往舊類」的連接 對象
4.對於想搬移的沒一個字段,運用Move Field 搬移之。 接口
5.每次搬移後,編譯、測試。 事務
6. 使用Move Method將必要函數搬移到新類。先搬移較低層函數(「被其餘函數調用」多於「調用其餘函數」者),再搬移較高層函數。
7.每次搬移以後,編譯、測試
8. 檢查,精簡每一個類的接口
若是創建起雙向鏈接,檢查是否能夠將它改成單向鏈接。
9決定是否公開新類。若是的確須要公開它,就要決定讓它成爲引用對象仍是不可變的值對象。
咱們從一個簡單的person類開始
public class Person { private String name; private String officeAreaCode; private String officeNumber; public String getName() { return name; } public String getTelephoneNumber() { return ( "(" + officeAreaCode + ")" + officeNumber ); } public String getOfficeAreaCode() { return officeAreaCode; } public void setOfficeAreaCode( String arg ) { officeAreaCode = arg; } public String getOfficeNumber() { return officeNumber; } public void setOfficeNumber( String arg ) { officeNumber = arg; } }在這個例子中,咱們能夠將與電話號碼相關的行爲分離到一個獨立類中。首先要定義一個TelephoneNumber類來表示「電話號碼」這個概念
public class TelephoneNumber { }而後,創建從Person到TelephoneNumber的連接:
public class Person { private TelephoneNumber officeTelephone = new TelephoneNumber(); ... }
如今,咱們運用Move Field移動一個字段:
public class TelephoneNumber { private String areaCode; String getAreaCode() { return areaCode; } void setAreaCode( String arg ) { areaCode = arg; } }
public class Person { public String getTelephoneNumber() { return ( "(" + getOfficeAreaCode() + ")" + officeNumber ); } public String getOfficeAreaCode() { return officeTelephone.getAreaCode(); } public void setOfficeAreaCode( String arg ) { officeTelephone.setAreaCode( arg ); }
而後咱們能夠移動其餘字段,並運用Move Method將相關函數移動到TelephoneNumber類中:
public class TelephoneNumber { private String areaCode; private String number; public String getTelephnoeNumber() { return ( "(" + getAreaCode() + ")" + number ); } String getAreaCode() { return areaCode; } void setAreaCode( String arg ) { areaCode = arg; } String getNumber() { return number; } void setNumber( String arg ) { number = arg; } }
public class Person { private TelephoneNumber officeTelephone = new TelephoneNumber(); private String name; public String getName() { return name; } public String getTelephoneNumber() { return officeTelephone.getTelephnoeNumber(); } TelephoneNumber getOfficeTelephone() { return officeTelephone; } }下一步要作的決定是:要不要對用戶公開這個新類? 咱們能夠將Person中與電話號碼相關的函數委託至TelephoneNumber,從而徹底隱藏這個新類;也能夠直接將它對用戶公開。咱們還能夠將它公開給部分用戶。
若是選擇公開新類,就須要考慮別名帶來的危險。若是公開了TelephoneNumber,而有個用戶修改了對象中的areaCode字段值,咱們又怎麼能知道呢?並且,作出修改的可能不是直接用戶,而是用戶的用戶。
面對這個問題,咱們又下列幾種選擇。
1.容許任何對象修改TelephoneNumber對象的任何部分。這就使得TelephoneNumber對象成爲引用對象,因而咱們應該考慮使用Change Value to Reference。這種狀況下,Person應該是TelephoneNumber的訪問點。
2.不容許任何人不經過Person對象就修改TelephoneNumber對象。爲此,咱們能夠將TelephoneNumber設爲不可修改的,或爲它提供一個不可修改的接口。
3. 另外一個辦法是:先複製一個TelephoneNumber對象,而後將複製獲得的新對象傳遞給用戶。但這可能會形成必定程度的迷惑,由於人們會認爲他們能夠修改TelephoneNumber對象值。此外,若是同一個TelephoneNumber對象被傳遞給多個用戶,也可能在用戶之間形成別名問題。
Extract Class是改善併發程序的一種經常使用技術,由於它使你能夠爲提煉後的兩個類分別加鎖。若是你不須要同時鎖定兩個對象,就沒必要這樣作。
這裏也存在危險性。若是須要確保兩個對象被同時鎖定,就面臨事務問題,須要使用其餘類型的共享鎖。這是一個複雜的領域,比起通常狀況須要更繁重的機制。事務頗有實用性,可是編寫事務管理程序則超出了大多數程序員的職責範圍。