Unity C# | 物件導向五大設計原則(SOLID)

閱讀時間約 5 分鐘

前言

  這篇文章將會介紹五大物件導向守則。

1. 單一職責原則(SRP: Single Responsibility Principle)

  強調「當設計封裝一個類別時,該類別只負責一件事」。這與類別抽象化的過程中,對於該類別應該負責那些功能有關。一個類別應該只負責系統中一個單獨功能的實作,但對於功能的切分及歸屬,通常也是開發過程中最困擾設計者的。

現象 | 累加再累加

  設計師一開始不太容易實現這個原則,在專案開發的過程中,不斷地往同一個類別上增加功能,最後導致類別過於龐大、介面過於複雜後才會發現問題。

建議 | 類別重構

  將類別中與實作相關功能的部分抽取出來,另外封裝維新的類別,之後再利用組合的方式將新增的類別加入原類別之中,慢慢地就能達成單一職責化的需求。

2. 開放封閉原則(OPC:Open-Closed Priciple)

  類別要「對外擴充開放、對修改關閉」。類別是實作系統某項功能的類別。當軟體的開發流程進入「完工測試」時,對於已經完成或上線的功能,就應該「關閉修改的需求」,當增加系統功能的需求發生,就要對「功能的增加保持開放」。

建議 | 功能介面化

  將系統功能的「操作方法」向上提升,抽象畫為「介面」,將「功能的實作」往下一到子類別中。
  1. 重新實作一個新的子類別。
  2. 繼承舊有的實作類別,並在新的子類別中實作新增的系統功能。

3. 里氏替代原則(LSP:Liskov Substitution Principle)

  指「子類別必須能夠替換父類別」。如果按造這個設計原則去實作一個有多層繼承的類別群組,那麼當中的父類別通常是「介面類別」或是「可被繼承的類別」。
  父類別中醫定包含了可被子類別重新實作的方法,而客戶端使用的操作介面也由父類別來定義。客戶端在使用過程中,必須不能使用到「物件強制轉型為子類別」的語法,客戶端也不應該知道,現在使用的物件是哪一個子類別實作。
  使用哪個子類別的物件來替代父物件類別,則是由類別本身的物件產生機制來決定,外界無法得知。

4. 相依性反向原則(DIP:Dependence Inversion Principle)

此原則包含兩個主題。
  • 高層模組不應該相依於低層模組,兩者都應該相依於抽象概念。
  • 抽象介面部應該相依於實作,而實作應該相依於抽象介面。

介紹 | 汽車

  汽車就是所謂的高層模組,它由引擎系統、傳動系統、懸吊系統、車身骨架系統、電裝系統等,有了低層模組的相互配合才完成一部汽車。
  然而汽車卻很容易被引擎給限定,也就是說,裝載無鉛汽油的汽車不能使用柴油做為燃料;裝載柴油引擎的汽車不能使用無鉛汽油作為燃料,這就是「高階模組相依於低層模組」的例子。

介紹 | 個人電腦

  電腦是高層模組,定義了 USB 介面,這個介面定義了硬體鎖需的規格及軟體驅動程式的撰寫規格,任何底層模組(記憶卡、隨身碟、讀卡機、相機、手機等)都能加入個人電腦的模組中。

總結 | 相依反轉

  在個人電腦中,高層模組定義介面,低層模組再依照這個介面實作,這個過程可以讓他們之間的相依關係反轉,不用像汽車那樣被限制,同時也說明了「抽象介面部應該相依於實作,而實作應該相依於抽象介面」。
  當高層模組定義好介面之後,底層模組的溝通就只能藉由介面來進行,這個介面可以是一個類別的變數或物件參考。

5. 介面分割原則(ISP:Interface Segregation Principle)

  這是在解決「客戶端不應該被迫使用他們用不到的介面方法」,這個問題一般隨著專案開發的進行而越來越明顯:
  當專案中出現了一個負責主要功能的類別,而且這個類別還必須負責跟其他子系統做溝通時,針對每一項子系統的需求,主要類別就必須增加對應的方式,越多的方法就等同增加類別的介面複雜度。

建議 | 介面分割

  透過「功能的切分」及「介面的簡化」可以減少這類問題的發生,或是套用設計模式來重新規劃類別,也可以減少不必要的操作介面出現在類別中。

常用的其他原則

  這裡收納兩個常用的其他原則。

最少知識原則(LKP:Least Knowledge Principle)

  當實作一個類別,這個類別應該越少使用到其他類別提供的功能越好,也就是說當個類別能夠只依照本身的「知識」去完成功能,就相對減少與其它物件「知識」的依賴程度。
這樣有兩個好處:
  1. 減少此類別與其他類別的耦合度。
  2. 增加被其他專案共用的可能性。

