物件導向的SOLID

閱讀時間約 5 分鐘

說到物件導向就必須提五個原則,統稱SOLID,它被認為是物件導向的重要概念。這五個原則並不只適用於物件導向,事實上它很像函數式編程的習慣。它的命名很奇怪且容易讓人混淆,所以我會用我自己的翻譯解釋。


Single Responsibility Principle 是「單一職責原則」,認為一個模組應該只有一個職責。模組是將相同功能的函式和類別搜集起來的實體,我們應該根據「職責」將函式裝進一個模組,這樣能提升程式的可維護性。當你想要寫一個網路服務時,你應該要將有關連線的部分拆出來包成一個模組,這樣當程式出錯時更容易找到問題點。類別本身也是一個小模組,因此也適用於這個原則,但它的範圍非常小,所以職責應該也要特別單一,例如管理特定連線狀態。在函數式編程,資料結構沒有職責,畢竟它不會自帶行為,所有關聯方法都是藉由特性系統附加上去的,若要封裝也應該是對這些特性的實作封裝。但資料結構還是應該只有單一語義,複用相同的類型會使程式的可讀性降低。單一職責不代表一定要隱藏實作細節,它只是提供一個組織程式碼的判斷準則。它很常跟「單一功能」(do one thing well)搞混,這是指一個函式應該只做一件事,並把它做到最好。「職責」是指一系列相關的操作,而非一件事,複雜的系統通常需要多個操作才能完整使用。


Open-Closed Principle 是「擴充修改原則」,認為軟體應該是對於擴充開放的,但是對於修改封閉的。當一個公開的函式定義後,就不應該再修改,如果要修改也不應該違反定義函式時文件所描述的特性。這些特性描述是這個函式的承諾,它告訴使用者這個函式在做什麼。如果因為當初設計問題導致承諾本身就是不合理的,應該標示函式為過時並另外定義新的函式取代。如果想要擴充這個函式的功能,則應該另外定義新的函式,並把原本的函式轉接過去,而不應該直接修改原本的函式。也可以透過多型擴充函式,讓原本的函式類型也被涵蓋其中,例如增加可選參數(多載)或是泛型化,但這種方法很容易讓依賴類型推論的情況出問題,因此並不推薦。因為承諾的關係,已經定義好的函式很難再透過修改擴充,因此在定義函式時就應該考慮未來需要擴充的狀況。可以透過泛型化和介面/特性把實作時所用到的方法抽離出來,讓使用者自己決定。這個規則可以應用在任何實體上,例如類型、介面、模組和類別。這個原則將實體的語義與承諾分離出來,強調函式實作應該更著重於實現承諾而非完成業務目標。


Liskov Substitution principle是「子類替換原則」,認為程式中的對象應該是可以在不改變程式正確性的前提下被它的子類所替換的。它描述了子類關係的定義,我們已經在前幾篇文章討論過,在此不再贅述。


Interface-Segregation principle是「介面細分原則」,認為多個特定客戶端介面要好於一個廣泛用途的介面。如果介面包含太多功能,就會使依賴此介面的函式或類別依賴太多無關的方法,使得我們必須為不需要的功能實作方法。透過將功能細分成多個介面,我們可以更容易掌握誰真的依賴於這個方法,從而減少耦合。這對於物件導向來說更重要,因為類別本身就是一個介面,但是當我們增加一些方法給他時,就會讓依賴此類別的函式也增加依賴關係。透過在他們之間定義細分的介面,可以讓函式不會直接受到實作改變的影響,從而起到隔離的作用。


Dependency Inversion Principle是「依賴抽象原則」,認為一個方法應該依賴於抽象而不是一個實例。高階、複雜的操作是由多個低階、特定的操作組成,因此一般來說高階邏輯總是依賴於低階的實作,但這麼做會讓高階的方法很難抽象化。因此應該反過來讓低階的實作依賴於高階的實體,例如透過定義高階的介面讓低階的部分去實作它,而非寫死在高階的方法實作裡。反轉的目的不是為了解決循環依賴的問題,而是為了讓高階的模組更容易抽象化與重用。事實上不是只有高階模組需要抽象化,低階操作不代表它必須寫死成特定實作,具體與抽象的分類和高階與低階的分類並不衝突。解決因依賴造成難以抽象化的方法就是讓實作依賴於抽象,而非直接依賴於實作,具體來說可以參數化依賴的部分讓使用者自己傳入(依賴注入),或是將依賴的特性抽取成介面,並讓具體類別實作這個介面。可以說依賴抽象是一種進階的參數化方法,它把程式碼裡面需要開放的特性作為參數抽取出來,讓程式碼不會直接依賴具體的方法,而是依賴這個參數。


