祖?zhèn)?/a>shi山代碼重構(gòu)實戰(zhàn)(01)-Extract Class提煉類
某個類做了應(yīng)該由兩個類做的事。
建立一個新類,將相關(guān)的字段和函數(shù)從舊類移到新類。
動機(jī)
一個類應(yīng)該是一個清楚的抽象,處理一些明確的責(zé)任。但實際工作中,類會不斷擴(kuò)展。你會在這兒加入一些功能,在那兒加入一些數(shù)據(jù)。給某個類添加一項新責(zé)任時,你會覺得不值得為這項責(zé)任分離出一個單獨(dú)的類。于是,隨著責(zé)任不斷增加,這個類會變得過分復(fù)雜。很快,你的類變成一團(tuán)亂麻。這樣的類往往含有大量函數(shù)和數(shù)據(jù),太大而不易理解。
此時你需要考慮哪些部分可以分離出去,并將它們分離到一個單獨(dú)的類。如果某些數(shù)據(jù)和某些函數(shù)總是一起出現(xiàn),某些數(shù)據(jù)經(jīng)常同時變化甚至彼此相依,這就表示你應(yīng)該將它們分離出去。
一個有用的測試,問你自己,若你搬移了某些字段和函數(shù),會發(fā)生啥事?其他字段和函數(shù)是否因此變得無意義?
另一個往往在開發(fā)后期出現(xiàn)的信號是類的子類化方式。若發(fā)現(xiàn):
子類化只影響類的部分特性
或發(fā)現(xiàn)某些特性需要以一種方式來子類化,某些特性則需以另一種方式子類化
這就意味你需要分解原來的類。
做法
決定如何分解類所負(fù)的責(zé)任。
建立一個新類,用以表現(xiàn)從舊類中分離出來的責(zé)任。若舊類剩下的責(zé)任與舊類名稱不符,為舊類改名
構(gòu)造舊類時,創(chuàng)建一個新類的實例,建立“從舊類訪問新類”的連接關(guān)系
對你想搬移的每個字段,運(yùn)用【搬移字段】搬移之。每次更改后運(yùn)行測試。
使用【搬移函數(shù)】將必要函數(shù)搬移到新類。先搬移較低層函數(shù)(也就是“被其他函數(shù)調(diào)用”多于“調(diào)用其他
函數(shù)”者)。每次更改后運(yùn)行測試。
檢查兩個類的接口,去掉不再需要的函數(shù),必要時為函數(shù)重新取一個適合新環(huán)境的名字。
決定是否公開新的類。如果確實需要,考慮對新類應(yīng)用【將引用對象改為值對象】 使其成為一個值對象
范例
Person類:
package com.javaedge.refactor.extract_class; import lombok.Data; /** * @author JavaEdge * @date 2022/3/30 */ @Data public class Person { private String name; private String officeAreaCode; private String officeNumber; public String getTelephoneNumber() { return this.officeAreaCode + this.officeNumber; } }
可以將與電話號碼相關(guān)的行為分離到一個獨(dú)立的類。
首先,定義一個空的 TelephoneNumber 類來表示“電話號碼”:
class TelephoneNumber { }
然后,建立從Person到TelephoneNumber的連接:
class Person { private TelephoneNumber _officeTe1ephone = new TelephoneNumber() }
現(xiàn)在,運(yùn)用MOVE Field 移動一個字段過來:
package com.javaedge.refactor.extract_class; import lombok.Data; /** * @author JavaEdge * @date 2022/3/30 */ @Data public class Person { private String name; private String officeNumber; private TelephoneNumber officeTelephone = new TelephoneNumber(); public String getTelephoneNumber() { return getOfficeAreaCode() + this.officeNumber; } public String getOfficeAreaCode() { return officeTelephone.getAreaCode(); } public void setOfficeAreaCode(String areaCode) { officeTelephone.setAreaCode(areaCode); } }
package com.javaedge.refactor.extract_class; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @author JavaEdge * @date 2022/3/30 */ @Data @NoArgsConstructor @AllArgsConstructor public class TelephoneNumber { private String areaCode; }
移動其它字段,并用 move method將相關(guān)方法移動到TelephoneNumber
package com.javaedge.refactor.extract_class; import lombok.Data; /** * @author JavaEdge * @date 2022/3/30 */ @Data public class Person { private String name; private TelephoneNumber officeTelephone = new TelephoneNumber(); public String getTelephoneNumber() { return officeTelephone.getTelephoneNumber(); } }
package com.javaedge.refactor.extract_class; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @author JavaEdge * @date 2022/3/30 */ @Data @NoArgsConstructor @AllArgsConstructor public class TelephoneNumber { private String areaCode; private String number; public String getTelephoneNumber() { return this.areaCode + this.number; } }
要不要對用戶公開這個新類?我可以將Person中與電話號碼相關(guān)的函數(shù)委托至TelephoneNumber,從而完全隠藏這個新類;也可以直接將它對用戶公開。我還可以將它公開給部分用戶(位于同一個包中的用戶),而不公開給其他用戶。
如果我選擇公開新類,就需要考慮別名帶來的危險。如果我公開了TelephoneNumber,而有個用戶修改了對象中的areaCode字段值,我又怎么能知道呢?而且,做出修改的可能不是直接用戶,而是用戶的用戶的用戶。面對這個問題,我有下列幾種選擇。
允許任何對象修改TelephoneNumber對象的任何部分。這就使得TelephoneNumber對象成為引用對象,于是我應(yīng)該考慮使用CZimigeValue to Reference(179)。這種情況下,Person應(yīng)該是TelephoneNumber的訪問點(diǎn)
不許任何人不通過Person對象就修改TelephoneNumber對象,為此,可以將TelephoneNumber設(shè)為不可修改或為它提供一個不可修改的接口
先復(fù)制一TelephoneNumber對象,然后將復(fù)制得到的新對象傳遞給用戶。但這可能會造成一定程度的迷惑,因為人們會認(rèn)為他們可以修改TelephoneNumber對象的值。此外,如果同個TelephoneNumber對象被傳遞給多個用戶,也可能在用戶之間造成別名問題
電話號碼”對象一般還具有復(fù)用價值,因此我考慮將新提煉的類暴露給更多的客戶端。需要訪問TelephoneNumber對象時,只須把Person類中那些office開頭的訪問函數(shù)搬移過來并略作修改即可。但這樣TelephoneNumber就更像一個值對象了,因此我會先對它使用【將引用對象改為值對象】。
Extract Class是改善并發(fā)程序的一種常用技術(shù),因為它使你可以為提煉后的兩個類分別加鎖。如果你不需要同時鎖定兩個對象,就不必這樣做。
這里也存在危險性。如果需要確保兩個對象被同時鎖,就面臨事務(wù)問題,需要使用其他類型的共享鎖。這是一個復(fù)雜領(lǐng)域,比起一般情況需要更繁重的機(jī)制。事務(wù)很有實用性,但是編寫事務(wù)管理程序則超出了大多數(shù)程序員的職責(zé)范圍。
Java
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。