導演,先來個情境!
Ken: 哈囉! Mike跟Mike (別懷疑,同名同姓)
Mike: 嗨! Ken (x2)
Ken: 你們對於Ruby有什麼看法啊? 好玩嗎?
Mike: 我覺得..... (x2)
別懷疑,在上述的情境裡Mike是兩個人,而且同手同腳地走路甚至想法和呼吸頻率一切都一樣,根本就是同一人(但世界上不會有這種狀況)
所以我們把這種一模一樣的彼此兩人,定義成依賴關係100%,代表他們完全懂彼此的一切,沒有任何秘密。
那麼0%則是代表兩個人彼此不相識,而且在世界的兩端... 毫無關聯。
而每個人都是介於0% ~ 100%間的關係吧! 程式碼也是如此
當物件的關係上升時,代表耦合(Coupling)上升,但我們可以理解耦合是必然的,可怕的是如果過於嚴重時,就應該要重新設計兩者的依賴關係。(如果是我,我會砍掉一個Mike,要不然會煩死)
class Ken
attr_reader :location, :time, :weather, :content
def initialize(location, time, weather, content)
@location = location
@time = time
@weather = weather
@content = content
end
def chatting
res = Mike.new(content).talk
end
end
從上述程式碼可以看出幾個耦合的關係
接下來我會使用Sandi Metz著作中的例子來解說
(絕對不是我偷懶噢!是因為寫得太好了)
class Gear
attr_reader :chainring, :cog, :rim, :tire
def initialize(chainring, cog, rim, tire)
#若少任何一個值,都會導致程式出錯
@chainring = chainring
@cog = cog
@rim = rim
@tire = tire
end
def gear_inches
ratio * Wheel.new(rim, tire).diameter #gear_inches只限定給Wheel類別使用
end
def ratio
chainring / cog.to_f
end
end
class Wheel
attr_reader :rim, :tire
def initialize(rim, tire)
@rim = rim
@tire = tire
end
def diameter
rim + (tire * 2)
end
end
Gear.new(52, 11, 26, 1.5).gear_inches #需要知道參數輸入順序
class Gear
attr_reader :chainring, :cog, :wheel
def initialize(args)
#使用||提供預設值
@chainring = args[:chainring] || 40
@cog = args[:cog] || 18
@wheel = args[:wheel]
end
def gear_inches
#當wheel變成參數時,只要任何有diameter方法的類別,都可以執行gear_inches
#而不會限定在Wheel類別中
#意味著Gear不用知道誰執行diameter只要會執行diameter就好(鴨子型別)
ratio * wheel.diameter
end
def ratio
chainring / cog.to_f
end
end
class Wheel
attr_reader :rim, :tire
def initialize(rim, tire)
@rim = rim
@tire = tire
end
def diameter
rim + (tire * 2)
end
end
#使用args可以避免知道順序,而且增加可讀性
Gear.new(
cog: 11,
chainring: 52,
wheel: Wheel.new(26, 1.5)).gear_inches
以上的重構,就已經解釋完上面前三點囉! 再來先解釋何謂隔離依賴~
隔離依賴的概念其實很簡單,就是在讓這個類別依賴內容直接顯露。
就像是把依賴Wheel.new(rim, tire)
時,在initialize
就產生實體,這樣讓依賴內容顯而易見!
或者是額外封裝依賴行為,在往後找code時就不需要在茫茫程式碼中尋找,而是直接找到方法就好囉~
比較困難的就是依賴反轉,因為需要先理解何謂抽象
其實當我們使用鴨子型別時,就開始在定義抽象介面
試想看看,Gear告訴我們你可以在wheel這個參數上,幫他放上任何一個擁有diameter
方法的類別喔!
這就代表這是一個非限定類別的參數,而傳入著個參數的地方,就是抽象介面
抽象介面其中一個好處就是彈性大增,因為你不用只為其中一個類別做事,而是很多類別都可以帶進來執行!
所以與其依賴一個非抽象介面(限定參數),依賴抽象介面應該來的更安全且更方便。
好啦~ 可以洗洗睡囉~ 明天我們再來聊聊怎麼設計介面吧!
感謝大家 如有問題,再煩請大家指教!
依賴反轉:這就代表這是一個非限定類別的參數,而傳入著個參數的地方,就是抽象介面。
這部分是在說,後來的 Gear.new 因為可以把其他經過隔離依賴的類別當成參數帶進去,所以這個 Gear 類別就形成了其他類別的抽象介面?