除了子類替換原則,其他的原則基本上都跟物件導向沒有直接關係,它可以應用於任何軟體開發上。這些概念對應於函數式編程的一些習慣用法:OCP擴充修改原則強調承諾的重要性,這對於注重規則的函數式程式語言尤其重要;ISP介面細分原則強調依賴關係的管理,函數式編程常常依據規則建立介面而非依據功能,因此不常遇到這種問題;DIP依賴抽象原則對函數式編程來說只是一種參數化方法,這與高階還是低階、是否反轉依賴關係無關。對於函數式編程,健全的機制往往可以避免生產出不好的程式碼,而非依賴於開發者的素養。或者說當你理解函數式編程的特性,就更容易掌握將業務邏輯轉換成程式設計的邏輯思路的方法。

avatar-img
4會員
28內容數
這不是教你如何從物件導向到函數式編程的入門教程。我會深入探討物件導向與函數式編程的差異,並討論為什麼你應該使用函數式編程並徹底放棄物件導向。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
have bear的沙龍 的其他內容
讓我在這篇文章總結一下前面對物件導向設計的討論,我們討論了物件導向的四個特性:繼承、抽象、多型、封裝,分析了它們的問題,並跟函數式編程的思維做比較。我們引入了與之相對應的特性:泛型、特性系統、模組化,有些特性雖然跟那四個特性很像,但在一些細微的地方有不同的詮釋,使得整體思考方式很不一樣。 「繼
物件導向設計的一個重點就是封裝,這有很多層面上的意義,但基本上就是控制物件的成員變數和方法的存取權。物件導向的封裝還跟繼承機制有關,這使得有一些時候我們逼不得已必須把函式定義在類別上,這種做法使得物件的功能變得難以拆解。封裝應該是模組的職責,並不需要再給物件相同的能力。 一般的模組系統就是把相
上一篇文章提到有些介面不應被繼承,但物件導向的子類別只能繼承父類別的介面,因而產生一些不合適的介面實作。trait/typeclass則沒有這種繼承機制,這似乎使需要繼承的特性無法直接使用。然而函數式導向比起繼承,更適合使用組合,根本不需要使用繼承疊加特性。 資料類型的定義往往跟怎麼建構模型相
類似於trait/typeclass的特性系統能提供程式「延展性」,它能讓函式針對不同的類型做出不同的行為。這種機制與物件導向的繼承非常像,然而特性系統的彈性比較大一點,而且概念上也有一些差別。為了探討討論這些差異,我們必須深入了解繼承機制到底是什麼。 繼承並不是建立子類關係的唯一方法。所謂的
所謂的多型是讓一個函式或是資料結構能擁有多個不同的類型,其中上一篇文章所談的就是參數多型(parametric polymorphism),這篇文章將繼續討論特設多型(ad hoc polymorphism)。特設多型跟泛型的差別在於:泛型函式對於所有的類型只能有一種實作,而特設多型會根據類型有不同
前幾篇文章討論了類型系統的合理性,而這會影響我們對於變數與函式是什麼的理解。其中泛型是當中很重要的一個元素,很多討論都是基於泛型的使用。泛型會大大地增加類型系統的複雜度,因此有些語言選擇不提供泛型(go),但缺少泛型又會使簡單的容器都無法用類型精確描述。泛型的強大必須結合有紀律的類型系統才能顯現,但
讓我在這篇文章總結一下前面對物件導向設計的討論,我們討論了物件導向的四個特性:繼承、抽象、多型、封裝,分析了它們的問題,並跟函數式編程的思維做比較。我們引入了與之相對應的特性:泛型、特性系統、模組化,有些特性雖然跟那四個特性很像,但在一些細微的地方有不同的詮釋,使得整體思考方式很不一樣。 「繼
物件導向設計的一個重點就是封裝,這有很多層面上的意義,但基本上就是控制物件的成員變數和方法的存取權。物件導向的封裝還跟繼承機制有關,這使得有一些時候我們逼不得已必須把函式定義在類別上,這種做法使得物件的功能變得難以拆解。封裝應該是模組的職責,並不需要再給物件相同的能力。 一般的模組系統就是把相
上一篇文章提到有些介面不應被繼承,但物件導向的子類別只能繼承父類別的介面,因而產生一些不合適的介面實作。trait/typeclass則沒有這種繼承機制,這似乎使需要繼承的特性無法直接使用。然而函數式導向比起繼承,更適合使用組合,根本不需要使用繼承疊加特性。 資料類型的定義往往跟怎麼建構模型相
類似於trait/typeclass的特性系統能提供程式「延展性」,它能讓函式針對不同的類型做出不同的行為。這種機制與物件導向的繼承非常像,然而特性系統的彈性比較大一點,而且概念上也有一些差別。為了探討討論這些差異,我們必須深入了解繼承機制到底是什麼。 繼承並不是建立子類關係的唯一方法。所謂的
所謂的多型是讓一個函式或是資料結構能擁有多個不同的類型,其中上一篇文章所談的就是參數多型(parametric polymorphism),這篇文章將繼續討論特設多型(ad hoc polymorphism)。特設多型跟泛型的差別在於:泛型函式對於所有的類型只能有一種實作,而特設多型會根據類型有不同
前幾篇文章討論了類型系統的合理性,而這會影響我們對於變數與函式是什麼的理解。其中泛型是當中很重要的一個元素,很多討論都是基於泛型的使用。泛型會大大地增加類型系統的複雜度,因此有些語言選擇不提供泛型(go),但缺少泛型又會使簡單的容器都無法用類型精確描述。泛型的強大必須結合有紀律的類型系統才能顯現,但
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
物件導向(OOP),不僅提供了更結構化的程式碼組織方式,還有助於提高程式碼的可讀性、可重用性和可維護性。本文將介紹物件導向概念中的類別、對象、繼承、封裝和多型,並透過具體範例來展示這些概念如何在實際編程中應用。
Thumbnail
  物件導向說到這邊其實修飾詞不只有之前所提的public、private這些,還有可能遇到抽象(abstract)、虛擬(virtual)、覆寫(override)、隱藏(new)、密封(sealed)、介面(interface)等等,我們就來看一下它們的差異吧~
