《無瑕的程式碼》- SOLID 設計原則

Jason Z
jason-read
Published in
Mar 8, 2021

無暇程式碼可以說是軟體界的經典,推薦所有不論是不是軟體工程師的人都值得一看。就像武俠小說中的武林秘笈一樣,不會武功的人學習了可以強身健體,會一點武功的人學習之後,武功大有助益,武功老練的人,則是更可以發現一片新天地。

這本書主要在探討軟體架構的設計方法論,提供讀者架構設計的原則,如何設計一個大部分人都看得懂,而且好維護的軟體架構,這是一門非常不容易的功夫。我們總是在不自覺當中,寫出很多只有自己覺得很厲害、自己看得懂的程式碼,事實上對別人難說,難懂又不易解。因此作者認為人人都可以寫程式,就算是剛入門學習程式的人,也可以用暴力的方法寫出厲害的程式。但是厲害的軟體工程師,最大的差別就是把程式當成是工藝,如何寫出優雅又容易維護的程式碼,才是功力的展現。

以下擷取其中一個篇章,盡量不用太多術語,而用平白直述或比喻的方式,分享一篇我覺得最受用的篇章。

另外還會附上以程式碼,用實際的程式碼講解SOLD原則的實作方式

SOLID 設計原則

SOLID設計原則分別代表五個設計原則,取其第一個字母,分別是:

  • Single-responsibility principle (SRP) 單一職責原理
  • Open–closed principle (OCP) 開閉原則
  • Liskov substitution principle (LSP) 里氏替換原則
  • Interface segregation principle (ISP) 介面隔離原則
  • Dependency inversion principle (DIP) 依賴反向原則

單一職責原理

單一職責原理如同字面上的意思,每次都只做一件事情,不做太多的事情。因為我們沒有辦法一次做太多的事情,一次專注做一件事情不但比較容易專注也比較容易深入。

就像是以前我們在學校的時候,都會有不同的股長,例如體育股長、衛生股長、風紀股長等等,每一個股長都有其專屬的職責,例如體育股長要掌管大家的體育活動,根據活動安排器材。而衛生股長掌管大家的衛生環境,維護衛生整潔。風紀股長就掌管班上的秩序,讓班上不要太吵。每個股長的工作都很單一,不會跨到別人的工作去,像是要風紀股長管環境或要體育股長管秩序,這不但不是本來的工作,甚至因為不熟悉也不一定做得好。

而且如果下次要重選班上的幹部的話,如果要重選「管秩序的人」,到底是要重選應該管秩序的「風紀股長」還是重選參與管秩序的「體育股長」,相信這是一個會令人頭痛的事情,因為職責不單一,過於複雜,所以才會有這個問題。所以單一職責原理應該:

一個功能類別應該只有單一職責

以程式的角度講解:單一職責原則

開閉原則

瀏覽器對於現在的人來說,是上網不可或缺的工具。尤其現在的瀏覽器都主打擴充功能,可以自己新增一些瀏覽器沒有,但是別人提供的功能。

像是我就很喜歡使用 ABP 這套擋廣告的軟體,可以讓我在瀏覽網頁的時候,不必再看到一堆鋪天蓋的廣告。

瀏覽器讓我可以安裝擴充套件的功能,就符合所謂的「開閉原則」

開就是對擴充功能開放,提供我們可以自由自在的擴充功能

關就是對修改關閉,在我們擴充功能的時候,確保功能只有新增,而不會影響到原本的功能。

也就是說,當我新增擋廣告的擴充套件的時候,不應該讓我的瀏覽器的任何一個功能壞掉,例如:就不能將我喜歡的頁面加入書籤等等,這是不允許的

軟體應該對擴展開放,對修改關閉

以程式的角度講解:開閉原則

里氏替換原則

想像一下,我們對於手機的要求是什麼?最基本的要求莫過於可以打電話,沒有一支手機是不能打電話的。如果不能打電話的手機,我們不會稱呼他為手機。

因此不論是以前的智障型手機或是現在的智慧型手機,都有打電話的功能。

智慧型手機算是手機的一種,因此也具備打電話的功能,但是除了打電話的功能外,還有額外的功能,像是上網、拍照等等功能。也就是說子類別(智慧型手機)一定要完全繼承父類別(手機)的功能,且不能出錯。

子型別必須遵從父型別的行為進行設計

以程式的角度講解:里氏替換原則

介面隔離原則

不知道大家有沒有看過這一張圖,這是平偉的梗圖。事情來由是這樣子的,電視台接獲民眾投訴有一家黑心的機車行,於是記者喬裝並跟老闆平偉說煞車皮壞掉要換煞車皮,結果老闆卻說換煞車皮之前一定要先拆坐墊,於是把記者的坐墊也拆掉了,然後收取公道價

這就是一個違反介面介面隔離原則的例子。試想,為什麼換煞車皮一定要被迫做跟它無關的方法先拆座墊才能換煞車皮呢?拆座墊的方法跟換煞車皮一點關係都沒有。

再舉一個例子,假設我今天是一名駕駛,我所需要的技能就是知道如何開車、停車、倒車入庫等等駕駛技能。總使我開著車子,但是我卻沒有必要知道引擎發動的原理或是引擎的歷史等等,這個雖然重要,但是和我的駕駛沒有一點關係。

也就是說:

一個類別不應該被強迫實作一個它不需要的方法

以程式的角度講解:介面隔離原則

依賴反向原則

以上是台北的大眾運輸路網圖,在此問一個問題就是,如何從台北到板橋?

也許有人可以馬上不假思索回答出:搭乘板南線就可以從台北到板橋了!

但是從台北到板橋就只能搭乘板南線嗎?沒有別的方法嗎?還有很多別的辦法,例如:除了搭乘捷運以外,也可以搭乘台鐵,不論搭乘站站停的區間車或是只停大站的自強號,都可以從台北到板橋,搭乘時間甚至比台北短,車票錢還比捷運低。甚至也可以搭乘高鐵,雖然沒有比較快而且貴很多,但是不失為也是一個很好的方法。

換句話說,如果將從台北到板橋只依賴在搭乘板南線的方法的話,會有一個風險,假如哪一天捷運突然發生異常的話,那就必須等捷運恢復正常,才能出發,很不方便。因此比較好的辦法是不要將做一件事情的方法依賴在一個特別的方法,而應該依賴於一個普遍的方法。

因此應該說,搭乘交通工具就可以從台北到板橋了!

這樣一來,就有很多種又很彈性的方法,可以從台北到板橋,不論想搭高級一點的高鐵或是搭平常一點的捷運,甚至其中一種發生異常,都可以迅速轉換交通工具,也就是說:

高層模組不應該依賴底層模組,它們都應該依賴抽象。

抽象不應該依賴細節。細節應該依賴抽象。

以程式的角度講解:依賴反向原則

--

--

Jason Z
jason-read

哲學系畢業的前端工程師,大部分時間都在搞鐵路系統,喜歡寫程式外,更喜歡鐵道,欣賞路上每個平凡的風景