<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 牛人業(yè)話(huà) > 【《代碼整潔之道》精讀與演繹】之五 整潔類(lèi)的書(shū)寫(xiě)準則

【《代碼整潔之道》精讀與演繹】之五 整潔類(lèi)的書(shū)寫(xiě)準則

作者: 時(shí)間:2016-09-07 來(lái)源:網(wǎng)絡(luò ) 收藏

  這篇文章將與大家一起聊一聊,書(shū)寫(xiě)整潔類(lèi)的一些法則。

本文引用地址:http://dyxdggzs.com/article/201609/296663.htm

  一、引言

  以下引言的內容,有必要伴隨這個(gè)系列的每一次更新,這次也不例外。

  《整潔之道》這本書(shū)提出了一個(gè)觀(guān)點(diǎn):質(zhì)量與其整潔度成正比,干凈的,既在質(zhì)量上可靠,也為后期維護、升級奠定了良好基礎。書(shū)中介紹的規則均來(lái)自作者多年的實(shí)踐經(jīng)驗,涵蓋從命名到重構的多個(gè)編程方面,雖為一“家”之言,然誠有可資借鑒的價(jià)值。

  但我們知道,很多時(shí)候,理想很豐滿(mǎn),現實(shí)很骨感,也知道人在江湖,身不由己。因為項目的緊迫性,需求的多樣性,我們無(wú)法時(shí)時(shí)刻刻都寫(xiě)出整潔的代碼,保持自己輸出的都是高質(zhì)量、優(yōu)雅的代碼。

  但若我們理解了代碼整潔之道的精髓,我們會(huì )知道怎樣讓自己的代碼更加優(yōu)雅、整潔、易讀、易擴展,知道真正整潔的代碼應該是怎么樣的,也許就會(huì )漸漸養成持續輸出整潔代碼的習慣。

  而且或許你會(huì )發(fā)現,若你一直保持輸出整潔代碼的習慣,長(cháng)期來(lái)看,會(huì )讓你的整體效率和代碼質(zhì)量大大提升。

  二、本文涉及知識點(diǎn)思維導圖

  國際慣例,先放出這篇文章所涉及內容知識點(diǎn)的一張思維導圖,就開(kāi)始正文。大家若是疲于閱讀文章正文,直接看這張圖,也是可以Get到本文的主要知識點(diǎn)的大概。

  

 

  三、整潔類(lèi)的書(shū)寫(xiě)準則

  1 合理地分布類(lèi)中的代碼

  一般情況下,我們遵循變量列表在前,函數在后的原則。

  類(lèi)應該從一組變量列表開(kāi)始。若有公有靜態(tài)常量,應該最先出現,然后是私有靜態(tài)變量,以及公有變量,私有變量。盡可能少的出現公有變量。

  公共函數應該出現在變量列表之后。我們喜歡把由某個(gè)公共函數調用的私有工具函數緊跟在公共函數后面。

  這樣是符合自定向下的原則,讓程序讀起來(lái)像一篇報紙文章。

  2 盡可能保持類(lèi)的封裝

  我們喜歡保持變量和工具函數的私有性,但不執著(zhù)于此。有時(shí),我們需要用到protected變量或者工具,比如讓測試可以訪(fǎng)問(wèn)到。然而,我們會(huì )盡可能使函數或變量保持私有,不對外暴露太多細節。放松封裝,總是下策。

  3 類(lèi)應該短小

  正如之前關(guān)于函數書(shū)寫(xiě)的論調。類(lèi)的一條規則是短小,第二條規則還是要短小。

  和函數一樣,馬上有個(gè)問(wèn)題要出現,那就是,多小合適呢?

  對于函數,我們通過(guò)計算代碼行數來(lái)衡量大小,對于類(lèi),我們采用不同的衡量方法,那就是權責(responsibility)。

  3.1 單一權責原則

  單一權責(Single Responsibility Principle,SRP)認為,類(lèi)或模塊應有且只有一條加以修改的理由。

  舉個(gè)栗子,下面這個(gè)類(lèi)足夠短小了嗎?

  [cpp] view plain copy print?

  public class SuperDashboard extends JFrameimplements MetaDataUser

  {

  public Component getLastFocusedComponent()

  public void setLastFocused(Component lastFocused)

  public int getMajorVersionNumber()

  public int getMinorVersionNumber()

  public int getBuildNumber()

  }

  答案是否定的,這個(gè)類(lèi)不夠“短小”。5個(gè)方法不算多,但是這個(gè)類(lèi)雖方法少,但還是擁有太多權責。這個(gè)貌似很小的SuperDashboard類(lèi),卻有兩條關(guān)聯(lián)度并不大的加以修改的理由:

  第一, 它跟蹤會(huì )隨著(zhù)軟件每次發(fā)布而更新的版本信息(含有g(shù)etMajorVersionNumber等方法)。

  第二,它還在管理組件(含有g(shù)etLastFocusedComponent方法)。

  其實(shí),鑒別權責(修改的理由)常常幫助我們在代碼中認識到并創(chuàng )建出更好的抽象。

  我們可以輕易地將SuperDashboard拆解成名為Version的類(lèi)中,而這個(gè)名為Version的類(lèi),極可能在其他應用程序中得到復用:

  [cpp] view plain copy print?

  public class Version

  {

  public int getMajorVersionNumber()

  public int getMinorVersionNumber()

  public int getBuildNumber()

  }

  這樣,這個(gè)類(lèi)就大致做到了單一權責。

  4 合理提高類(lèi)的內聚性

  我們希望類(lèi)的內聚性保持在較高的水平。

  何為類(lèi)的內聚性?類(lèi)的內聚性就是類(lèi)中變量與方法之間的依賴(lài)關(guān)系。類(lèi)中方法操作的變量越多,就越黏聚到類(lèi)上,就代表類(lèi)的內聚性高。

  類(lèi)應該只有少量的實(shí)體變量,類(lèi)中的每個(gè)方法都應該操作一個(gè)或者多個(gè)這種變量。通常而言,如果一個(gè)類(lèi)中的每個(gè)變量都被每個(gè)方法所使用,則該類(lèi)具有最大的內聚性。一般來(lái)說(shuō),創(chuàng )建這種極大化的內聚類(lèi)不可取,也不可能。

  我們只希望內聚性保持在較高的水平。內聚性高,表示類(lèi)中方法和變量相互依賴(lài),相互結合成一個(gè)邏輯整體。

  舉個(gè)高內聚的例子:

  [cpp] view plain copy print?

  public class Stack

  {

  private int topOfStack = 0;

  List elements = new LinkedList();

  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;

  }

  }

  這個(gè)類(lèi)非常內聚,在三個(gè)方法中,僅有size()方法沒(méi)有使用所有的兩個(gè)變量。

  注意,保持函數和參數短小的策略,有時(shí)候會(huì )導致為一組子集方法所用的實(shí)體變量增加。我們應該嘗試將這些方法拆分到兩個(gè)或者多個(gè)類(lèi)中,讓新的類(lèi)更為內聚。

  5 有效地隔離修改

  需求會(huì )改變,所以代碼也會(huì )改變。在面向對象入門(mén)知識中我們學(xué)習到,具體類(lèi)包含實(shí)現細節(代碼),而抽象類(lèi)則呈現概念。依賴(lài)于具體細節的客戶(hù)類(lèi),當細節改變時(shí),就會(huì )有風(fēng)險。我們可以借助接口和抽象類(lèi)來(lái)隔離這些細節帶來(lái)的影響。

  舉個(gè)栗子,在一個(gè)設計場(chǎng)景下,我們以其設計直接依賴(lài)于TokyoStockExchange的Protfolio類(lèi),不如創(chuàng )建StockExchange接口,里面只聲明一個(gè)方法:

  [cpp] view plain copy print?

  public interface StockExchange

  {

  MoneycurrentPrice(String symbol);

  }

  接著(zhù)設計TokyoStockExchange類(lèi)來(lái)實(shí)現這個(gè)接口:

  [cpp] view plain copy print?

  public class TokyoStockExchange extends StockExchange

  {

  //…

  }

  我們還要確保Portfolio的構造器接受作為參數StickExchange引用:

  [cpp] view plain copy print?

  public Portfolio

  {

  private StockExchange exchange;

  public Portfolio(StockExchange exchange)

  {

  this.exchange = exchange;

  }

  // ...

  }

  那么現在就可以為StockExchange接口創(chuàng )建可以測試的實(shí)現了,例如返回固定的股票現值。比如測試購買(mǎi)5股微軟股票,我們下面的實(shí)現代碼返回100美元的現值,然后再實(shí)現一個(gè)總投資價(jià)值為500美元的測試,那么大概代碼則是:

  [cpp] view plain copy print?

  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());

  }

  }

  如果系統解耦到足以這樣測試的程度,也就更加靈活,更加可復用。部件之間的解耦代表著(zhù)系統中的元素相互隔離得很好。隔離也讓對系統每個(gè)元素的理解變得更加容易。

  我們的Portfolio類(lèi)不再是依賴(lài)于TokyoStockExchange類(lèi)的實(shí)現細節,而是依賴(lài)于StockExchange接口這個(gè)抽象的概念,這樣就隔離了特定的細節。而其實(shí)我們的類(lèi)就遵循了另一條類(lèi)的設計原則,依賴(lài)倒置原則(Dependency Inversion Principle , DIP),因為依賴(lài)倒置原則的本質(zhì),實(shí)際上就是認為類(lèi)應該依賴(lài)于抽象,而不是依賴(lài)于具體細節。

  四、一些思考與總結

  讓軟件能夠保持工作和讓軟件代碼整潔,是兩種截然不同的工作。我們中大多數人腦力有限,只能更多把更多精力放在讓代碼能夠工作上,而不是放在保持代碼有組織和整潔上。

  問(wèn)題是太多人在程序能夠正常工作時(shí)就以為萬(wàn)事大吉了。我們沒(méi)能把思維轉向有關(guān)代碼組織和整潔的部分,我們只是一直在做新的需求,而不是回頭將臃腫的類(lèi)切分為只有單一權責的去耦式單元。

  與此同時(shí),許多開(kāi)發(fā)者害怕數量巨大的短小單一目的的類(lèi)會(huì )導致難以一目了然抓住全局。他們認為,要搞清楚一件較大的工作如果完成,就得在類(lèi)與類(lèi)之間找來(lái)找去。其實(shí),有大量短小的類(lèi)的系統并不比有少量龐大類(lèi)的系統更難掌控。問(wèn)題是:你是想把工具歸置于有許多抽屜、每個(gè)抽屜中裝有定義和標記的良好組件的工具箱中呢,還是想要少數幾個(gè)能隨便把所有東西都扔進(jìn)去的抽屜呢?大概我們都更趨向于選擇前者。

  每個(gè)達到一定規模的系統都包含大量邏輯和復雜性。管理這種復雜性的首要目標就是加以組織,以便開(kāi)發(fā)者能知道在哪里找到需要的內容,專(zhuān)注于當下工作直接相關(guān)的具體模塊。反之,擁有巨大、多目的類(lèi)的系統,總是讓我們在目前并不需要了解的一大堆東西中艱難跋涉。

  最終再強調一下:系統應該由許多短小的類(lèi)而不是少量巨大的類(lèi)組成。每個(gè)小類(lèi)封裝一個(gè)權責,只有一個(gè)修改的原因,并與少數其他類(lèi)一起協(xié)同達成期望的系統行為。

  五、本文涉及知識點(diǎn)提煉整理

  原則一:合理地分布類(lèi)中的代碼。 類(lèi)中代碼的分布順序大致是:

  1. 公有靜態(tài)常量

  2. 私有靜態(tài)變量

  3. 公有普通變量

  4. 私有普通變量

  5. 公共函數

  6. 私有函數

  原則二:盡可能地保持類(lèi)的封裝。盡可能使函數或變量保持私有,不對外暴露太多細節。

  原則三:類(lèi)應該短小,盡量保持單一權責原則。類(lèi)或模塊應有且只有一條加以修改的理由。

  原則四:合理提高類(lèi)的內聚性。我們希望類(lèi)的內聚性保持在較高的水平。內聚性高,表示類(lèi)中方法和變量相互依賴(lài),相互結合成一個(gè)邏輯整體。

  原則五:有效地隔離修改。類(lèi)應該依賴(lài)于抽象,而不是依賴(lài)于具體細節。盡量對設計解耦,做好系統中的元素的相互隔離,做到更加靈活與可復用。

  本文就此結束。

  至此,《代碼整潔之道》的精讀與演繹系列文章,已經(jīng)完結。

  Best Wish~



關(guān)鍵詞: 代碼

評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>