单一职责原理讲解coding
生活随笔
收集整理的這篇文章主要介紹了
单一职责原理讲解coding
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
單一職責原則是說一個類只有一個發生變更的原因,這個是在類層之上的,那在接口層次,方法層次,也要遵循單一原則,那我們現在結合一些例子來說明一下,那單一職責的好處呢,首先降低類的復雜性,提高可讀性,提高維護性,最重要的是變更的時候風險率降低,那我們現在結合例子一起來體會一下,單一職責原則,首先呢我們現在有一個類
這個類圖還是非常非常簡單的,其實是由Test創建這兩個類,這個Bird類現在已經不用了,那職責在類的層次上,還是比較清晰的,那剛剛說的是類的職責上,那我們再看一下在接口級別上,還是用我們的課程舉例,ICourse是一個接口
package com.learn.design.principle.singleresponsibility;/*** 鳥主要的移動方式我們來學一下* * @author Leon.Sun**/
public class Bird {/*** 主要是用翅膀飛* 這里面我們增加一個birdName* 鳥的名稱* * * @param birdName*/public void mainMoveMode(String birdName){/*** 這里要做一個判斷* 這個就非常符合我們的日常開發* 一個需求來說* 我們在這里做一個修改* 其實是最快的方式* 我們在實際開發中還要考慮開發的成本* 時間,進度* 完全遵守單一原則* 有的時候還是要看實際情況的* 但是我們有一顆按照原則寫代碼的心* 條件允許的情況下* 還是要大家來遵守這些設計原則* 那需求現在增加了鴕鳥* 那我們在原有的類加一個判斷* * */if("鴕鳥".equals(birdName)){/*** 如果是鴕鳥的話* 用腳走* 鴕鳥用腳走* * 這里面有bug了* System.out.println(birdName+"用翅膀飛");* 下一行又走到了這里* 這里就是一個典型的一個例子* 那我們在擴展的時候* 對于一些邊界* 我們這里增加了if* 但是下邊并沒有放到else里* 這種情況也比較常見* 正確的寫法應該是這樣的* 如果繼續擴展下來* 包括企鵝* 企鵝也是鳥* 企鵝也是用腳走* 那鴕鳥跟企鵝呢* 我們還可以細分一下* 那如果現在再傳來一些特殊的鳥類* 那我們的這個方法還要繼續擴展* 還記得我們說的* 單一職責原則其中有一個最重要的好處* 他就是變更時風險率降低* 那現在是不遵循單一原則的* 所以風險還是比較大* 當然我們看到的這個方法還比較簡單* 那實際的業務比這個復雜* 邊界比這個判斷要 更多一些* 那我們現在從類的形式來把這個拆分開* */System.out.println(birdName+"用腳走");}else{System.out.println(birdName+"用翅膀飛");}}
}
package com.learn.design.principle.singleresponsibility;/*** 我們 創建一個類FlyBird* * * @author Leon.Sun**/
public class FlyBird {public void mainMoveMode(String birdName){System.out.println(birdName+"用翅膀飛");}
}
package com.learn.design.principle.singleresponsibility;/*** 我們再創建一個類WalkBird* 走路的鳥* * * @author Leon.Sun**/
public class WalkBird {public void mainMoveMode(String birdName){System.out.println(birdName+"用腳走");}
}
package com.learn.design.principle.singleresponsibility;/*** 我們寫一個測試類* * * @author Leon.Sun**/
public class Test {public static void main(String[] args) {/*** new一個bird* 我們傳進去一個大雁* 大雁用翅膀飛* 我現在再傳一個鴕鳥* 那鴕鳥用翅膀飛* 就不對* 因為鴕鳥飛不起來* 那我們再看一下這個類* * 可以看到大雁沒有問題* 用翅膀飛* 那鴕鳥用腳走* 鴕鳥用翅膀飛* * */
// Bird bird = new Bird();
// bird.mainMoveMode("大雁");
// bird.mainMoveMode("鴕鳥");/*** 那我們飛的鳥和走的鳥都區分一下* 這塊還是比較簡單的* 我們應用層來判斷這個邏輯* 如果是大雁* 我們就用FlyBird* 如果是鴕鳥我們就用行走的Bird* 那這個就是類的單一原則的體現* 我們把一個類進行拆分* 這樣我們就使每個類的方法職責單一的* 比較簡單* 也不至于引入的時候出現新的問題* 那我們來看一下類圖* * */FlyBird flyBird = new FlyBird();flyBird.mainMoveMode("大雁");WalkBird walkBird = new WalkBird();walkBird.mainMoveMode("鴕鳥");}
}
課程類實現兩個接口,我們可以通過實現一個接口或者多個接口,來組合出這個實現類的一個實現,但是我也可以實現一個接口,也就是我們這個實現類實現什么職責呢,都是有清楚明確的定義,復雜性也是降低了,復雜性降低了可讀性也就提高了,可讀性提高了也就更容易維護了,可讀性也就提高了,同時變更引起的影響降低了,一個接口的更改只對相應的實現類有影響,與其他的接口無關,這一點對項目的幫助是非常大的,這個就是從接口級別上來講,單一職責,剛剛類,接口,我們再來看看單一職責
package com.learn.design.principle.singleresponsibility;/*** 這個是一個接口* 我們來看一下這個接口* 對于課程來說* 獲得課程的名字* 獲得課程的視頻* 那在接口的基礎上做單一原則的話* 也就是ICourse這個接口* 可不只有一個職責* 首先一個大的職責是獲得課程的信息* 比如名稱和視頻字節流* 另外一個職責是管理課程* 和課程內容無關* 例如學習課程* 那如果學習課程需要獲取name* 視頻字節流* 如果退了這個課程呢* 可能就獲取不了這個名字和字節流了* 因為這個課程已經被我退掉了* 那退會影響獲取內容的變化* 這兩個職責是互相影響的* 現在我們退了這個課程* 那獲取課程信息的時候* 我們這個實現就獲取不到* 就可以了* 那整體來看* 這個接口兩個職責* 獲取課程相關信息* 還有課程管理上的相關處理* 那我們就可以把這個接口拆成兩個接口* 一個是獲取課程信息的接口* 另外是課程管理的接口* 我們來嘗試一下* * * @author Leon.Sun**/
public interface ICourse {/*** 我是可以獲得課程的名稱的* * * @return*/String getCourseName();/*** 我還可以獲得一個視頻* 拿到一個字節流* * @return*/byte[] getCourseVideo();/*** 學習課程* */void studyCourse();/*** 退款* */void refundCourse();}
package com.learn.design.principle.singleresponsibility;public interface ICourseManager {/*** 然后把這個拿到這邊* */void studyCourse();void refundCourse();
}
package com.learn.design.principle.singleresponsibility;/*** 注意這個關鍵字是interface* * @author Leon.Sun**/
public interface ICourseContent {/*** 我們把這兩個拿到這里邊* * @return*/String getCourseName();byte[] getCourseVideo();
}
package com.learn.design.principle.singleresponsibility;/*** 我們現在來實現一個Course的實現類* 他來實現ICourseManager這個接口* 同時來實現ICourseContent這個接口* CourseImpl實現類實現這兩個接口* 我們來看一下類圖* * * @author Leon.Sun**/
public class CourseImpl implements ICourseManager,ICourseContent {@Overridepublic void studyCourse() {}@Overridepublic void refundCourse() {}@Overridepublic String getCourseName() {return null;}@Overridepublic byte[] getCourseVideo() {return new byte[0];}
}
package com.learn.design.principle.singleresponsibility;/*** 那我們簡單的總結一下* 類的單一職責原則和接口方法的單一職責是一樣的* 但是我們在實際的項目開發中* 我們在創建類的時候* 包括依賴組合聚合* 受很多因素的影響* 包括我們項目的規模* 還有項目的周期* 技術人員的水平* 還有對進度的把控* 這個都是一個平衡的因素* 那另外有一個考慮* 也就是我們在擴展的時候* 如果我們沒有面向接口的編程* 而又非常遵循單一職責原則* 可能引起類的一個爆炸* 類的數量會比較多* 所以我們在總結起來* 就是說在實際的開發中* 我們的接口和方法* 一定要做到單一職責* 這個其實還是蠻好的* 對于我們維護起來也會比較方便* 而且成本也非常低* * * @author Leon.Sun**/
public class Method {/*** 我們現在有一個方法叫updateUserInfo* 這里面可以更新用戶名稱* 還可以更新地址* 其實這個方法就是同時更新userName和地址* * * @param userName* @param address*/private void updateUserInfo(String userName,String address){/*** 這里是一個偽代碼* 大家認為是一個更新的過程就可以了* * */userName = "geely";address = "beijing";}/*** 如果我們還有一種寫法* 如果是一個可變長度的參數* 還有其他的properties* 其他的屬性* 那這個方法的職責就更多了* 不一定更新什么* 這里是一個可變長的參數* 可變長的參數肯定是放在方法最后一個位置* 聲明才可以* 這里面可能包括各種信息* 例如說他的體重* 那這個方法從命名上* 包括里面的實現上* 職責就不是單一的* * * @param userName* @param properties*/private void updateUserInfo(String userName,String... properties){userName = "geely";
// address = "beijing";}/*** 那更好的方式應該是這樣的* 大家可以理解他的一個變化* 只更新名字* 這個就叫做updateUsername* 需要更新name的話* 就調用這個方法* 這兩個方法的職責是非常的單一且清晰的* 那我們在寫方法的時候* 經常還會碰到這個場景* * * @param userName*/private void updateUsername(String userName){userName = "geely";}/*** 這個就叫做updateUserAddress* 我們把之前的那個方法拆分成兩個方法* 如果我們需要更新地址的話* 那就調用這個方法* * * @param address*/private void updateUserAddress(String address){address = "beijing";}/*** 后面多了一個布爾* 這個方法里面傳了一個布爾類型* 這就有意思了* 我們看一下* * * @param userName* @param address* @param bool*/private void updateUserInfo(String userName,String address,boolean bool){/*** 我們一般會這么寫* 也就是布爾類型要么true要么false* 其實這個方法的職責要么todo something1* 還有todo something2* 那這種情況我們就應該把這個方法拆開* 因為這個方法很明顯的就是兩個職責* 如果你傳進來的布爾你沒有使用* 如果使用的就拆開* 有布爾的存在這個方法就不會有單一的職責* 這樣開發起來簡單* 維護起來也容易* * */if(bool){//todo something1}else{//todo something2}userName = "geely";address = "beijing";}}
?
總結
以上是生活随笔為你收集整理的单一职责原理讲解coding的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 单一职责原理讲解
- 下一篇: 接口隔离原则原理讲解-coding