Re: [概念] single responsibility principle

看板OOAD作者 (狗狗)時間14年前 (2010/02/25 20:35), 編輯推噓3(306)
留言9則, 4人參與, 最新討論串3/5 (看更多)
※ 引述《hsnucsc (hsnugo)》之銘言: : 我找了很多網站 : 都將解釋SRP成: 每一個物件, 應該要只有單一的responsibility : 而將responsibility解釋成: 只有一個理由去改變物件 : 但是我還是覺得responsibility這個詞很模糊 看了一下你的po的兩篇文 我想你對於OO的design pattern應該是不熟悉 我會強烈建議你先去念Design Pattern 自然而然就會知道這些原則是怎麼回事 以及知道什麼才是真正的OO programming 一般來說 要寫OO程式 初學者會直接硬幹 也就是列出會用到的物件類別之後 就開始拼命對class塞attr和method 然後遇到分歧處就開始subclassing 但是這樣的編寫程式只會造成後續的麻煩 尤其是在維護上(無論是擴充或是修改) 當你開發到一定規模的程式時 修改程式碼往往比開發新功能更花時間 而OO design pattern就是這個現象的救世主 在四人幫(GoF)的Design Pattern (1994)中 書皮就講明了"Elements of Reusable Object-Oriented Software" 它闡述了一個OO的程式碼 該如何撰寫才能讓程式具有彈性 在不同的情況下 大都有適合的pattern可以使用 而當你使用了這些pattern 將會對於你未來對於這些程式碼的維護或再利用有很大幫助 你看的Head First OOA&D 事實上 它是再開始撰寫程式碼之前的動作 先明確的確定該如何撰寫 再去實作 簡單的說 就是「謀定而後動」 較好的寫OO流程是 1. 先做 OO analysis & design 2. 再用 OO design pattern決定該如何實作 3. 最後 OO programming撰寫程式碼 如果你不懂為什麼要先做OOA&D 是因為你不知道中間有design pattern的步驟 或是無法體會design pattern所能帶來難以想像的幫助 正常情況下 OOA&D可以幫助你了解各個物件類別 該套用到哪些design pattern 以及整個程式完整的架構(如果你有好好做UML圖的話) 如果要學完整的OO programming 則在學完基本OO特性、如何使用物件及如何撰寫程式碼之後 會先完整學完OO design pattern(OODP) 最後才去學OOA&D 關於Design Pattern的學習 我會建議你先去看「大話設計模式」(程杰 著) 它比較適合新手 同時搭配http://www.oodesign.com/ 這個網站 先確定把這些所有提到的DP都弄懂精髓所在 以及所有OO原則 之後再去看Head First Design Pattern才會更了解這本書在說什麼 當你徹底的了解OO原則的精髓 就能自然而然的使用design pattern了 所以當你再去學OOA&D 就能夠對於新的專案開發能快速切入要點 最後 關於你問的SRP原則 事實上是OO設計原則的一部分 它的前提順序是這樣的: 1. 當你程式需要改變時 你應該是增加新的程式碼 而不是修改舊的 (OCP) =>簡單的說 已經寫好的程式(或說是封裝好的) 則不應該再去更動原有類別 更改已經寫的程式碼通常不會有什麼好下場 往往只會弄得一團亂 當你要修改一個類別時 你就是在破壞它原有的封裝 這是不好的現象 而這部份可以藉由多型來完成 2. 情非得已下 必須修改現有的程式碼時 則你應該只需修改一個類別(或是最少類別) =>簡單的說 如果你只需要修改一個類別 就比較能夠追蹤修改後的影響 這部份可以藉由封裝「因為一種理由而產生變化」的程式碼到一個類別來完成 3. 程式碼中絕對不能含有相同的演算法 你必須把相同的部份抽取出來 才能達到2的原則 =>簡單的說 就是因為你要改這個演算法時 你不用再去一個一個類別去改 這樣就不會遺漏哪些部分忘了修改 而造成debug的大災難 這部份會因為不同的狀況 有適合的design pattern可以使用 4. 當你要修改一個類別時 你應該只會因為一種「原因」而去改它 (SRP) =>簡單的說 當你要「拆開(或說是修改)」一個已經封裝的類別時 你應該只會修改跟這個原因相關的程式碼 其他的就不會動到 但是當你封裝在一起時 你就有可能會不小心動到 (哪怕是你上個廁所時 被你家的貓踩鍵盤) 因為你不會去花時間檢查你預期完全沒有修改到的部份 所以一但同時被「開放」修改時 你是在增加風險 (早叫你雞蛋不要放同一籃了) 而這部份 可藉由封裝不同原因才會修改的程式碼來達成 PS 事實上 這是要減少耦合 增加reuse的機會 5. 這是OO programming中最恐怖的單字:耦合(coupling) =>基本上 所有的design pattern和OO原則都是圍繞在這個單字 其實coupling的意義就是「把兩個東西綁在一起」(這年代講耦合誰聽得懂阿!) 耦合(或說「緊的耦合」)只發生在3個狀況下: 1) inherit 2) reference 3) encapsulate 「緊的耦合」應該發生在兩個程式碼意義是99.99%以上的關聯時 而任何間接方式就是「鬆的耦合」(例如多型) 我改用另一個例子來說明 如果你把「eat():」和「fly():」兩個方法都「封裝」在Bird的類別當中 你就是把這兩個method耦合在一起(這個耦合是藉由封裝) 但是這兩個method的意義是有99.99%以上的關聯嗎? 我想是沒有 因為會吃飯不見得就會飛 (不然我就不會坐在這裡長篇廢話) 當你下次要讓「狗」會吃飯時 或是讓「飛機」會飛時 你之前實作的程式碼就不能再利用 (當然例子不是舉的很好 因為飛機的飛法和鳥的飛法是不一樣的) 汽車也是 「引擎」的動力輸出 和「方向盤」的使用介面 並不互相耦合 這兩樣只是跟「汽車」耦合 (直升機也有引擎但是沒有方向盤 玩具車有方向盤但沒有引擎) 換句話說 把「引擎」的能力 和「方向盤」的功能都封裝在「汽車類別」裡 有違原則 哪天你想改車 來替你的愛車升級引擎時 你就要改原本汽車的程式碼啦! 但是藉由設計模式 你只需要將「汽車類別」去跟「引擎介面」耦合(不是引擎實體類別) 這樣的意思就是 汽車的確是有引擎 但是是哪顆引擎則可以有彈性的動態選擇 簡單的說 你就是藉由多型 而可以彈性的讓汽車擁有不同的引擎 但是如果你好不容易抽離出來成為多型的「引擎」程式碼 卻將「方向盤」的功能也封裝在「引擎」的介面底下(封裝就是耦合啦!) 那麼當使用者選擇某顆引擎的時候 他就被強制的安裝一個耦合在一起的方向盤(怒!) 反過來講 當使用者安裝方向盤的時候 也被強制換一顆引擎 更扯的是 當你想改裝你的方向盤時 你也可以看到引擎的設計圖 甚至可以偷改引擎 (因為你打開了已經封裝好的東西) SRP就是告訴你:「別在把風馬牛不相干的東西湊在一起坐撒尿牛丸啦!」 其他的原則我就不贅述了 基本上 用design pattern之後 你的程式碼有可能會拆得到處都是 然後在檢視或使用一個類別時 會搞不懂這個類別底層到底在幹嘛、要怎麼運行 但是事實上 你有做好documentation和UML圖的話 就會很容易使用 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 203.77.52.127 ※ 編輯: leondemon 來自: 203.77.52.127 (02/25 20:40) ※ 編輯: leondemon 來自: 203.77.52.127 (02/25 20:42) ※ 編輯: leondemon 來自: 203.77.52.127 (02/25 21:24)

02/25 23:52, , 1F
這篇文章超長的...未看完先推
02/25 23:52, 1F

02/26 09:06, , 2F
耦合 + 需求改變 = 超可怕 XD
02/26 09:06, 2F

02/27 13:55, , 3F
謝謝! 非常詳細且清楚的回答
02/27 13:55, 3F

02/27 13:56, , 4F
因為我以為是先有OOanalysis, 經過了一陣子後
02/27 13:56, 4F

02/27 13:57, , 5F
才歸納出一些常用的pattern, 稱為design pattern
02/27 13:57, 5F

02/27 13:58, , 6F
所以覺得應該先從(源頭)開始學起, 沒想到analysis
02/27 13:58, 6F

02/27 13:58, , 7F
非常的抽象
02/27 13:58, 7F

02/28 11:15, , 8F
design pattern 顧名思義是 design 時才用的。
02/28 11:15, 8F

02/28 11:15, , 9F
當然上面的東西也還是有所謂的 pattern 存在。
02/28 11:15, 9F
文章代碼(AID): #1BXcwZDc (OOAD)
文章代碼(AID): #1BXcwZDc (OOAD)