Thumbnail
單一職責原則(Single Responsibility Principle) 里氏替換原則(Liskov Substitution Principle) 依賴反轉原則(Dependence Inversion Principle) 最少知識原則(得墨忒耳定律)(Law Of Demeter)
Thumbnail
本筆記除了以文字說明SOLID設計原則以外,並以Java code實際舉例。 Single Responsibility Principle (SRP) 單一職責原則 每個人負責屬於自己的職責,不該承擔太多職責,大家各自做自己應該做的事情,且不會互相干擾。 續上程式碼,修改如下: Output:
Thumbnail
在類別一節中,我們可以用Student類別的實體來存取類別中的name變數、score字典、以及其中的所有方法,這些可以被類別以外的程式碼所直接存取的屬性稱為公有屬性(public attribute)、可以被類別以外的程式碼所直接呼叫的方法稱為公有方法(public method)。
Thumbnail
到目前為止,我們所學習的都是程序性的程式設計(procedural programming),也就是程式碼是透過一連串的指令組成的程序或函數,由上而下依序執行不同的程序或是呼叫函數來完成程式的功能。 Python其實是一種物件導向的程式(object oriented programming, 簡稱
Thumbnail
物件導向的概念,以python程式為範例。
Thumbnail
圖書館借的一本書,原本只是打算找Unity相關的書,但找不到的前提下隨意挑了一本書。 基本資料 書名: 作者:鄭仲平,高煥堂 出版日期:2019/01/31
也許有人聽過, Kotlin 是物件導向語言。 既然都特別提了,肯定還有其他種語言,那就是程序導向語言。 最大的差別就是因為物件導向語言有了物件的概念,所以有了三大能力:繼承(Inheritance)、封裝(Encapsulation)和多型(Polymorphism)。
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
物件導向(OOP),不僅提供了更結構化的程式碼組織方式,還有助於提高程式碼的可讀性、可重用性和可維護性。本文將介紹物件導向概念中的類別、對象、繼承、封裝和多型,並透過具體範例來展示這些概念如何在實際編程中應用。
Thumbnail
  物件導向說到這邊其實修飾詞不只有之前所提的public、private這些,還有可能遇到抽象(abstract)、虛擬(virtual)、覆寫(override)、隱藏(new)、密封(sealed)、介面(interface)等等,我們就來看一下它們的差異吧~
Thumbnail
單一職責原則(Single Responsibility Principle) 里氏替換原則(Liskov Substitution Principle) 依賴反轉原則(Dependence Inversion Principle) 最少知識原則(得墨忒耳定律)(Law Of Demeter)
Thumbnail
本筆記除了以文字說明SOLID設計原則以外,並以Java code實際舉例。 Single Responsibility Principle (SRP) 單一職責原則 每個人負責屬於自己的職責,不該承擔太多職責,大家各自做自己應該做的事情,且不會互相干擾。 續上程式碼,修改如下: Output:
Thumbnail
在類別一節中,我們可以用Student類別的實體來存取類別中的name變數、score字典、以及其中的所有方法,這些可以被類別以外的程式碼所直接存取的屬性稱為公有屬性(public attribute)、可以被類別以外的程式碼所直接呼叫的方法稱為公有方法(public method)。
Thumbnail
到目前為止,我們所學習的都是程序性的程式設計(procedural programming),也就是程式碼是透過一連串的指令組成的程序或函數,由上而下依序執行不同的程序或是呼叫函數來完成程式的功能。 Python其實是一種物件導向的程式(object oriented programming, 簡稱
Thumbnail
物件導向的概念,以python程式為範例。
Thumbnail
圖書館借的一本書,原本只是打算找Unity相關的書,但找不到的前提下隨意挑了一本書。 基本資料 書名: 作者:鄭仲平,高煥堂 出版日期:2019/01/31
也許有人聽過, Kotlin 是物件導向語言。 既然都特別提了,肯定還有其他種語言,那就是程序導向語言。 最大的差別就是因為物件導向語言有了物件的概念,所以有了三大能力:繼承(Inheritance)、封裝(Encapsulation)和多型(Polymorphism)。