重構 在物件之間搬移特性

2021-10-24 22:44:33 字數 4080 閱讀 8494

你的程式中,有個函式與其所駐類之外的另乙個類進行更多交流:呼叫後者,或被後者呼叫;則在該函式最常引用的類中建立乙個有著類似行為的新函式,將舊函式變成乙個單純的委託函式,或是將舊函式完全移除。

動機

如果乙個類有太多行為,或如果乙個類與另乙個類有太多合作而形成高度耦合,就使用搬移函式,通過這種手段,可以使系統中的類更簡單。

做法

檢查源類中被源函式所使用的的一切特性,考慮它們是否也該被搬移;

檢查源類的子類和超類,看看是否有該函式的其他宣告;

在目標類中宣告這個函式;

將源函式的**複製到目標函式中,調整後者,使其等在新家中正常執行;

決定如何從源函式正確引用目標物件;

修改源函式,使之成為乙個純委託函式;

決定是否刪除源函式,或將它當做乙個委託函式保留;

如果要移除源函式,請將源類中與源函式的所有呼叫替換為對目標函式的呼叫;

當時用源類的特性時,有四種選擇:將這個特性也移到目標類、建立或使用乙個從目標類到源類的引用關係、將源物件當做引數傳給目標函式、如果所需特性是個變數,將它當做引數傳給目標函式。

如果需要源類的多個特性,那麼就將源物件傳遞給目標函式,但是如果目標含糊需要太多的源類特性,就得進一步重構,通常這種情況下需要分解目標函式,並將其一部分移回源類。

你的程式中,某個欄位被其所駐類之外的另乙個類更多地用到,在目標類新建乙個字段,修改源字段的所有使用者,令它們改用新字段。動機在類之間移動狀態和行為,室重構過程中必不可少的措施,如果對乙個字段,在其所駐類之外的另乙個類有更多函式使用了它,則需要考慮搬移這個字段。所謂「使用」可能是通過設值、取值函式簡介進行的。

做法

如果欄位的訪問級是public,使用封裝屬性將它封裝起來;

在目標類中建立與源字段相同的字段,並同時建立相應的設值/取值函式;

決定如果在源物件中引用目標物件;

首先看是否有現成的字段或函式可以幫助你得到目標物件,如果沒有,就看能否請以建立這樣乙個函式。如果還不行,就得在源類中新建乙個欄位來存放目標物件。

刪除源欄位;

將所有對源字段的引用替換為對某個目標函式的呼叫;

如果需要讀取該變數,就把對源字段的引用替換為對目標取值函式的呼叫;如果要對該變數賦值,就把源字段的引用替換成對設值函式的呼叫。

如果源欄位不是private的,就必須在源類的所有子類中查詢源字段的引用點,並進行相應替換。

某個類做了應該由兩個類做的事,建立乙個新類,將相關的字段和函式從舊類搬移到新類。

動機

乙個類應該是乙個清楚的抽象,處理一些明確的責任。但是在實際中隨著業務功能的增加,類會不斷成長擴充套件。給某個類新增一項新責任時,會覺得不值得為這項責任分離出乙個單獨的類。

這樣的類往往含有大量函式和資料,導致往往太大而不易理解。此時需要考慮哪些部分可以分離出去,並將它們分離到乙個單獨的類中。分離的原則就是如果你搬移了某些欄位和函式,會發生什麼事,其他欄位和函式是否因此變得無意義。

另乙個往往在開發後期出現的訊號是類的子類化方式,如果發現子類化只影響類的部分特性,或如果發現某些特性需要以一種方式來子類化,某些特性則需要以另一種方式來子類化,這就意味著你需要分解原來的類。

做法

決定如何分解類所負的責任;

建立乙個新類,用以表現從舊類中分離出來的責任;

建立「從舊類訪問新類」的連線關係;

對於你想搬移的每乙個字段,執行搬移特性搬移;

使用搬移函式將必要函式搬移到新類,先搬移較底層函式,再搬移較高層函式;

決定是否公開新類,如果需要公開,就要決定讓它成為引用物件還是不可變的值物件。

某個類沒有做太多的事情,將這個類的所有特性搬移到另乙個類中,然後移除原類。

動機

如果乙個類不在承擔足夠責任、不再有單獨存在的理由,我們就會挑選這一「萎縮類」的最頻繁使用者然後將「萎縮類」塞進另乙個類中。

客戶通過乙個委託類來呼叫另乙個物件,在服務類上建立客戶所需的所有函式,用以隱藏委託關係。

動機

