### Replace Array with Object(以對象取代數組)
你有一個數組(array),其中的元素各自代表不同的東西。
以對象替換數組。對于數組中的每個元素,以一個值域表示之。
~~~
String[] row = new String[3];
row [0] = "Liverpool";
row [1] = "15";
~~~
=>
~~~
Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");
~~~
**動機(Motivation)**
數組(array)是一種常見的用以組織數據的結構體。不過,它們應該只用于「以某種順序容納一組相似對象」。有時候你會發現,一個數組容納了數種不同對象,這會給array用戶帶來麻煩,因為他們很難記住像「數組的第一個元素是人名」這樣的約定。對象就不同了,你可以運用值域名稱和函數名稱來傳達這樣的信息,因此你無需死記它,也無需倚賴注釋。而且如果使用對象,你還可以將信息封裝起來,并使用Move Method 為它加上相關行為。
**作法(Mechanics)**
- 新建一個class表示數組所示信息,并在該class中以一個public值域保存原先的數組。
- 修改數組的所有用戶,讓它們改用新建的class實體。
- 編譯,測試。
- 逐一為數組元素添加取值/設值函數(getters/setters)。根據元素的用途,為這些訪問函數命名。修改客戶端代碼,讓它們通過訪問函數取用數組內的元素。 每次修改后,編譯并測試。
- 當所有「對數組的直接訪問」都被取代為「對訪問函數的調用」后,將class之中保存該數組的值域聲明private。
- 編譯。
- 對于數組內的每一個元素,在新class中創建一個型別相當的值域;修改該元素的訪問函數,令它改用上述的新建值域。
- 每修改一個元素,編譯并測試。
- 數組的所有元素都在對應的class內有了相應值域之后,刪除該數組。
**范例(Example)**
我們的范例從一個數組開始,其中有三個元素,分別保存一支球隊的名稱、獲勝場 次和失利場次。這個數組的聲明可能像這樣:
~~~
String[] row = new String[3];
~~~
客戶端代碼可能像這樣:
~~~
row [0] = "Liverpool";
row [1] = "15";
String name = row[0];
int wins = Integer.parseInt(row[1]);
~~~
為了將數組變成對象,我首先建立一個對應的class:
~~~
class Performance {}
~~~
然后為它聲明一個public值域,用以保存原先數組。〔我知道public值域十惡不赦,請放心,稍后我便讓它改邪歸正。)
~~~
public String[] _data = new String[3];
~~~
現在,我要找到創建和訪問數組的地方。在創建地點,我將它替換為下列代碼:
~~~
Performance row = new Performance();
~~~
對于數組使用地點,我將它替換為以下代碼:
~~~
row._data [0] = "Liverpool";
row._data [1] = "15";
String name = row._data[0];
int wins = Integer.parseInt(row._data[1]);
~~~
然后我要逐一為數組元素加上有意義的取值/設值函數(getter/setter)。首先從「球隊名稱」開始:
~~~
class Performance...
public String getName() {
return _data[0];
}
public void setName(String arg) {
_data[0] = arg;
}
~~~
然后修改row對象的用戶,讓他們改用「取值丨設值函數」來訪問球隊名稱:
~~~
row.setName("Liverpool");
row._data [1] = "15";
String name = row.getName();
int wins = Integer.parseInt(row._data[1]);
~~~
第二個元素也如法炮制。為了簡單起見,我還可以把數據型別的轉換也封裝起來:
~~~
class Performance...
public int getWins() {
return Integer.parseInt(_data[1]);
}
public void setWins(String arg) {
_data[1] = arg;
}
....
client code...
row.setName("Liverpool");
row.setWins("15");
String name = row.getName();
int wins = row.getWins();
~~~
處理完所有元素之后,我就可以將保存該數組的值域聲明為private了。
~~~
private String[] _data = new String[3];
~~~
現在,本次重構最重要的部分(接口修改)已經完成。但是「將對象內的數組替換掉」的過程也同樣重要。我可以針對每個數組元素,在內建立一個型別相當的值域,然后修改該數組元素的訪問函數,令它直接訪問新建值域,從而完全擺脫 對數組元素的依賴。
~~~
class Performance...
public String getName() {
return _name;
}
public void setName(String arg) {
_name = arg;
}
private String _name;
~~~
對數組中的每一個元素都如法炮制。全部處理完畢后,我就可以將數組從我的Performance class刪掉了。
- 譯序 by 侯捷
- 譯序 by 熊節
- 序言
- 前言
- 章節一 重構,第一個案例
- 起點
- 重構的第一步
- 分解并重組statement()
- 運用多態(Polymorphism)取代與價格相關的條件邏輯
- 結語
- 章節二 重構原則
- 何謂重構
- 為何重構
- 「重構」助你找到臭蟲(bugs)
- 何時重構
- 怎么對經理說?
- 重構的難題
- 重構與設計
- 重構與性能(Performance)
- 重構起源何處?
- 章節三 代碼的壞味道
- Duplicated Code(重復的代碼)
- Long Method(過長函數)
- Large Class(過大類)
- Long Parameter List(過長參數列)
- Divergent Change(發散式變化)
- Shotgun Surgery(散彈式修改)
- Feature Envy(依戀情結)
- Data Clumps(數據泥團)
- Primitive Obsession(基本型別偏執)
- Switch Statements(switch驚悚現身)
- Parallel Inheritance Hierarchies(平行繼承體系)
- Lazy Class(冗贅類)
- Speculative Generality(夸夸其談未來性)
- Temporary Field(令人迷惑的暫時值域)
- Message Chains(過度耦合的消息鏈)
- Middle Man(中間轉手人)
- Inappropriate Intimacy(狎昵關系)
- Alternative Classes with Different Interfaces(異曲同工的類)
- Incomplete Library Class(不完美的程序庫類)
- Data Class(純稚的數據類)
- Refused Bequest(被拒絕的遺贈)
- Comments(過多的注釋)
- 章節四 構筑測試體系
- 自我測試代碼的價值
- JUnit測試框架
- 添加更多測試
- 章節五 重構名錄
- 重構的記錄格式
- 尋找引用點
- 這些重構準則有多成熟
- 章節六 重新組織你的函數
- Extract Method(提煉函數)
- Inline Method(將函數內聯化)
- Inline Temp(將臨時變量內聯化)
- Replace Temp with Query(以查詢取代臨時變量)
- Introduce Explaining Variable(引入解釋性變量)
- Split Temporary Variable(剖解臨時變量)
- Remove Assignments to Parameters(移除對參數的賦值動作)
- Replace Method with Method Object(以函數對象取代函數)
- Substitute Algorithm(替換你的算法)
- 章節七 在對象之間搬移特性
- Move Method(搬移函數)
- Move Field(搬移值域)
- Extract Class(提煉類)
- Inline Class(將類內聯化)
- Hide Delegate(隱藏「委托關系」)
- Remove Middle Man(移除中間人)
- Introduce Foreign Method(引入外加函數)
- Introduce Local Extension(引入本地擴展)
- 章節八 重新組織數據
- Self Encapsulate Field(自封裝值域)
- Replace Data Value with Object(以對象取代數據值)
- Change Value to Reference(將實值對象改為引用對象)
- Replace Array with Object(以對象取代數組)
- Replace Array with Object(以對象取代數組)
- Duplicate Observed Data(復制「被監視數據」)
- Change Unidirectional Association to Bidirectional(將單向關聯改為雙向)
- Change Bidirectional Association to Unidirectional(將雙向關聯改為單向)
- Replace Magic Number with Symbolic Constant(以符號常量/字面常量取代魔法數)
- Encapsulate Field(封裝值域)
- Encapsulate Collection(封裝群集)
- Replace Record with Data Class(以數據類取代記錄)
- Replace Type Code with Class(以類取代型別碼)
- Replace Type Code with Subclasses(以子類取代型別碼)
- Replace Type Code with State/Strategy(以State/strategy 取代型別碼)
- Replace Subclass with Fields(以值域取代子類)
- 章節九 簡化條件表達式
- Decompose Conditional(分解條件式)
- Consolidate Conditional Expression(合并條件式)
- Consolidate Duplicate Conditional Fragments(合并重復的條件片段)
- Remove Control Flag(移除控制標記)
- Replace Nested Conditional with Guard Clauses(以衛語句取代嵌套條件式)
- Replace Conditional with Polymorphism(以多態取代條件式)
- Introduce Null Object(引入Null 對象)
- Introduce Assertion(引入斷言)
- 章節十一 處理概括關系
- Pull Up Field(值域上移)
- Pull Up Method(函數上移)
- Pull Up Constructor Body(構造函數本體上移)
- Push Down Method(函數下移)
- Push Down Field(值域下移)
- Extract Subclass(提煉子類)
- Extract Superclass(提煉超類)
- Extract Interface(提煉接口)
- Collapse Hierarchy(折疊繼承關系)
- Form Template Method(塑造模板函數)
- Replace Inheritance with Delegation(以委托取代繼承)
- Replace Delegation with Inheritance(以繼承取代委托)
- 章節十二 大型重構
- 這場游戲的本質
- Tease Apart Inheritance(梳理并分解繼承體系)
- Convert Procedural Design to Objects(將過程化設計轉化為對象設計)
- Separate Domain from Presentation(將領域和表述/顯示分離)
- Extract Hierarchy(提煉繼承體系)
- 章節十三 重構,復用與現實
- 現實的檢驗
- 為什么開發者不愿意重構他們的程序?
- 現實的檢驗(再論)
- 重構的資源和參考資料
- 從重構聯想到軟件復用和技術傳播
- 結語
- 參考文獻
- 章節十四 重構工具
- 使用工具進行重構
- 重構工具的技術標準(Technical Criteria )
- 重構工具的實用標準(Practical Criteria )
- 小結
- 章節十五 集成
- 參考書目