單一職責(Simple Responsibility Pinciple,SRP)是指不要存在多於一個導致類變更的原因。假設我們有一個類負責兩個職責,一旦發生需求變更,修改其中一個職責的邏輯代碼,有可能導致另一個職責的功能發生故障。這樣一來,這個類就存在兩個導致類變更的原因。如何解決這個問題呢?將兩個職責用兩個類來實現,進行解耦。后期需求變更維護互不影響。這樣的設計,可以降低類的復雜度,提高類的可讀性,提高系統的可維護性,降低變更引起的風險。總體來說,就是一個類、接口或方法只負責一項職責。
接下來,我們來看代碼實例,還是用課程舉例,我們的課程有直播課和錄播課。直播課不能快進和快退,錄播課程可以任意地反復觀看,功能職責不一樣。還是先創建一個Course類:
public class Course {
public void study(String courseName){
if("直播課".equals(courseName)){
System.out.println(courseName + "不能快進");
}else{
System.out.println(courseName + "可以反復回看");
}
}
}
看調用代碼:
public static void main(String[] args) {
Course course = new Course();
course.study("直播課");
course.study("錄播課");
}
從上面的代碼來看,Course類承擔了兩種處理邏輯。假如現在要對課程進行加密,直播課程和錄播課程的加密邏輯不一樣,必須修改代碼。而修改代碼的邏輯勢必會相互影響,容易帶來不可控的風險。我們對職責進行解耦,來看代碼,分別創建兩個類:LiveCourse和ReplayCourse。
LiveCourse類的代碼如下:
public class LiveCourse {
public void study(String courseName){
System.out.println(courseName + "不能快進看");
}
}
ReplayCourse類的代碼如下:
public class ReplayCourse {
public void study(String courseName){
System.out.println(courseName + "可以反復回");
}
}
調用代碼如下:
public static void main(String[] args) {
LiveCourse liveCourse = new LiveCourse();
liveCourse.study("直播課");
ReplayCourse replayCourse = new ReplayCourse();
replayCourse.study("錄播課");
}
業務繼續發展,課程要做權限。沒有付費的學員可以獲取課程基本信息,已經付費的學員可以獲得視頻流,即學習權限。那么在控制課程層面上至少有兩個職責。我們可以把展示職責和管理職責分離開來,都實現同一個抽象依賴。設計一個頂層接口,創建ICourse接口:
public interface ICourse {
//獲得基本信息
String getCourseName();
//獲得視頻流
byte[] getCourseVideo();
//學習課程
void studyCourse();
//退款
void refundCourse();
}
我們可以把這個接口拆成兩個接口:ICourseInfo和ICourseManager。
ICourseInfo接口的代碼如下:
public interface ICourseInfo {
String getCourseName();
byte[] getCourseVideo();
}
ICourseManager接口的代碼如下:
public interface ICourseManager {
void studyCourse();
void refundCourse();
}
來看一下類圖,如下圖所示。

下面我們來看一下方法層面的單一職責設計。有時候我們會偷懶,把一個方法寫成下面這樣:
private void modifyUserInfo(String userName,String address){
userName = "Tom";
address = "Changsha";
}
還可能寫成這樣:
private void modifyUserInfo(String userName,String... fileds){
userName = "Tom";
// address = "Changsha";
}
private void modifyUserInfo(String userName,String address,boolean bool){
if(bool){
}else{
}
userName = "Tom";
address = "Changsha";
}
顯然,上面的modifyUserInfo()方法承擔了多個職責,既可以修改userName,也可以修改address,甚至更多,明顯不符合單一職責。我們做如下修改,把這個方法拆成兩個方法:
private void modifyUserName(String userName){
userName = "Tom";
}
private void modifyAddress(String address){
address = "Changsha";
}
修改之后,開發起來簡單,維護起來也容易。我們在實際開發中會有項目依賴、組合、聚合這些關系,還有項目的規模、周期、技術人員的水平、對進度的把控,很多類都不符合單一職責。但是,我們在編寫代碼的過程,盡可能地讓接口和方法保持單一職責,對項目后期的維護是有很大幫助的。
關注微信公眾號『 Tom彈架構 』回復“設計模式”可獲取完整源碼。
本文為“Tom彈架構”原創,轉載請注明出處。技術在於分享,我分享我快樂!
如果本文對您有幫助,歡迎關注和點贊;如果您有任何建議也可留言評論或私信,您的支持是我堅持創作的動力。關注微信公眾號『 Tom彈架構 』可獲取更多技術干貨!
其他設計原則
Tom彈架構:開閉原則(Open-Closed Principle,OCP)
Tom彈架構:依賴倒置原則(Dependence Inversion Principle,DIP)
Tom彈架構:接口隔離原則(Interface Segregation Principle, ISP)
Tom彈架構:迪米特原則(Law of Demeter LoD)
