客戶直接調用其server object(服務對象)的delegate class。
在server端(某個class)建立客戶所需的所有函數,用以隱藏委托關系(delegation)。

在server端(某個class)建立客戶所需的所有函數,用以隱藏委托關系(delegation)。

ivaneeo's blog自由的力量,自由的生活。 |
客戶直接調用其server object(服務對象)的delegate class。
在server端(某個class)建立客戶所需的所有函數,用以隱藏委托關系(delegation)。 ![]()
先前(上個重構項)我從TelephoneNumber提煉出另一個class,現在我要將它inlining塞回到Person去。一開始這兩個classes是分離的:
class Person... public String getName() { return _name; } public String getTelephoneNumber() { return _officeTelephone.getTelephoneNumber(); } TelephoneNumber getOfficeTelephone() { return _officeTelephone; } private String _name; private TelephoneNumber _officeTelephone = new TelephoneNumber(); class TelephoneNumber... public String getTelephoneNumber() { return ("(" + _areaCode + ")" + _number); String getAreaCode() { return _areaCode; } void setAreaCode(String arg) { _areaCode = arg; } String getNumber() { return _number; } void setNumber(String arg) { _number = arg; } private String _number; private String _areaCode; 首先我在Person中聲明TelephoneNumber的所有[可見](public)函數: class Person... String getAreaCode() { return _officeTelephone.getAreaCode(); } void setAreaCode(String arg) { _officeTelephone.setAreaCode(arg); } String getNumber() { return _officeTelephone.getNumber(); } void setNumber(String arg) { _officeTelephone.setNumber(arg); } 現在,我要找出TelephoneNumber的所有用戶,讓它們轉而使用Person接口。于是下列代碼: Person martin = new Person(); martin.getOfficeTelephone().setAreaCode("781"); 就變成了: Person martin = new Person(); martin.setAreaCode("781"); 現在,我可以持續使用Move Method(142)和Move Field(146),直到TelephoneNumber不復存在。
作法(Mechanics)
動機(Motivation)
Inline Class(154)正好與Extract Class(149)相反。如果一個class不再承擔足夠責任、不再有單獨存在的理由(這通常是因為此前的重構動作移走了這個class的責任),我就會挑選這一[萎縮class]的最頻繁用戶(也是個class),以Inline Class(154)手法將[萎縮class]塞進去。
你的某個class沒有做太多事情(沒有承擔足夠責任)。
將class的所有特性搬移到另一個class中,然后移除原class。 ![]()
讓我們從一個簡單的Person class開始:
class Person... public String getName() { return _name; } public String getTelephoneNumber() { return ("(" + _officeAreaCode + ")" + _officeNumber); } String getOfficeAreaCode() { return _officeAreaCode; } void setOfficeAreaCode(String arg) { _officeAreaCode = arg; } String getOfficeNumber() { return _officeNumber; } void setOfficeNumber(String arg) { _officeNumber = arg; } private String _name; private String _officeAreaCode; private String _officeNumber; 在這個例子,我可以將[與電話號碼相關]的行為分離到一個獨立class中。首先我要定義一個TelephoneNumber class來表示[電話號碼]這個概念: class TelephoneNumber { } 易如反掌!然后,我要建立從Person到TelephoneNumber的連接: class Person... private TelephoneNumber _officeTelephone = new TelephoneNumber(); 現在,我運用Move Field(146)移動一個值域: class TelephoneNumber { String getAreaCode() { return _areaCode; } void setAreaCode(String arg) { _areaCode = arg; } private String _areaCode; } class Person... public String getTelephoneNumber() { return ("(" + getOfficeAreaCode() + ")" + _officeNumber); } String getOfficeAreaCode() { return _officeTelephone.getAreaCode(); } void setOfficeAreaCode(String arg) { _officeTelephone.setAreaCode(arg); } 然后我可以移動其他值域,并運用Move Method(142)將相關函數移動到TelephoneNumber class中: class Person... public String getName() { return _name; } public String getTelephoneNumber() { return _officeTelephone.getTelephoneNumber(); } TelephoneNumber getOfficeTelephone() { return _officeTelephone; } private String _name; private TelephoneNumber _officeTelephone = new TelephoneNumber(); class TelephoneNumber... public String getTelephoneNumber() { return ("(" + _areaCode + ")" + _number); String getAreaCode() { return _areaCode; } void setAreaCode(String arg) { _areaCode = arg; } String getNumber() { return _number; } void setNumber(String arg) { _number = arg; } private String _number; private String _areaCode; 下一步要做的決定是:要不要對客戶揭示這個新class?我可以將Person中[與電話號碼相關]的函數委托(delegating)至 TelephoneNumber,從而完全隱藏這個新class;也可以直接將對用戶曝光。我還可以將它暴露給部分用戶(位于同一個package中的用 戶),而不暴露給其他用戶。 如果我選擇暴露新class嗎,我就需要考慮別名(aliasing)帶來的危險。如果我暴露了TelephoneNumber,而有個用戶修改了對象中的_areaCode值域值,我又怎么能知道呢?而且,做出修改的可能不是直接用戶,而是用戶的用戶的用戶。 面對這個問題,我有下列數種選擇: 1.允許任何對象修改TelephoneNumber對象的任何部分。這就使得TelephoneNumber對象成為引用對象(reference object),對于我應該考慮使用Change Value to Reference(179)。這種情況下,Person應該是TelephoneNumber的訪問點。 2.不許任何人[不通過Person對象就修改TelephoneNumber對象]。為了達到目的,我可以將TelephoneNumber設為不可修改的(immutable),或為它提供一個不可修改的接口(immutable interface)。 3.另一個辦法是:先復制一個TelephoneNumber對象,然后將復制得到的新對象傳遞給用戶。但這可能會造成一定程度的迷惑,因為人們會認為他們可以修改TelephoneNumber對象值。此外,如果同一個TelephoneNumber對象被傳遞給多個用戶,也可能在用戶之間造成別名(aliasing)問題。 Extract Class(149)是改善并發(concurrent)程序的一種常用技術,因為它使你可以提煉后的兩個classes分別加鎖(locks)。
作法(Mechanics)
動機(Motivation)
如果某些數據和某些函數總是一起出現,如果某些數據經常同時變化甚至彼此相依,這就表示你應該將它們分離出去。 另一個往往在開發后期出現的信號是class的[subtyped方式]。如果你發現subtyping只影響class的部分特性,或如果你發現某些特 性[需要以此方式subtyped],某些特性[需要以彼此方式subtyped],這就是意味你需要分解原來的class。
某個class做了應該由兩個classes做的事。
建立一個新class,將相關的值域和函數從舊class搬移到新class。 ![]()
如果有很多函數已經使用了_interestRate field,我應該先運用Self Encapsulate Field(171):
class Account... private AccountType _type; private double _interestRate; double interestForAmount_days(double amount, int days) { return getInterestRate() * amount * days / 365; } private void setInterestRate(double arg) { _interestRate = arg; } private double getInterestRate() { return _interestRate; } 這樣,在搬移field之后,我就只需要修改訪問函數就行了: double interestForAmount_days(double amount, int days) { return getInterestRate() * amount * days / 365; } private void setInterestRate(double arg) { _type.setInterestRate(arg); } private double getInterestRate() { return _type.getInterestRate(); } 如果以后有必要,我可以修改訪問函數(accessors)的用戶,讓它們使用新對象。Self Encapsulate Field(171)使我得以保持小步前進。如果我需要對class做許多處理,保持小步前進是有幫助的。特別值得一提的是:首先使用Self Encapsulate Field(171)使我得以更輕松使用Move Method(142)將函數搬移到target class中。如果待移函數引用了field的訪問函數(accessors),那么那些引用點是無須修改的。 |