物件導向設計原則 - SOLID

閱讀時間約 7 分鐘
本筆記除了以文字說明SOLID設計原則以外,並以Java code實際舉例。

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

  • 每個人負責屬於自己的職責,不該承擔太多職責,大家各自做自己應該做的事情,且不會互相干擾。
以下舉例說明,假設有個Employee類別,calculatePay()用來計算薪資,是屬於會計部的需求,genHoursReport()用來產生工時報表,是屬於人資部的需求。
calculateWorkingHours() 用來計算工時,calculatePay()與genHoursReport() 方法都依賴這個方法,也就是無論是計算薪資或是產生工時報表,都需要先計算出工時。
假設會計部門要求要更改計算薪資的方式,需要動到計算工時的邏輯calculateWorkingHours(),由於產生工時報表的方法也會呼叫這個method來計算工時,因此這個修改有可能會導致工時報表錯誤的問題,也就是為了會計部門的需求更動,會影響到人資部門的需求功能。
因此問題就浮現了,Employee class做太多事情了,把其他人的職責也囊括進來這邊,會計部門的需求改變會影響到人資部門的功能,這就不符合SRP了。
因此,需要把不同的職責拆分到不同的類別,以免互相影響:
如此將會計的計算薪資與人資的工時報表拆開為獨自的類別(將不同的責任方法拆分到不同的類別),內部有各自的計算工時邏輯,互不影響。會計部門的計算工時邏輯更動,不會影響人資部門的功能了。

Open/Closed Principle (OCP) 開放封閉原則

  • 「對擴充開放,對修改封閉」: 需求變動時,可以對既有的程式碼進行擴展,而不須修改原有的程式碼。
續上程式碼,修改如下:
要根據不同角色計算薪資,這樣的程式碼,每當有一個新的role就要去改switch的code,增加一個case與對應的method,來計算對應的薪資,如此就違反OCP原則了,要擴展不應該要去改原本的程式碼
因此為了符合OCP原則,我們修改一下code:
如此修改,日後若要擴充新的Role,只需新增一個XXXRole class,並且定義calcSalary()方法,將薪資計算邏輯寫在這,就完成了。擴充功能完全不需要動到原本的code,即符合OCP原則。

Liskov Substitution Principle (LSP) 里氏替換原則

  • 當子類別替換掉父類別時,其功能不受影響。
  • 子類別繼承父類別可以擴充新的功能,不應覆寫原本父類定義的功能,若要覆寫就必須符合原來預期的行為。
這邊修改一下剛剛的role extends的code,CEORole繼承了Role且覆寫了setName() 方法,將原本父類別setName的行為改掉了,導致發生預期之外的錯誤,這就是違反LSP原則。
從輸出的結果來看,子類的行為改變(覆寫)了父類原本的行為,造成子類無法替代父類,即違反LSP原則。
Output:
Role Name: ABC
Role Name: CEO

Interface Segregation Principle (ISP) 介面隔離原則

  • 將龐大的介面拆分為更小且具體的介面,讓客戶只需實作他們需要的方法,不應有用不到的功能可以呼叫。
沿用上述code,修改成如下:
假設讀取報表(readReport)是祕書才會做的事,審核假單(verifyBreakLog)是主管才會做的事,對於主管(Director)來說,不需要readReport()這個方法,但卻被迫實作且可以呼叫不會使用的方法,這就違反了ISP原則。
為了符合ISP原則,將code改成如下,將AdminUsage拆的更小:
如此一來,秘書可以讀取報表,不需要去實作用不到的審核假單。
主管可以審核假單,不需要去實作用不到的讀取報表,也就符合了ISP原則。

Dependency Inversion Principle(DIP) 依賴反轉原則

  • 高階模組不應該依賴於低階模組,兩者都應該依賴抽象。
