在上一章我們提到了一個基於類別的物件導向語言,物件之間通過訊息傳送來呼叫彼此的行為,物件導向系統的基礎是訊息(message ),但最明顯的組織結構則是類別(class ),每個類別定義了方法和屬性,方法在收到訊息時被呼叫。
因此,最小的目標即是使用類別來建構應用程式的模型,使它逹成目前希望它達成的事情並且將來也能夠輕易地進行修改。像Ruby這種基於類別的OO語言,方法是定義在類別裡,因次我們會需要決定類別的內容,並將方法分組成類別。
透明性(Transparent):
程式碼的修改顯而易見,無論是自己的部分或是遠處依賴於它的程式碼
合理性(Reasonable):
任何修改的成本與效益成正比
可用性(Usable):
現存的程式碼在新的或是預期外的上下文中應保持可用
典範性(Exemplary):
程式碼本身應鼓勵那些為延續自身特點而進行的修改
具有 TRUE 特點的程式碼,符合易於修改的條件而能夠滿足未來的需求,要建立TRUE程式碼首先是要能夠確保每個類別都具有單一且明確定義的職責。
單一職責(Single Responsibility Principle, SRP)
A class should have only one reason to change.
一個類別應該盡可能作為最小且有用的事物,符合單一職責精神。
(*本章及接下來章節皆會以書中舉的自行車例子來做說明與討論)
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
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(修改某個方法所要求參數的個數,會破壞先前任何呼叫該方法的程式。)
然而,多數未經思考的修改常常會產生以下狀況:
判斷一個類別是否包含屬於其他地方的行為,通過問題詢問、用一句話描述、內聚性和單一職責原則來判斷類別是否具有適當的職責。當你如果需要使用「和」或「或」來描述該類別,可能表示它具有多種職責。我們必須確保類別的功能明確,並保持其內聚性,有助於設計更清晰、易於理解和可維護的程式碼。
今天先理解到類別與方法的關係,以及類別的單一職責是什麼就好,明天我們再來看看書上是如何調整修改,在單一職責的大前提下讓自行車可以運行。
參考資料: