1.0整潔類的書寫準則
1.1 合理地分布類中的代碼
一般情況下抹竹,我們遵循變量列表在前,函數(shù)在后的原則盏档。
類應該從一組變量列表開始。若有公有靜態(tài)常量燥爷,應該最先出現(xiàn)蜈亩,然后是私有靜態(tài)變量,以及公有變量前翎,私有變量稚配。盡可能少的出現(xiàn)公有變量。
公共函數(shù)應該出現(xiàn)在變量列表之后港华。我們喜歡把由某個公共函數(shù)調(diào)用的私有工具函數(shù)緊跟在公共函數(shù)后面道川。
這樣是符合自定向下的原則,讓程序讀起來像一篇報紙文章立宜。
1.2 盡可能保持類的封裝
我們喜歡保持變量和工具函數(shù)的私有性冒萄,但不執(zhí)著于此。有時橙数,我們需要用到protected變量或者工具尊流,比如讓測試可以訪問到。然而灯帮,我們會盡可能使函數(shù)或變量保持私有崖技,不對外暴露太多細節(jié)。放松封裝钟哥,總是下策迎献。
1.3 類應該短小
正如之前關于函數(shù)書寫的論調(diào)。類的一條規(guī)則是短小瞪醋,第二條規(guī)則還是要短小忿晕。
和函數(shù)一樣,馬上有個問題要出現(xiàn)银受,那就是践盼,多小合適呢鸦采?
對于函數(shù),我們通過計算代碼行數(shù)來衡量大小咕幻,對于類渔伯,我們采用不同的衡量方法,那就是權責(responsibility)肄程。
1.3.1 單一權責原則
單一權責(Single Responsibility Principle锣吼,SRP)認為,類或模塊應有且只有一條加以修改的理由蓝厌。
舉個栗子玄叠,下面這個類足夠短小了嗎?
public class SuperDashboard extends JFrameimplements MetaDataUser
{
public Component getLastFocusedComponent()
public void setLastFocused(Component lastFocused)
public int getMajorVersionNumber()
public int getMinorVersionNumber()
public int getBuildNumber()
}
答案是否定的拓提,這個類不夠“短小”读恃。5個方法不算多,但是這個類雖方法少代态,但還是擁有太多權責寺惫。這個貌似很小的SuperDashboard類,卻有兩條關聯(lián)度并不大的加以修改的理由:
第一蹦疑, 它跟蹤會隨著軟件每次發(fā)布而更新的版本信息(含有getMajorVersionNumber等方法)西雀。
第二,它還在管理組件(含有getLastFocusedComponent方法)歉摧。
其實艇肴,鑒別權責(修改的理由)常常幫助我們在代碼中認識到并創(chuàng)建出更好的抽象。
我們可以輕易地將SuperDashboard拆解成名為Version的類中判莉,而這個名為Version的類豆挽,極可能在其他應用程序中得到復用:
public class Version
{
public int getMajorVersionNumber()
public int getMinorVersionNumber()
public int getBuildNumber()
}
這樣育谬,這個類就大致做到了單一權責券盅。
1.4 合理提高類的內(nèi)聚性
我們希望類的內(nèi)聚性保持在較高的水平。
何為類的內(nèi)聚性膛檀?類的內(nèi)聚性就是類中變量與方法之間的依賴關系锰镀。類中方法操作的變量越多,就越黏聚到類上咖刃,就代表類的內(nèi)聚性高泳炉。
類應該只有少量的實體變量,類中的每個方法都應該操作一個或者多個這種變量嚎杨。通常而言花鹅,如果一個類中的每個變量都被每個方法所使用,則該類具有最大的內(nèi)聚性枫浙。一般來說刨肃,創(chuàng)建這種極大化的內(nèi)聚類不可取古拴,也不可能。
我們只希望內(nèi)聚性保持在較高的水平真友。內(nèi)聚性高黄痪,表示類中方法和變量相互依賴,相互結(jié)合成一個邏輯整體盔然。
舉個高內(nèi)聚的例子:
public class Stack
{
private int topOfStack = 0;
List<Integer> elements = new LinkedList<Integer>();
public int size()
{
return topOfStack;
}
public void push(int element)
{
topOfStack++;
elements.add(element);
}
public int pop() throws PoppedWhenEmpty
{
if (topOfStack == 0)
throw new PoppedWhenEmpty();
int element = elements.get(--topOfStack);
elements.remove(topOfStack);
return element;
}
}
這個類非常內(nèi)聚桅打,在三個方法中,僅有size()方法沒有使用所有的兩個變量愈案。
注意挺尾,保持函數(shù)和參數(shù)短小的策略,有時候會導致為一組子集方法所用的實體變量增加站绪。我們應該嘗試將這些方法拆分到兩個或者多個類中潦嘶,讓新的類更為內(nèi)聚。
1.5 有效地隔離修改
需求會改變崇众,所以代碼也會改變掂僵。在面向?qū)ο笕腴T知識中我們學習到,具體類包含實現(xiàn)細節(jié)(代碼)顷歌,而抽象類則呈現(xiàn)概念锰蓬。依賴于具體細節(jié)的客戶類,當細節(jié)改變時眯漩,就會有風險芹扭。我們可以借助接口和抽象類來隔離這些細節(jié)帶來的影響。
舉個栗子赦抖,在一個設計場景下舱卡,我們以其設計直接依賴于TokyoStockExchange的Protfolio類,不如創(chuàng)建StockExchange接口队萤,里面只聲明一個方法:
public interface StockExchange
{
MoneycurrentPrice(String symbol);
}
接著設計TokyoStockExchange類來實現(xiàn)這個接口:
public class TokyoStockExchange extends StockExchange
{
//…
}
我們還要確保Portfolio的構造器接受作為參數(shù)StickExchange引用:
public Portfolio
{
private StockExchange exchange;
public Portfolio(StockExchange exchange)
{
this.exchange = exchange;
}
// ...
}
那么現(xiàn)在就可以為StockExchange接口創(chuàng)建可以測試的實現(xiàn)了轮锥,例如返回固定的股票現(xiàn)值。比如測試購買5股微軟股票要尔,我們下面的實現(xiàn)代碼返回100美元的現(xiàn)值舍杜,然后再實現(xiàn)一個總投資價值為500美元的測試,那么大概代碼則是:
public class PortfolioTest
{
privateFixedStockExchangeStub exchange;
privatePortfolio portfolio;
@Before
protected void setUp() throws Exception
{
exchange = new FixedStockExchangeStub();
exchange.fix("MSFT", 100);
portfolio = new Portfolio(exchange);
}
@Test
public void GivenFiveMSFTTotalShouldBe500() throws Exception
{
portfolio.add(5, "MSFT");
Assert.assertEquals(500,portfolio.value());
}
}
如果系統(tǒng)解耦到足以這樣測試的程度赵辕,也就更加靈活既绩,更加可復用。部件之間的解耦代表著系統(tǒng)中的元素相互隔離得很好还惠。隔離也讓對系統(tǒng)每個元素的理解變得更加容易饲握。
我們的Portfolio類不再是依賴于TokyoStockExchange類的實現(xiàn)細節(jié),而是依賴于StockExchange接口這個抽象的概念,這樣就隔離了特定的細節(jié)救欧。而其實我們的類就遵循了另一條類的設計原則歪今,依賴倒置原則(Dependency Inversion Principle , DIP),因為依賴倒置原則的本質(zhì)颜矿,實際上就是認為類應該依賴于抽象寄猩,而不是依賴于具體細節(jié)。
2.0總結(jié)
- 合理地分布類中的代碼: 類中代碼的分布順序大致是:
<1> 公有靜態(tài)常量
<2> 私有靜態(tài)變量
<3> 公有普通變量
<4> 私有普通變量
<5> 公共函數(shù)
<6> 私有函數(shù) - 盡可能地保持類的封裝: 盡可能使函數(shù)或變量保持私有骑疆,不對外暴露太多細節(jié)田篇。
- 類應該短小,盡量保持單一權責原則: 類或模塊應有且只有一條加以修改的理由箍铭。
- 合理提高類的內(nèi)聚性: 我們希望類的內(nèi)聚性保持在較高的水平泊柬。內(nèi)聚性高,表示類中方法和變量相互依賴诈火,相互結(jié)合成一個邏輯整體兽赁。
- 有效地隔離修改: 類應該依賴于抽象,而不是依賴于具體細節(jié)冷守。盡量對設計解耦刀崖,做好系統(tǒng)中的元素的相互隔離,做到更加靈活與可復用拍摇。