如何設(shè)計(jì)優(yōu)雅的類結(jié)構(gòu)(clean code閱讀筆記之九)

如何寫(xiě)出優(yōu)雅的類結(jié)構(gòu)

注:正文中的引用是直接引用作者的話,兩條橫線中間的段落的是我自己的觀點(diǎn)倘核,其他大約都可以算是筆記了陕见。

「Clean Code」這本書(shū)從這一章開(kāi)始文風(fēng)有些變化秘血,感覺(jué)比較亂,很多概念在之前的章節(jié)也提到過(guò)评甜,因?yàn)檫@本書(shū)的某些章節(jié)是不同的人編寫(xiě)的灰粮,所以這種情況也難免,所以可能會(huì)有些小節(jié)我會(huì)幾句話簡(jiǎn)單帶過(guò)忍坷。

本章講的是類的組織結(jié)構(gòu)粘舟,其實(shí)很多這些概念我們?cè)趯W(xué)校里學(xué)習(xí)OOP時(shí)可能都有學(xué)到過(guò)奈嘿,有些人可能會(huì)覺(jué)得講得比較虛系羞,但文中確實(shí)有些細(xì)節(jié)還是解開(kāi)了一些之前的疑惑荔茬,姑且當(dāng)做復(fù)習(xí)面向?qū)ο蟮母拍钜埠谩?/p>


在前面的章節(jié)中詳細(xì)討論了命名轻庆、方法和數(shù)據(jù)結(jié)構(gòu)等等這些概念,它們能夠幫助我們更好地理解在代碼行或者代碼塊的級(jí)別里如何寫(xiě)出簡(jiǎn)潔優(yōu)雅晰骑。在此基礎(chǔ)上适秩,我們還是要在更高的層面上去探究代碼簡(jiǎn)潔之道。在現(xiàn)代的高級(jí)語(yǔ)言編程世界里硕舆,類是系統(tǒng)的基本組成部分秽荞,這章就著重討論一下如何寫(xiě)出好的類。

類的組織結(jié)構(gòu)

對(duì)于類的代碼結(jié)構(gòu)抚官,Java中有一套不成文的約定:

  • 一個(gè)類應(yīng)該以一系列的常量和變量定義作為開(kāi)始
  • 如果有公共靜態(tài)常量扬跋,它們應(yīng)該放在最前邊
  • 接下來(lái)是私有的靜態(tài)常量
  • 接下來(lái)是私有的實(shí)例變量
  • 類中不應(yīng)該有公共的變量
  • 緊接著是公共的方法
  • 一些私有的方法應(yīng)該緊接著它被調(diào)用的共有方法后邊

封裝

在OOP中很多概念都是相通的,封裝作為OOP的一個(gè)基本概念突出了「開(kāi)閉原則」的重要性凌节,它很好地解決了一些擴(kuò)展性的問(wèn)題胁住,它使得接口的提供者可以屏蔽此接口的具體實(shí)現(xiàn),從而可以在一個(gè)較自由的范圍內(nèi)去修改自己的實(shí)現(xiàn)刊咳。

按照封裝的概念,一個(gè)類中的所有變量都應(yīng)該是私有的儡司,但同時(shí)也不要對(duì)此概念太過(guò)執(zhí)著娱挨,前邊的章節(jié)也提到過(guò)測(cè)試的重要性,有時(shí)候測(cè)試需要某些類的變量可訪問(wèn)捕犬,那么可以考慮給它們賦予protected屬性跷坝。但應(yīng)該盡可能的保證封裝的特性。

類應(yīng)該盡可能的「小」

在函數(shù)的那一章我們提到過(guò)方法應(yīng)該設(shè)計(jì)的盡可能的小碉碉,我們衡量函數(shù)使用代碼行數(shù)柴钻,在這里我們衡量類使用「職責(zé)」。

一個(gè)類的職責(zé)應(yīng)該是唯一的垢粮,這才符合OOP對(duì)現(xiàn)實(shí)世界的模擬的概念贴届。職責(zé)往往是和代碼的行數(shù)正相關(guān),但它們并不是完全正相關(guān)的蜡吧,如代碼9-1中所示:

代碼9-1
public class SuperDashboard extends JFrame implements MetaDataUser{
  public Component getLastFocusedComponent()
  public void setLastFocused(Component lastFocused)
  public int getMajorVersionNumber()
  public int getMinorVersionNumber()
  public int getBuildNumber()
}

這個(gè)類只包含了5個(gè)函數(shù)毫蚓,那么它是不是已經(jīng)足夠小了呢?非也昔善,它包含了兩個(gè)不同的職責(zé)——它同時(shí)管理「版本號(hào)」與「某個(gè)JFrame組件」元潘。

單職責(zé)原則(SRP)

SRP的意思是說(shuō)一個(gè)類(或者一個(gè)模塊)應(yīng)該有且只有一個(gè)要修改它的原因(職責(zé))。

比如代碼9-1中所示那樣君仆,我們可能有兩個(gè)原因要去修改類SuperDashboard翩概,一個(gè)是版本號(hào)改變了牲距,另一個(gè)是獲取組件的方法變了。誠(chéng)然钥庇,當(dāng)我們修改了獲取lastFocus組件的方法時(shí)牍鞠,往往是要修改版本號(hào)的,但是反過(guò)來(lái)就不一定了上沐。

