iT邦幫忙

2023 iThome 鐵人賽

DAY 4
0
自我挑戰組

入坑 RoR 必讀 - Ruby 物件導向設計實踐系列 第 4

Day4 CH2 設計具有單一職責的類別(上)

  • 分享至 

  • xImage
  •  

在上一章我們提到了一個基於類別的物件導向語言,物件之間通過訊息傳送來呼叫彼此的行為,物件導向系統的基礎是訊息(message ),但最明顯的組織結構則是類別(class ),每個類別定義了方法屬性,方法在收到訊息時被呼叫。

因此,最小的目標即是使用類別來建構應用程式的模型,使它逹成目前希望它達成的事情並且將來也能夠輕易地進行修改。像Ruby這種基於類別的OO語言,方法是定義在類別裡,因次我們會需要決定類別的內容,並將方法分組成類別。

程式碼組織化

定義「易於修改」的程式碼

  • 修改時不會產生意料外的副作用
  • 小需求的改變也只需要小幅度修改程式碼
  • 現存的程式碼容易被重複使用
  • 最簡單的修改方法是增加自身也易於修改的程式碼

有品質的程式碼應具備的特點

  • 透明性(Transparent)
    程式碼的修改顯而易見,無論是自己的部分或是遠處依賴於它的程式碼

  • 合理性(Reasonable)
    任何修改的成本與效益成正比

  • 可用性(Usable)
    現存的程式碼在新的或是預期外的上下文中應保持可用

  • 典範性(Exemplary)
    程式碼本身應鼓勵那些為延續自身特點而進行的修改

具有 TRUE 特點的程式碼,符合易於修改的條件而能夠滿足未來的需求,要建立TRUE程式碼首先是要能夠確保每個類別都具有單一且明確定義的職責

建立具有單一職責的類別

定義

單一職責(Single Responsibility Principle, SRP)

A class should have only one reason to change.

一個類別應該盡可能作為最小且有用的事物,符合單一職責精神。

實作

(*本章及接下來章節皆會以書中舉的自行車例子來做說明與討論)

  1. 先定義自行車的 Gear 類別,實作了三種方法:chainring、cog 和 ratio。
class Gear
attr_reader :chainring, :cog 

  def initialize(chainring, cog)
    @chainring = chainring
    @cog = cog
  end

  def ratio
   chainring / cog.to_f
  end
end

puts Gear.new(52, 11).ratio # -> 4.72727272727273 
puts Gear.new(30, 27).ratio # -> 1.11111111111111
  1. 接著,新的需求出現,我們要用「齒輪英寸數」來測量不同齒輪的自行車差異,修改 Gear 類別,增加新的行為:
    (註:齒輪英寸數 = 輪子直徑 X 齒輪比率,其中,輪子直徑 = 鋼圈直徑 + 兩倍的輪胎直徑。 )
class Gear
  attr_reader :chainring, :cog, :rim, :tire
  def initialize(chainring, cog, rim, tire)
    @chainring = chainring 
    @cog = cog
    @rim = rim
    @tire = tire
	end

	def ratio
    chainring / cog.to_f
  end

  def gear_inches
   # 鋼圈加I:圍繞的輪胎即為輪子直徑
   ratio * (rim + (tire * 2))
  end

end

puts Gear.new(52, 11, 26, 1.5).gear_inches
# -> 137.090909090909

puts Gear.new(52, 11, 24, 1.25).gear_inches
# -> 125.272727272727

這樣的修改的確達成了我們的目的,不過會不會有什麼問題呢?(先思考一下,別急著往下哦!)

.

.

.

.

.

.

.

.

此時,我們為了符合需求而增加 gear_inches 方法時,會導致原本的程式碼使用錯誤:

puts Gear.new (52, 11).ratio # 這原本是可以運作的?
# ArgumentError: wrong number of arguments (2 for 4)

修改後的 Gear 在 initialize 時需要兩個額外的參數 :rim 和 :tire(修改某個方法所要求參數的個數,會破壞先前任何呼叫該方法的程式。)

然而,多數未經思考的修改常常會產生以下狀況:

  1. 多職責類別的問題:擁有多種職責的類別往往難以重複使用。不同職責可能相互糾纏在一起,使得無法只使用所需的部分行為。
  2. 處理多職責的選擇
    • 複製程式碼:如果職責之間緊密耦合,可能會導致複製程式碼。
    • 重複使用整個類別:導致混淆,因為該類別可能包含多種糾纏不清的職責。
  3. 修改引起的問題:修改這種類別的原因可能與如何使用它無關,而且可能導致所有依賴它的類別都受到影響。

判斷類別是否為單一職責

判斷一個類別是否包含屬於其他地方的行為,通過問題詢問、用一句話描述、內聚性和單一職責原則來判斷類別是否具有適當的職責。當你如果需要使用「和」或「或」來描述該類別,可能表示它具有多種職責。我們必須確保類別的功能明確,並保持其內聚性,有助於設計更清晰、易於理解和可維護的程式碼。

今天先理解到類別與方法的關係,以及類別的單一職責是什麼就好,明天我們再來看看書上是如何調整修改,在單一職責的大前提下讓自行車可以運行。

參考資料:


上一篇
Day3 CH1物件導向的設計?(下)
下一篇
Day5 CH2 設計具有單一職責的類別 (下)
系列文
入坑 RoR 必讀 - Ruby 物件導向設計實踐30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言