例如為了「讀取報表」功能,需要依賴Excel類別的readFile()來讀取檔案,如同下面的程式碼一樣。
這時候問題就來了,假設要改從Word去讀取檔案,而Word類別的方法名稱不是readFile(), 而是readContent(),高階模組readReport()這邊的程式碼,就會因為低階模組的改動,也需要跟著改程式,這就是所謂的高階模組依賴於低階模組:
為了符合DIP原則,修改如下:
現在變成高階模組跟低階模組都依賴IReader interface,假如今天要換成從PPT讀取呢? 這樣的程式碼擴充就很容易,對我來說只需要新增一個PPT class並且implements IReader 實作readContent() method,然後將PPT instance傳入高階模組建構子即可:
有沒有發現,這樣設計後高階模組的code完全不用改,也變相的統一了method name,都叫做readContent(),對於新的低階模組class而言只需把readContent()的邏輯寫好,高階模組會去call readContent(),完全不須改code。
也就是說,想要符合DIP原則,可以朝這樣的方向去思考: 「為了達成這個目的,我需要依賴什麼功能?」將這個功能設計成interface即可
以這個例子而已,為了達成讀取報表的目的,我需要依賴讀取資料的功能,因此把讀取資料功能拉出來設計成介面,讓高低階模組皆依賴於這個介面,即可符合DIP原則。
本筆記參考:
為什麼會看到廣告
21會員
161內容數
留言0
查看全部
發表第一個留言支持創作者!
Vic Lin的沙龍 的其他內容
data() method 續上篇,這邊修改一下HomeView.vue的code。 Before: After: 在頁面最下方顯示 name: Vic,data method直接回傳name變數,在<template>中可以直接用{{ name }}將變數顯示出來。 結果如下: v-model
Vue CLI與React的create-react-app是類似的工具,目的都是為了讓開發者快速建置環境,節省複雜的安裝相關環境的時間,簡單的一個建立專案指令與設定,即建立一個內建了Hot-Reload, webpack, ESLint等功能的專案。 安裝Vue CLI: 選擇檢查程式碼的時機
假設資料如下: local DB裡面的test Collection SELECT SELECT可以這樣寫: 由於config/database.php中設定的default DB_CONNECTION是mysql,所以這邊特別指定使用mongodb connection。 回傳結果如下: 軟刪除
欲在Laravel中使用MongoDB,首先要確認有安裝MongoDB PHP driver,接著再安裝Laravel MongoDB套件,才能開始使用CRUD。 1. 安裝MongoDB PHP driver (1) 到這邊下載MongoDB PHP driver (3) 檢查是否正常 後記:
MongoDB 簡介 MongoDB是一種開源的NoSQL文件資料庫(Document Database),MongoDB中可以有多個Database,每個Database中可以有多個Collection,每個Collection中可以有多個Document。 Windows 安裝 MongoDB
在Laravel中想達到websocket效果,由後端主動傳訊給前端,需使用broadcasting 將event廣播出去,由前端來接收訊息。 因此在業界常看到使用Redis + socket.io的架構,也是本篇選擇的機制。 從伺服器廣播訊息到前端接收的流程,大概會是這樣: 安裝與設定Redis
data() method 續上篇,這邊修改一下HomeView.vue的code。 Before: After: 在頁面最下方顯示 name: Vic,data method直接回傳name變數,在<template>中可以直接用{{ name }}將變數顯示出來。 結果如下: v-model
Vue CLI與React的create-react-app是類似的工具,目的都是為了讓開發者快速建置環境,節省複雜的安裝相關環境的時間,簡單的一個建立專案指令與設定,即建立一個內建了Hot-Reload, webpack, ESLint等功能的專案。 安裝Vue CLI: 選擇檢查程式碼的時機
假設資料如下: local DB裡面的test Collection SELECT SELECT可以這樣寫: 由於config/database.php中設定的default DB_CONNECTION是mysql,所以這邊特別指定使用mongodb connection。 回傳結果如下: 軟刪除
欲在Laravel中使用MongoDB,首先要確認有安裝MongoDB PHP driver,接著再安裝Laravel MongoDB套件,才能開始使用CRUD。 1. 安裝MongoDB PHP driver (1) 到這邊下載MongoDB PHP driver (3) 檢查是否正常 後記:
MongoDB 簡介 MongoDB是一種開源的NoSQL文件資料庫(Document Database),MongoDB中可以有多個Database,每個Database中可以有多個Collection,每個Collection中可以有多個Document。 Windows 安裝 MongoDB
在Laravel中想達到websocket效果,由後端主動傳訊給前端,需使用broadcasting 將event廣播出去,由前端來接收訊息。 因此在業界常看到使用Redis + socket.io的架構,也是本篇選擇的機制。 從伺服器廣播訊息到前端接收的流程,大概會是這樣: 安裝與設定Redis
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
※ OPP第三大核心-多型 ※ 多型的基本定義: 多型是利用繼承的特性,讓不同的子類別可以實現相同的介面,但在呼叫這些介面的方法時會表現出不同的行為。這使得程式設計更具彈性和擴展性,避免了複雜的條件判斷式,同時促進了代碼的重用。 class Animal { makeSound() {
Thumbnail
※ OPP第一大核心-封裝 封裝的精神在於將「方法」、「屬性」和「邏輯」包裝在類別裡面,透過類別的實例來實現。這樣外部物件不需要了解內部的實現細節,只需要知道如何使用該類別提供的接口即可。換句話說,封裝是將內部細節隱藏起來,只暴露必要的部分給使用者。 封裝的核心概念是,使用者如果想要接觸資料,只
※ class類別 什麼是class? class是創造consturctor function時的語法糖,本質上與使用function創造物件(object)的行為沒有不同。 class的作用: 用來定義、描述要創造的物件(object)具有那些屬性、行為的一個表達式。就像是「車子的設計圖
※ ES6 變數宣告介紹: 在ES6中,推薦使用let和const取代原有的var來宣告變數。 ※ var的特點: 勢力範圍(scope)只有兩種:function、global(全域部分)。 勢力範圍(scope)指的是宣告變數的範圍,能夠被有效的使用的範圍。 可以在宣告變數之前就使用。
※ Object(物件) & Constructor Function(建構式函式) Object(物件)是什麼? 物件是一種「可以將資料、程式碼包含在其中」的資料結構。 Object(物件)的兩種創造方式: 匿名物件:直接使用"{}"。沒有特別的名字,直接從Object中繼承過來的一個物件
Thumbnail
到目前為止,我們所學習的都是程序性的程式設計(procedural programming),也就是程式碼是透過一連串的指令組成的程序或函數,由上而下依序執行不同的程序或是呼叫函數來完成程式的功能。 Python其實是一種物件導向的程式(object oriented programming, 簡稱
Thumbnail
大家在極簡生活的過程當中,一定會愈來愈清楚自己「購買物品的原則」,例如衣服必須要舒服、適合場合夠多(要能上班、爬山等等)、有彈性、純棉質、快乾吸汗等,同樣的,觀察自己的飲食生活,也會逐漸讓我們發現自己的飲食原則! 在練習少食生活之後,我們也愈來愈清楚適合自己的飲食生活,於是整理了以下我們設計自己菜
Thumbnail
​來聊一下令人開心的事情! 小時候總覺得設計鍛胚、成形工法、鍛造模具的人很厲害,可以鍛造做出客戶需要的東西…實際上確實也非常厲害。 長大後發現其實只要符合客戶需求,其實怎麼做都可以,就算是純手工也可以。 常態下,當收到客戶委託需要以鍛造開發的成品,通常第一步會先評估廠內設備是否可以生產,最重要的依據
Thumbnail
物件導向的概念,以python程式為範例。
Thumbnail
圖書館借的一本書,原本只是打算找Unity相關的書,但找不到的前提下隨意挑了一本書。 基本資料 書名: 作者:鄭仲平,高煥堂 出版日期:2019/01/31
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
※ OPP第三大核心-多型 ※ 多型的基本定義: 多型是利用繼承的特性,讓不同的子類別可以實現相同的介面,但在呼叫這些介面的方法時會表現出不同的行為。這使得程式設計更具彈性和擴展性,避免了複雜的條件判斷式,同時促進了代碼的重用。 class Animal { makeSound() {
Thumbnail
※ OPP第一大核心-封裝 封裝的精神在於將「方法」、「屬性」和「邏輯」包裝在類別裡面,透過類別的實例來實現。這樣外部物件不需要了解內部的實現細節,只需要知道如何使用該類別提供的接口即可。換句話說,封裝是將內部細節隱藏起來,只暴露必要的部分給使用者。 封裝的核心概念是,使用者如果想要接觸資料,只
※ class類別 什麼是class? class是創造consturctor function時的語法糖,本質上與使用function創造物件(object)的行為沒有不同。 class的作用: 用來定義、描述要創造的物件(object)具有那些屬性、行為的一個表達式。就像是「車子的設計圖
※ ES6 變數宣告介紹: 在ES6中,推薦使用let和const取代原有的var來宣告變數。 ※ var的特點: 勢力範圍(scope)只有兩種:function、global(全域部分)。 勢力範圍(scope)指的是宣告變數的範圍,能夠被有效的使用的範圍。 可以在宣告變數之前就使用。
※ Object(物件) & Constructor Function(建構式函式) Object(物件)是什麼? 物件是一種「可以將資料、程式碼包含在其中」的資料結構。 Object(物件)的兩種創造方式: 匿名物件:直接使用"{}"。沒有特別的名字,直接從Object中繼承過來的一個物件
Thumbnail
到目前為止,我們所學習的都是程序性的程式設計(procedural programming),也就是程式碼是透過一連串的指令組成的程序或函數,由上而下依序執行不同的程序或是呼叫函數來完成程式的功能。 Python其實是一種物件導向的程式(object oriented programming, 簡稱
Thumbnail
大家在極簡生活的過程當中,一定會愈來愈清楚自己「購買物品的原則」,例如衣服必須要舒服、適合場合夠多(要能上班、爬山等等)、有彈性、純棉質、快乾吸汗等,同樣的,觀察自己的飲食生活,也會逐漸讓我們發現自己的飲食原則! 在練習少食生活之後,我們也愈來愈清楚適合自己的飲食生活,於是整理了以下我們設計自己菜
Thumbnail
​來聊一下令人開心的事情! 小時候總覺得設計鍛胚、成形工法、鍛造模具的人很厲害,可以鍛造做出客戶需要的東西…實際上確實也非常厲害。 長大後發現其實只要符合客戶需求,其實怎麼做都可以,就算是純手工也可以。 常態下,當收到客戶委託需要以鍛造開發的成品,通常第一步會先評估廠內設備是否可以生產,最重要的依據
Thumbnail
物件導向的概念,以python程式為範例。
Thumbnail
圖書館借的一本書,原本只是打算找Unity相關的書,但找不到的前提下隨意挑了一本書。 基本資料 書名: 作者:鄭仲平,高煥堂 出版日期:2019/01/31