SRP是OOP中最重要的設(shè)計(jì)理念之一皮服,但同時(shí)也是最常被違反的理念之一〔瘟「使軟件可以工作」和「使軟件簡(jiǎn)潔優(yōu)雅」是兩個(gè)截然不同的的工作龄广,我們常常沒(méi)有時(shí)間也沒(méi)有精力同時(shí)關(guān)注這兩者,然后就只關(guān)注前者了蕴侧。


在中國(guó)當(dāng)下的現(xiàn)實(shí)環(huán)境是择同,很多代碼的需求方(往往是老板)根本不在乎代碼的可維護(hù)性、可擴(kuò)展性甚至健壯性净宵,他們的要求常常是軟件快速上線敲才,而不是簡(jiǎn)潔優(yōu)雅但要延期上線的軟件。此外择葡,還有有很多代碼寫(xiě)完之后可能永遠(yuǎn)也不會(huì)被維護(hù)和修改紧武,所以「使軟件簡(jiǎn)潔優(yōu)雅」慢慢地塊要變成一種個(gè)人追求,變成了「大家都說(shuō)這樣做更好敏储,但真正這么做的卻很少」阻星。

我倒是覺(jué)得盡管在實(shí)際的編程工作中不得不不斷地進(jìn)行妥協(xié),但是只要把clean code的理念放在心中已添,并用它來(lái)審視自己的代碼妥箕,我們總是會(huì)寫(xiě)出越來(lái)越好的代碼。編程是如此更舞,人生何嘗不是如此畦幢。


問(wèn)題是很多人認(rèn)為軟件「可以工作」的那一刻,我們的工作就結(jié)束了缆蝉。我們接下來(lái)要做的是解決下一個(gè)問(wèn)題而不是回過(guò)頭來(lái)把這些超級(jí)類分解成解耦的小單元宇葱。與此同時(shí),還有很多人很害怕看到「大量的小而職責(zé)單一的類」刊头,覺(jué)得那樣會(huì)使他們很難去從大方向上理解整個(gè)系統(tǒng)贝搁。事實(shí)則恰恰相反,大量的小的職責(zé)單一的解耦的類往往帶來(lái)更多的好處芽偏。

我們的目標(biāo)是這樣的:我們的系統(tǒng)由大量的小的職責(zé)單一的類組成雷逆,而不是少數(shù)幾個(gè)超級(jí)大類。每一個(gè)類都只與少數(shù)幾個(gè)其他類進(jìn)行交互(這點(diǎn)有點(diǎn)像迪米特法則)污尉。

內(nèi)聚

內(nèi)聚的概念是這樣的:一個(gè)類應(yīng)該只有少數(shù)的幾個(gè)實(shí)例變量膀哲,這個(gè)類的每個(gè)方法都應(yīng)該操作這個(gè)類中的一個(gè)或多個(gè)實(shí)例變量往产。通常一個(gè)方法操作的實(shí)例變量越多,那么這個(gè)方法對(duì)于這個(gè)類來(lái)說(shuō)聚合性就越高某宪。一個(gè)類中的所有方法都操作了這個(gè)類中的所有實(shí)例變量仿村,那么這個(gè)類就是聚合型最高的。

但是兴喂,通常來(lái)說(shuō)這樣的超級(jí)內(nèi)聚的類不太可能出現(xiàn)蔼囊,也不建議去建立這樣的類。但我們還是想要一個(gè)類的內(nèi)聚性是高的衣迷,這表明這個(gè)類中的幾個(gè)組成部分是互相依賴不可分割的畏鼓。如代碼9-2中所示的一個(gè)堆棧的簡(jiǎn)單實(shí)現(xiàn),就是一個(gè)內(nèi)聚性很高的例子:

代碼9-2
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;
    }

}

在這個(gè)類中出了方法size()之外壶谒,其他幾個(gè)方法都同時(shí)使用到了這個(gè)類中的兩個(gè)變量云矫。所以寫(xiě)出高內(nèi)聚的類的訣竅就是,保持類中的變量個(gè)數(shù)很少汗菜,方法很小让禀。如果一個(gè)你代碼中某個(gè)類的內(nèi)聚性很低,那么你就要考慮一下陨界,是否要把它拆分成幾個(gè)更小的類了巡揍。

維護(hù)類的高內(nèi)聚往往會(huì)帶來(lái)更小的類

只要你不斷的將大的方法拆分成小的方法,最直接的結(jié)果就是你會(huì)看到越來(lái)越多的類菌瘪。

舉例來(lái)說(shuō)腮敌,我們經(jīng)常會(huì)碰到一個(gè)場(chǎng)景,我們想把一個(gè)超級(jí)方法中的某一個(gè)邏輯(可能是幾行代碼)抽出來(lái)重構(gòu)成為一個(gè)新的方法麻车,然后抽取之后的新方法需要傳入4個(gè)在這個(gè)超級(jí)方法中定義的變量,這種情形下斗这,最好就是把這4個(gè)變量編程類級(jí)別的變量动猬,這樣我們抽取的這個(gè)新方法就不需要傳入任何的參數(shù)了。

