517,六大設(shè)計原則之單一職責原則(面試點:應(yīng)該有且僅有一個原因引起類的變更)

單一職責原則

應(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)此功能的:

image.png

我們假設(shè):
如果一個用戶的屬性發(fā)生改變(id,密碼,名字),或者添加,刪除用戶都會導(dǎo)致類的改變,也就是說此類沒有把用戶的屬性和用戶的行為分開,導(dǎo)致了在有用戶的屬性和用戶的行為變化時,UserInfo類也會改變。這就違反了我們的單一職責原則(應(yīng)該有且僅有一個原因引起類的變更)。

我們按照把用戶信息重新抽象成二個接口,一個IUserBO接口負責處理用戶的屬性,一個IUserBiz接口負責處理用戶的行為,這樣用戶屬性改變,只會導(dǎo)致IUserBO接口改變,用戶的行為改變,只會導(dǎo)致IUserBiz接口改變,這樣也就更符合單一職責原則。

修改后的類圖如下:

image.png

我們經(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)常使用這個符合單一職責原則的類圖:

image.png

接口的單一職責原則

先看幾個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ù)用。

image.png

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

image.png

總結(jié)

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容