封裝即使不是物件最關鍵的特徵,也是特徵之一,封裝意味著每個物件都應該盡可能少了解系統的其他部分。如此一來,一旦發生變化,需要了解這一變化的物件就會比較少。

如果某個客戶先通過服務物件的字段得到另乙個物件,然後呼叫後者的函式,那麼客戶就必須知道這層委託關係。萬一委託關係發生變化,客戶也得相應變化,可以再服務物件上放置乙個簡單的委託函式,將委託關係隱藏起來,而從去除這種依賴。

做法

對於每乙個委託關係中的函式,在服務物件端建立乙個簡單地委託函式;

調整客戶,令它只呼叫服務物件提供的函式;

如果將來不再有任何客戶需要取用受託類,便可移除服務物件中的相關訪問函式。

某個類做了過多的簡單委託動作,讓客戶直接呼叫受託類。

「封裝受託物件」雖然可以解耦,但是這層封裝也是要付出代價的,代價就是:每當客戶要使用受託類的新特性時,你就必須在服務端新增乙個簡單委託函式。隨著受託類的特性越來越多,服務類完全變成了乙個中間人,此時就應該讓客戶端直接呼叫受託類。

合適的隱藏程度這個尺度在不斷改變。

你需要為提供服務的類增加乙個函式,但你無法修改這個類。在客戶端中建立乙個函式,並以第一引數形式傳入乙個服務類例項。

動機

如果乙個類不能提供你目前需要的服務,並且不能修改該類原始碼,就得在客戶端進行編碼,不足你需要的那個函式。當你需要以外加函式實現一項功能最明確的訊號就是這個函式原本應該在提供服務的類中實現。

但是如果你發現自己為乙個服務類建立了大量外加函式,或者發現有許多類都需要同樣的外加函式,就不應該再使用本重構,而應該使用引入本地擴充套件

你需要為服務類提供一些額外函式,但你無法修改這個類。

建立乙個新類,使它包含這些額外函式,讓這個擴充套件品成為源類的子類或包裝類。

動機

如果你只需要一兩個外加函式,可以使用引入外加函式,但是如果你需要的額外函式超過兩個,外加函式就很難控制它們。所以你需要將這些函式組織在一起,放到乙個恰當的地方去。

要達到這一目的,兩種標準物件技術——子類化和包裝是顯而易見的方法,統稱為本地擴充套件

所謂本地擴充套件,它提供源類的一切特性,同時額外新增新特性,在任何使用源類的地方們都可以使用本地擴充套件取而代之。

在子類和包裝類之間做選擇時,通常首選子類,因為這樣的工作量少。但是子類化方案還必須產生乙個子類物件,這種情況下,如果有其他物件引用了舊物件,我們就同時有兩個物件儲存元資料。如果原資料是不可修改的,則可以放心複製。但如果原資料允許被修改,問題來了,因為乙個修改動作無法同時改變兩份副本。這時就必須改用包裝類。

做法建立乙個擴充套件類,將它作為原始類的子類和包裝類;

在擴充套件類中加入轉型建構函式;

所謂轉型建構函式是指接收原物件作為引數的建構函式。

如果採用子類化方案,那麼轉型建構函式應該呼叫適當的超類建構函式。

如果採用包裝類方案,那麼轉型建構函式應該將它得到的轉入引數以例項變數的形式儲存起來,用作接收委託的原物件。

在擴充套件類中加入新特性;

根據需要,將原物件替換為擴充套件物件;

將針對原始類定義的所有外加函式搬移到擴充套件類中。

重構 在物件之間搬移特性

1 move method 搬移函式 有個函式與所在類之外的另乙個類進行更多的交流 呼叫或被呼叫 在該函式最常引用的類中建立乙個有著類似行為的新函式。將舊函式變為乙個單純的委託函式,或者將舊函式刪除。2 move field 搬移字段 某個欄位被其所屬類之外的另乙個類頻繁呼叫。3 extract c...

重構之在物件之間搬移特性

1.move method 搬移函式 你的程式中,有個函式與其所駐 class 之外的另乙個 class 2.move field 搬移值域 在 target class 建立乙個 new field 修改source field 的所有使用者,令它們改用 new field。3.extract c...

重構摘要7 在物件之間搬移特性

你的程式中,有個函式與其所駐類之外的另乙個類進行更多交流 呼叫後者,或被後者呼叫 在該函式最常引用的類中建立乙個有著類似行為的新函式。將舊函式變成乙個單純的委託函式,或者將舊函式完全移除。某個欄位被其所駐類之外的另乙個類更多地用到。如果我需要對類做許多處理,保持小步前進是有幫助的。某個類做了應該由梁...