繼上篇的物件導向程式設計概念,近期在面試上有遇到一個很重要的相關觀念,
當時不知道為什麼,現在整理出來分享~
:有聽過 SOLID 嗎?
:SOLID 是什麼?
SOLID 是一組五個物件導向程式設計的基本原則,旨在幫助開發者創建可維護、可擴展且易於理解的程式碼。這些原則有助於確保軟體設計具有高內聚性和低耦合性,這對於長期維護和協作開發非常重要。
單一職責原則 (Single Responsibility Principle - SRP)
假設我們有一個名為 Employee
的類別,
表示公司的員工,我們將根據 SRP 創建兩個具有不同職責的類別。
class EmployeeInfo
attr_reader :name, :employee_id
def initialize(name, employee_id)
@name = name
@employee_id = employee_id
end
def display_info
"Name: #{@name}, Employee ID: #{@employee_id}"
end
end
class EmployeeSalary
attr_reader :employee, :salary
def initialize(employee, salary)
@employee = employee
@salary = salary
end
def calculate_bonus
@salary * 0.1 # 假設獎金是薪水的10%
end
end
以上兩個不同的類別,每個類別都具有不同的職責。
EmployeeInfo
負責管理員工的基本資訊(如姓名和員工ID)EmployeeSalary
負責計算員工的薪水和獎金。
attr_reader
是 Ruby 中一個用於自動生成讀取實例變數(instance variables)的方法的簡便方式。主要用途是使類別的實例變數能夠被外部程式碼讀取,而不需要額外編寫自訂的 getter 方法。attr_reader
通常用於創建只讀取(read-only)的實例變數,這意味著外部程式碼可以訪問變數的值,但不能修改它。
以下是attr_reader
的基本用法:
class MyClass
attr_reader :my_variable
def initialize(value)
@my_variable = value
end
end
在這個示例中,
attr_reader :my_variable
被用來定義一個名為my_variable
的實例變數,並自動生成了一個名為my_variable
的讀取方法(getter 方法)。這意味著我們可以在外部程式碼中訪問my_variable
的值,如下所示:
obj = MyClass.new(42)
puts obj.my_variable # 輸出: 42
需要注意的是,
attr_reader
只創建了讀取方法,不允許外部程式碼修改實例變數的值。如果你需要允許變數被修改,則可以使用attr_writer
或attr_accessor
,分別用於生成寫入方法(setter 方法)或同時生成讀取和寫入方法。
里氏替換原則 (Liskov Substitution Principle - LSP)
假設我們有一個基類別 Bird
,代表鳥類,並且有一個 fly
方法用來表示鳥類飛行的行為:
class Bird
def fly
"This bird can fly."
end
end
然後,我們建立了一個子類別 Penguin
,代表企鵝,但企鵝不能飛行,所以我們需要重寫 fly
方法:
class Penguin < Bird
def fly
"This penguin can't fly."
end
end
現在,我們可以使用里氏替換原則,確保子類別 Penguin
可以替換其基類別 Bird
,並且在不影響程式正確性的情況下重寫 fly
方法。
bird = Bird.new
penguin = Penguin.new
puts bird.fly # 輸出: "This bird can fly."
puts penguin.fly # 輸出: "This penguin can't fly."
雖然 Penguin
重寫了 fly
方法,但它仍然可以替換 Bird
並遵守里氏替換原則。這使得我們能夠根據需要擴展程式碼,同時保持對基類別的一致性使用,而不會破壞程式的正確性。
介面隔離原則 (Interface Segregation Principle - ISP)
假設我們正在建立一個文件系統應用程式,需要處理文件(File)和資料夾(Folder)。我們想確保這些元素都可以執行基本的操作,如開啟(open
)、關閉(close
)和刪除(delete
)。
我們可以創建兩個模組,分別是 Openable
和 Deletable
:
module Openable
def open
puts "Opening..."
end
end
module Deletable
def delete
puts "Deleting..."
end
end
接下來,我們創建文件(File)和資料夾(Folder)類別,並混入相應的模組:
class File
include Openable
include Deletable
def initialize(name)
@name = name
end
def name
@name
end
end
class Folder
include Deletable
def initialize(name)
@name = name
@contents = []
end
def add_item(item)
@contents << item
end
end
在以上例子中,File
和 Folder
類別分別混入了 Openable
和 Deletable
模組。這樣,File
可以執行開啟、關閉和刪除操作,而 Folder
只能執行刪除操作。 確保每個類別僅實現了他們需要的方法,而不包含不必要的方法,從而減少不必要的耦合。 如果未來需要添加新的操作,可以輕鬆擴展相應的模組,而不會影響其他部分的程式碼。
依賴反轉原則 (Dependency Inversion Principle - DIP)
假設我們有一個介面 Device
代表設備,並且我們希望能夠將不同類型的設備連接到應用程式。
設備可以執行 connect
操作。
# 設備介面
class Device
def connect
end
end
# 具體設備類別
class Keyboard < Device
def connect
puts "Keyboard connected."
end
end
class Mouse < Device
def connect
puts "Mouse connected."
end
end
我們定義了一個 Device
類別,並建立兩個具體的設備類別 Keyboard
和 Mouse
,分別覆蓋了 connect
方法以提供實際的連接功能。
這樣,當我們需要添加新的設備類別時,只需創建一個新的設備類別,覆蓋 connect
方法,透過這方式實現 DIP,因為高層模組(使用這些設備的程式碼)依賴於 Device
抽象介面,而不需要關心具體的設備類別。
SOLID 原則通常與設計模式和良好的軟體設計實踐一起使用,以實現高品質的軟體。
當設計和編寫程式碼時,考慮這些原則可以幫助避免常見的設計問題並提高代碼的質量。
但是,即便這樣簡短的講完這些內容,還是必須反覆去複習才會理解!
今天先分享至此,我們下篇待續~!
參考資料:
文章同步於個人部落格:Viiisit!(歡迎參觀 ୧ʕ•̀ᴥ•́ʔ୨)