單一職責原則
應該有且僅有一個原因引起類的變更。
單一職責原則好處
降低類的復雜性
每個類實現(xiàn)單一職責液兽,并且單一職責都有清楚明確的定義奖蔓,復雜性當然降低。提高可讀性
類的復雜性降低了千劈,當然提高了可讀性了。提高可維護性
類的復雜性降低牌捷,可讀性好墙牌,當然好維護。提高擴展性
變更引起的風險降低暗甥,變更是必不可少的喜滨,如果接口的單一職責做的好,一個接口修改只對相應的實現(xiàn)類有影響撤防,對其它的接口沒有影響虽风,這對系統(tǒng)的擴展性,維護性都是有好處的寄月。
類的單一職責原則
一般一個對象可以分為屬性和行為二部分辜膝,所以在類的設計時,我們一般把對象的屬性抽象成一個BO(Business Object,業(yè)務對象)漾肮,把對象的行為抽象成一個Biz(Business Logic厂抖,業(yè)務邏輯)。
我們經(jīng)常會管理一個系統(tǒng)的用戶信息克懊,比如修改一個用戶的信息(id,密碼忱辅,名字),添加一個用戶信息保檐,刪除一個用戶信息耕蝉,對用戶進行處理,對用戶添加組織和角色夜只。下面有一個類圖垒在,就是實現(xiàn)此功能的:
我們假設:
如果一個用戶的屬性發(fā)生改變(id,密碼扔亥,名字),或者添加场躯,刪除用戶都會導致類的改變,也就是說此類沒有把用戶的屬性和用戶的行為分開旅挤,導致了在有用戶的屬性和用戶的行為變化時踢关,UserInfo類也會改變。這就違反了我們的單一職責原則(應該有且僅有一個原因引起類的變更)粘茄。
我們按照把用戶信息重新抽象成二個接口签舞,一個IUserBO接口負責處理用戶的屬性秕脓,一個IUserBiz接口負責處理用戶的行為,這樣用戶屬性改變儒搭,只會導致IUserBO接口改變吠架,用戶的行為改變,只會導致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ān)聽
void onStopTrackingTouch(SeekBar seekBar);
}
從上面的三個例子擦酌,我們可以清楚的看到OnClickListener接口只針對點擊功能定義onClick方法俱诸,OnLongClickListener接口只針對長按功能定義onLongClick方法,OnSeekBarChangeListener 接口針對SeekBar控件進度條改變功能定義了一組相關的三個方法仑氛。
可見乙埃,優(yōu)秀接口的定義都是符合單一職責原則闸英,針對單一的職責定義單一的方法或是相關的一組方法
方法的單一職責原則
其實類也好锯岖,接口也好,最后歸根到底還是要方法來支持和實現(xiàn)甫何,所以方法的單一職責是非常關鍵重要的出吹。
方法的單一職責原則簡單來說就是一個方法實現(xiàn)一個功能,解決一個方法辙喂。
符合單一職責原則的方法捶牢,會更方便系統(tǒng)的調(diào)用,但是如果方法過細的拆分巍耗,也會導致方法的劇增和類或接口的復雜秋麸,因此在具體項目中還是把握一個度。
下面一個修改用戶信息的樣例:
一個是一個方法不符合單一職責原則炬太,changerUser方法承擔多個職責灸蟆,必然導致此方法復雜,產(chǎn)適合其它方法來復用亲族。
一個是每個方法都符合單一職責原則炒考,changeUserName實現(xiàn)修改名字,changeHomeAddress實現(xiàn)修改家庭地址霎迫,changeTele實現(xiàn)修改電話斋枢,每個方法都職責明確清楚,邏輯也清晰明了知给,非常適合其它方法來調(diào)用瓤帚。
總結
對于單一職責原則,我們的建議是接口一定要做到單一職責原則,類的設計盡量做到只有一個原因引起變化戈次。