單一職責原則
應(yīng)該有且僅有一個原因引起類的變更。
單一職責原則好處
降低類的復(fù)雜性
每個類實現(xiàn)單一職責,并且單一職責都有清楚明確的定義,復(fù)雜性當然降低。提高可讀性
類的復(fù)雜性降低了,當然提高了可讀性了。提高可維護性
類的復(fù)雜性降低,可讀性好,當然好維護。提高擴展性
變更引起的風險降低,變更是必不可少的,如果接口的單一職責做的好,一個接口修改只對相應(yīng)的實現(xiàn)類有影響,對其它的接口沒有影響,這對系統(tǒng)的擴展性,維護性都是有好處的。
類的單一職責原則
一般一個對象可以分為屬性和行為二部分,所以在類的設(shè)計時,我們一般把對象的屬性抽象成一個BO(Business Object,業(yè)務(wù)對象),把對象的行為抽象成一個Biz(Business Logic,業(yè)務(wù)邏輯)。
我們經(jīng)常會管理一個系統(tǒng)的用戶信息,比如修改一個用戶的信息(id,密碼,名字),添加一個用戶信息,刪除一個用戶信息,對用戶進行處理,對用戶添加組織和角色。下面有一個類圖,就是實現(xiàn)此功能的:

我們假設(shè):
如果一個用戶的屬性發(fā)生改變(id,密碼,名字),或者添加,刪除用戶都會導(dǎo)致類的改變,也就是說此類沒有把用戶的屬性和用戶的行為分開,導(dǎo)致了在有用戶的屬性和用戶的行為變化時,UserInfo類也會改變。這就違反了我們的單一職責原則(應(yīng)該有且僅有一個原因引起類的變更)。
我們按照把用戶信息重新抽象成二個接口,一個IUserBO接口負責處理用戶的屬性,一個IUserBiz接口負責處理用戶的行為,這樣用戶屬性改變,只會導(dǎo)致IUserBO接口改變,用戶的行為改變,只會導(dǎo)致IUserBiz接口改變,這樣也就更符合單一職責原則。
修改后的類圖如下:

我們經(jīng)常使用的代碼示例:
IUserInfo userInfo = new UserInfo();
//userInfo當作IUserBO來使用
IUserBO userBO = (IUserBO)userInfo;
userBO.setPassword("test");
//userInfo當作IUserBiz 來使用
IUserBiz userBiz = (IUserBiz)userInfo;
userBiz.deleteUser();
在實際項目中,我們經(jīng)常使用這個符合單一職責原則的類圖:

接口的單一職責原則
先看幾個android開發(fā)中我們經(jīng)常使用的接口:
- View的click監(jiān)聽接口OnClickListener:
在frameworks/base/core/java/android/view/View.java代碼中,接口OnClickListener定義
/**
* Interface definition for a callback to be invoked when a view is clicked.
*/
//View的click監(jiān)聽接口
public interface OnClickListener {
void onClick(View v);
}
- View的長按監(jiān)聽接口OnLongClickListener
在frameworks/base/core/java/android/view/View.java代碼中,接口OnLongClickListener 定義:
/**
* Interface definition for a callback to be invoked when a view has been clicked and held.
*/
//View的長按監(jiān)聽接口
public interface OnLongClickListener {
boolean onLongClick(View v);
}
SeekBar控件的改變監(jiān)聽接口
在frameworks/base/core/java/android/widget/SeekBar.java代碼中,接口OnSeekBarChangeListener 定義:
//SeekBar控件的改變監(jiān)聽接口
public interface OnSeekBarChangeListener {
//進度改變監(jiān)聽
void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser);
//開始拖動進度條時監(jiān)聽
void onStartTrackingTouch(SeekBar seekBar);
//拖動進度條結(jié)束時監(jiān)聽
void onStopTrackingTouch(SeekBar seekBar);
}
從上面的三個例子,我們可以清楚的看到OnClickListener接口只針對點擊功能定義onClick方法,OnLongClickListener接口只針對長按功能定義onLongClick方法,OnSeekBarChangeListener 接口針對SeekBar控件進度條改變功能定義了一組相關(guān)的三個方法。
可見,優(yōu)秀接口的定義都是符合單一職責原則,針對單一的職責定義單一的方法或是相關(guān)的一組方法
方法的單一職責原則
其實類也好,接口也好,最后歸根到底還是要方法來支持和實現(xiàn),所以方法的單一職責是非常關(guān)鍵重要的。
方法的單一職責原則簡單來說就是一個方法實現(xiàn)一個功能,解決一個方法。
符合單一職責原則的方法,會更方便系統(tǒng)的調(diào)用,但是如果方法過細的拆分,也會導(dǎo)致方法的劇增和類或接口的復(fù)雜,因此在具體項目中還是把握一個度。
下面一個修改用戶信息的樣例:
一個是一個方法不符合單一職責原則,changerUser方法承擔多個職責,必然導(dǎo)致此方法復(fù)雜,產(chǎn)適合其它方法來復(fù)用。

一個是每個方法都符合單一職責原則,changeUserName實現(xiàn)修改名字,changeHomeAddress實現(xiàn)修改家庭地址,changeTele實現(xiàn)修改電話,每個方法都職責明確清楚,邏輯也清晰明了,非常適合其它方法來調(diào)用。

總結(jié)
對于單一職責原則,我們的建議是接口一定要做到單一職責原則,類的設(shè)計盡量做到只有一個原因引起變化。