但是表箭,這樣做之后這個(gè)類的內(nèi)聚性就降低了——這4個(gè)變量只在兩個(gè)方法中被調(diào)用——但是這種「有幾個(gè)方法想要分享幾個(gè)變量」的行為不正是類定義的由來(lái)嗎赁咙。所以,一旦你的類的內(nèi)聚性降低時(shí)免钻,就去著手把它拆分為更小的類吧彼水。

所以,拆分類可以從拆分超級(jí)方法開(kāi)始极舔,這樣往往能給我們帶來(lái)一個(gè)更清晰的類的組織結(jié)構(gòu)凤覆。

為了變化而設(shè)計(jì)

對(duì)于大多數(shù)的系統(tǒng),變化是持續(xù)發(fā)生的拆魏。每次發(fā)生改變盯桦,都可能對(duì)我們的現(xiàn)有系統(tǒng)造成威脅慈俯,那么我們?cè)O(shè)計(jì)系統(tǒng)中「類的組織結(jié)構(gòu)」時(shí)就要盡可能降低這種風(fēng)險(xiǎn)。

然后在這個(gè)小節(jié)作者舉了個(gè)使用abstract類來(lái)解決對(duì)類的修改的問(wèn)題拥峦√欤「對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉」最好的一個(gè)實(shí)現(xiàn)就是使用抽象類略号,因?yàn)閷?duì)于此抽象概念增加時(shí)只需要多寫(xiě)一個(gè)此抽象類的實(shí)現(xiàn)類刑峡,而不是去修改現(xiàn)有的實(shí)現(xiàn)類。

隔離變化

需求會(huì)不斷地變化玄柠,所以我們的代碼實(shí)現(xiàn)也會(huì)不斷地變化突梦。使用抽象類可以很大程度上地隔離這種變化。

這一小節(jié)的宗旨就是說(shuō)要使用面向接口編程随闪,使得此接口的調(diào)用者對(duì)接口依賴而不是對(duì)實(shí)現(xiàn)依賴阳似,這樣就實(shí)現(xiàn)了「隔離變化」。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末铐伴,一起剝皮案震驚了整個(gè)濱河市撮奏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌当宴,老刑警劉巖畜吊,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異户矢,居然都是意外死亡玲献,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)梯浪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)捌年,“玉大人,你說(shuō)我怎么就攤上這事挂洛±裨ぃ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵虏劲,是天一觀的道長(zhǎng)托酸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)柒巫,這世上最難降的妖魔是什么励堡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮堡掏,結(jié)果婚禮上应结,老公的妹妹穿的比我還像新娘。我一直安慰自己泉唁,他們只是感情好摊趾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布币狠。 她就那樣靜靜地躺著,像睡著了一般砾层。 火紅的嫁衣襯著肌膚如雪漩绵。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天肛炮,我揣著相機(jī)與錄音止吐,去河邊找鬼。 笑死侨糟,一個(gè)胖子當(dāng)著我的面吹牛碍扔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秕重,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼不同,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了溶耘?” 一聲冷哼從身側(cè)響起二拐,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凳兵,沒(méi)想到半個(gè)月后百新,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡庐扫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年饭望,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片形庭。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡铅辞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萨醒,到底是詐尸還是另有隱情斟珊,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布验靡,位于F島的核電站倍宾,受9級(jí)特大地震影響雏节,放射性物質(zhì)發(fā)生泄漏胜嗓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一钩乍、第九天 我趴在偏房一處隱蔽的房頂上張望辞州。 院中可真熱鬧,春花似錦寥粹、人聲如沸变过。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)媚狰。三九已至岛杀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崭孤,已是汗流浹背类嗤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辨宠,地道東北人遗锣。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像嗤形,于是被迫代替她去往敵國(guó)和親精偿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,183評(píng)論 25 707
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法赋兵,類相關(guān)的語(yǔ)法笔咽,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法毡惜,異常的語(yǔ)法拓轻,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,644評(píng)論 18 399
  • “自我實(shí)現(xiàn)的預(yù)言”的關(guān)鍵機(jī)制是選擇性注意扶叉,即維護(hù)自己的自戀,選擇“我是對(duì)的”內(nèi)容進(jìn)行關(guān)注帕膜。 覺(jué)知自己的“自我實(shí)現(xiàn)的...
    綠樹(shù)蔭濃閱讀 225評(píng)論 0 0
  • 佛陀告訴我們:人的一切快樂(lè)枣氧,都是從利益他人中產(chǎn)生的;一切痛苦垮刹,都是由只為自己而引起的达吞。 今天看潘琴的日志看到這段話...
    徐曉琳111閱讀 327評(píng)論 0 2
  • 餓的時(shí)候,都想吃肉荒典±医伲可是人多肉少,豬肉怎么分寺董? 有善良的人說(shuō)了覆糟,不患寡而患不均,有種分法遮咖,叫做大鍋飯滩字,雖然肉不多,...
    巴拿馬草帽閱讀 792評(píng)論 3 1