少用繼承多用組合

  當子類別繼承一個「介面類別」後,新的子類別就要負責重新設計實作介面類別中所定義的方法,而且不該額外擴充介面,以符合上述多個設計原則的需求。
  然而讓子類別繼承舊有的實作類別,卻也是最容易的實現方法之一,在子類別內增加想要擴充的「功能方法」並加以實作,客戶端之後就能直接利用子類別物件進行新增功能的呼叫。
  例如,鬧鐘類別可利用繼承「時鐘類別」的方式,取得「時間功能」的實作,只要子類別加上定時提醒的功能,就能達成鬧鐘功能的目標,然而客戶端使用鬧鐘類別時,對於取得目前時間沒有需求,只是單純想設定鬧鐘時間而已。
建議 | 宣告類別
在鬧鐘的類別定義中,宣告一個型別為時鐘類型的「類別成員」,那麼就可以減少不必要的方法出現在鬧鐘介面上,也可以減少鬧鐘類別的客戶端對時中類別的相依性。

後記

  這篇文章是我之前的一個筆記,關於物件導向五大基本原則,了解這些後對於程式設計想當有幫助。

瓶裝雪

參考資料

為什麼會看到廣告
avatar-img
106會員
247內容數
對設計師如何成長為設計師好奇嗎? 2020年九月,我進入大學學習當一位設計師,從開始到沉寂,再到重燃熱忱,我將在方格子紀錄我的成長歷程、理念、心情,分享我在這段旅程中所經歷的故事。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
瓶裝雪的沙龍 的其他內容
這篇文章將會講述子彈時間、暫停時間相關的時間規模。
這篇文章將會講述 Unity Event 的介紹及基礎使用方法。
這篇文章將會講述 static 的小技巧,讓常用的欄位不用重複指定或拖拉。
這篇文章將會講述使用 C# 的類( Class ) 來讓欄位模組(module)化。
這篇文章將會藉由企劃的角度去看企劃、程式、美術三者的合作方式,以及如何有效率的開發一款遊戲專案。
前言   這篇文章將會講述碰撞事件的撰寫,跟碰撞事件有關的函式共有六種,分別為 OnCollisionEnter/Stay/Exit 和 OnTriggerEnter / Stay /Exit ,本篇文章將會講述通用的邏輯。 Collider | 碰撞體   在之前我從組件(Component)和程
這篇文章將會講述子彈時間、暫停時間相關的時間規模。
這篇文章將會講述 Unity Event 的介紹及基礎使用方法。
這篇文章將會講述 static 的小技巧,讓常用的欄位不用重複指定或拖拉。
這篇文章將會講述使用 C# 的類( Class ) 來讓欄位模組(module)化。
這篇文章將會藉由企劃的角度去看企劃、程式、美術三者的合作方式,以及如何有效率的開發一款遊戲專案。
前言   這篇文章將會講述碰撞事件的撰寫,跟碰撞事件有關的函式共有六種,分別為 OnCollisionEnter/Stay/Exit 和 OnTriggerEnter / Stay /Exit ,本篇文章將會講述通用的邏輯。 Collider | 碰撞體   在之前我從組件(Component)和程
你可能也想看
Google News 追蹤
在寫程式的過程中,你是否遇過一個類別或模組負責了太多事情,結果導致程式變得難以維護?這類情況經常被稱為「巨石類別 (God Class)」。當我們對這樣的類別做出任何變更時,改動可能會牽一髮動全身影響其他部分,這時候「單一職責原則 (SRP)」便派上用場。 單一職責原則是什麼? 簡單來說,單一
Thumbnail
這個章節主要介紹了Swift程式語言中物件導向程式設計的基本概念,包括類別、建構子、公開、私有、受保護等等的概念。同時,也介紹了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型和反射等進階特性。
Thumbnail
本章節是一個初級的 TypeScript 教學,主要介紹了 TypeScript 中物件導向程式設計的各種核心概念,包括類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等。每個概念都通過詳細的解釋和實例代碼來進行深入的介紹。
※ 單例模式介紹 ※ 定義:單例模式是一種設計模式,確保一個class(類)只有一個實例,並提供一個存取它的全域存取點。無論如何取值,皆只對這個實例取值。 ※ 目的:保證一個類別只會產生一個物件,而且提供存取該物件的統一方法。 ※ 講解:單例模式確保一個類無論怎麼 new 或 get,都只能拿
※ 設計模式的五大精神介紹(S.O.L.I.D): ※ 第一大精神 — S:單一職責原則(Single responsibility principle, SRP) ※ 定義: 每個物件,不管是類別或函數,都應該只負責一項功能。 當需求改變時,僅需改相關的區域,而不需要更動其他不相關的部分
※ OPP第三大核心-多型 ※ 多型的基本定義: 多型是利用繼承的特性,讓不同的子類別可以實現相同的介面,但在呼叫這些介面的方法時會表現出不同的行為。這使得程式設計更具彈性和擴展性,避免了複雜的條件判斷式,同時促進了代碼的重用。 class Animal { makeSound() {
Thumbnail
※ OPP第一大核心-封裝 封裝的精神在於將「方法」、「屬性」和「邏輯」包裝在類別裡面,透過類別的實例來實現。這樣外部物件不需要了解內部的實現細節,只需要知道如何使用該類別提供的接口即可。換句話說,封裝是將內部細節隱藏起來,只暴露必要的部分給使用者。 封裝的核心概念是,使用者如果想要接觸資料,只
Thumbnail
在物件導向程式設計的進階階段,學生將學習繼承、介面、抽象類別等核心概念。繼承允許類別共享屬性和方法,介面確保實現類別提供特定的方法實現,而抽象類別定義了基本結構供子類別擴展。這些知識點有助於提升程式碼的重用性、擴展性和維護性。
讓我在這篇文章總結一下前面對物件導向設計的討論,我們討論了物件導向的四個特性:繼承、抽象、多型、封裝,分析了它們的問題,並跟函數式編程的思維做比較。我們引入了與之相對應的特性:泛型、特性系統、模組化,有些特性雖然跟那四個特性很像,但在一些細微的地方有不同的詮釋,使得整體思考方式很不一樣。 「繼
物件導向設計的一個重點就是封裝,這有很多層面上的意義,但基本上就是控制物件的成員變數和方法的存取權。物件導向的封裝還跟繼承機制有關,這使得有一些時候我們逼不得已必須把函式定義在類別上,這種做法使得物件的功能變得難以拆解。封裝應該是模組的職責,並不需要再給物件相同的能力。 一般的模組系統就是把相
在寫程式的過程中,你是否遇過一個類別或模組負責了太多事情,結果導致程式變得難以維護?這類情況經常被稱為「巨石類別 (God Class)」。當我們對這樣的類別做出任何變更時,改動可能會牽一髮動全身影響其他部分,這時候「單一職責原則 (SRP)」便派上用場。 單一職責原則是什麼? 簡單來說,單一
Thumbnail
這個章節主要介紹了Swift程式語言中物件導向程式設計的基本概念,包括類別、建構子、公開、私有、受保護等等的概念。同時,也介紹了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型和反射等進階特性。
Thumbnail
本章節是一個初級的 TypeScript 教學,主要介紹了 TypeScript 中物件導向程式設計的各種核心概念,包括類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等。每個概念都通過詳細的解釋和實例代碼來進行深入的介紹。
※ 單例模式介紹 ※ 定義:單例模式是一種設計模式,確保一個class(類)只有一個實例,並提供一個存取它的全域存取點。無論如何取值,皆只對這個實例取值。 ※ 目的:保證一個類別只會產生一個物件,而且提供存取該物件的統一方法。 ※ 講解:單例模式確保一個類無論怎麼 new 或 get,都只能拿
※ 設計模式的五大精神介紹(S.O.L.I.D): ※ 第一大精神 — S:單一職責原則(Single responsibility principle, SRP) ※ 定義: 每個物件,不管是類別或函數,都應該只負責一項功能。 當需求改變時,僅需改相關的區域,而不需要更動其他不相關的部分
※ OPP第三大核心-多型 ※ 多型的基本定義: 多型是利用繼承的特性,讓不同的子類別可以實現相同的介面,但在呼叫這些介面的方法時會表現出不同的行為。這使得程式設計更具彈性和擴展性,避免了複雜的條件判斷式,同時促進了代碼的重用。 class Animal { makeSound() {
Thumbnail
※ OPP第一大核心-封裝 封裝的精神在於將「方法」、「屬性」和「邏輯」包裝在類別裡面,透過類別的實例來實現。這樣外部物件不需要了解內部的實現細節,只需要知道如何使用該類別提供的接口即可。換句話說,封裝是將內部細節隱藏起來,只暴露必要的部分給使用者。 封裝的核心概念是,使用者如果想要接觸資料,只
Thumbnail
在物件導向程式設計的進階階段,學生將學習繼承、介面、抽象類別等核心概念。繼承允許類別共享屬性和方法,介面確保實現類別提供特定的方法實現,而抽象類別定義了基本結構供子類別擴展。這些知識點有助於提升程式碼的重用性、擴展性和維護性。
讓我在這篇文章總結一下前面對物件導向設計的討論,我們討論了物件導向的四個特性:繼承、抽象、多型、封裝,分析了它們的問題,並跟函數式編程的思維做比較。我們引入了與之相對應的特性:泛型、特性系統、模組化,有些特性雖然跟那四個特性很像,但在一些細微的地方有不同的詮釋,使得整體思考方式很不一樣。 「繼
物件導向設計的一個重點就是封裝,這有很多層面上的意義,但基本上就是控制物件的成員變數和方法的存取權。物件導向的封裝還跟繼承機制有關,這使得有一些時候我們逼不得已必須把函式定義在類別上,這種做法使得物件的功能變得難以拆解。封裝應該是模組的職責,並不需要再給物件相同的能力。 一般的模組系統就是把相