設計原則之單一職責

1. 單一職責原則

基本介紹:一個類只負責一項職責,完成一個單一的功能。

錯誤的示範

package principle.singleresponsibility.error;

/**
 * 單一職責錯誤示範
 * @author huangfu
 */
public class SingleResponsibilityError {
    public void mobile (String vehicle){
        System.out.println(vehicle + "在公路上跑!");
    }
}
package principle.singleresponsibility.error;

/**
 * 單一職測錯誤的示範的測試
 * @author huangfu
 */
public class TestSingleResponsibilityError {

    public static void main(String[] args) {
        SingleResponsibilityError singleResponsibilityError = new SingleResponsibilityError();
        singleResponsibilityError.mobile("汽車");
        singleResponsibilityError.mobile("輪船");
        singleResponsibilityError.mobile("飛機");
    }
}

結果

汽車在公路上跑!
輪船在公路上跑!
飛機在公路上跑!

上述就違反了單一職責原則,對於不同的交通工具,代碼邏輯完全耦合在一起,我們無論修改那一類的交通工具,都會影響其他兩種數據

正確的示範

定義接口

package principle.singleresponsibility.correct;

/**
 * @author huangfu
 */
public interface TrafficTool {
    /**
     * 交通工具
     * @param trafficToolName
     */
    void mobile(String trafficToolName);
}

定義基類,這個主要是對一類的交通工具進行抽象定義,具體的實現可以由子類實現也可直接使用父類的方法

package principle.singleresponsibility.correct.base;

import principle.singleresponsibility.correct.TrafficTool;

/**
 * 陸地交通工具
 * @author huangfu
 */
public abstract class LandTrafficTool implements TrafficTool {
    @Override
    public void mobile(String trafficToolName) {
        System.out.println(trafficToolName + "在陸地跑!");
    }
}

package principle.singleresponsibility.correct.base;

import principle.singleresponsibility.correct.TrafficTool;

/**
 * 海上交通工具
 * @author huangfu
 */
public abstract class MaritimeTrafficTool implements TrafficTool {

    @Override
    public void mobile(String trafficToolName) {
        System.out.println(trafficToolName + "在海上跑!");
    }
}

package principle.singleresponsibility.correct.base;

import principle.singleresponsibility.correct.TrafficTool;

/**
 * 天空交通工具
 * @author huangfu
 */
public abstract class SkyTrafficTool implements TrafficTool {
    @Override
    public void mobile(String trafficToolName) {
        System.out.println(trafficToolName + "在天空上飛");
    }
}

定義具體的實現,可以使用抽象方法也可自己實現邏輯

package principle.singleresponsibility.correct;

import principle.singleresponsibility.correct.base.SkyTrafficTool;

/**
 * 飛機交通工具
 * @author huangfu
 */
public class AircraftTrafficTool extends SkyTrafficTool {

    @Override
    public void mobile(String trafficToolName) {
        super.mobile(trafficToolName);
    }
}

package principle.singleresponsibility.correct;

import principle.singleresponsibility.correct.base.LandTrafficTool;

/**
 * 汽車交通工具
 * @author huangfu
 */
public class CarTrafficTool extends LandTrafficTool {
    @Override
    public void mobile(String trafficToolName) {
        super.mobile(trafficToolName);
    }
}

package principle.singleresponsibility.correct;

import principle.singleresponsibility.correct.base.MaritimeTrafficTool;

/**
 * 輪船類交通工具
 * @author huangfu
 */
public class SteamshipTrafficTool extends MaritimeTrafficTool {
    @Override
    public void mobile(String trafficToolName) {
        super.mobile(trafficToolName);
    }
}

package principle.singleresponsibility.correct;

import principle.singleresponsibility.correct.base.LandTrafficTool;

/**
 * 特殊的交通工具:馬
 * @author huangfu
 */
public class HorseLandTrafficTool extends LandTrafficTool {
    @Override
    public void mobile(String trafficToolName) {
        System.out.println(trafficToolName + "在大草原上跑!");
    }
}

測試類

package principle.singleresponsibility.correct;

/**
 * @author huangfu
 */
public class TestTrafficTool {

    public static void main(String[] args) {
        //汽車類
        CarTrafficTool carTrafficTool = new CarTrafficTool();
        //飛機類
        AircraftTrafficTool aircraftTrafficTool = new AircraftTrafficTool();
        //輪船類
        SteamshipTrafficTool steamshipTrafficTool = new SteamshipTrafficTool();
        //特殊的交通工具 馬
        HorseLandTrafficTool horseLandTrafficTool = new HorseLandTrafficTool();

        carTrafficTool.mobile("蘭博基尼");
        aircraftTrafficTool.mobile("南航");
        steamshipTrafficTool.mobile("泰坦尼克號");
        horseLandTrafficTool.mobile("汗血寶馬");
    }
}

結果

蘭博基尼在陸地跑!
南航在天空上飛
泰坦尼克號在海上跑!
汗血寶馬在大草原上跑!

上述的完全的描述了單一職責的原則,對莫一類交通工具的修改,並不會影響到全局的功能,也可以基於自己的需求來定製自己的交通工具,而不會對全局的功能產生影響!

如何理解單一職責原則呢?

對於單一職責原則我的理解是:一個類只負責完成一個職責或者功能。不要涉及大而全的類,要設計粒度小、功能單一的類。單一職責原則是爲了實現代碼高內聚、低耦合,提高代碼的複用性、可讀性、可維護性。

是否有必要對類做一個精準的劃分呢?

其實對於不同的業務場景對於單一職責原則的理解都是不一樣的,我舉個例子是我在極客時間上王爭大佬的《設計模式之美》舉的一個例子,我覺的甚好,充分的說明了,在不同的業務場景下,類的職責劃分也不盡相同!

我們模擬一個在社交產品中描述用戶信息的類

package principle.singleresponsibility.entity;

/**
 * 用戶信息
 * @author huangfu
 */
public class UserInfo {
    private String userId;
    private String userName;
    private String sex;
    private Integer age;
    private String email;
    private String phone;
    private long createTime;
    private long lastLoginTime;
    /**
     * 省
     */
    private String provinceOfAddress;
    /**
     * 市
     */
    private String cityOfAddress;
    /**
     * 區
     */
    private String regionOfAddress;
    /**
     * 詳細地址
     */
    private String detailedAddress;
}

主要爭論有兩點,第一是覺得這個類符合單一職責,因爲包含的都是跟用戶相關的信息,所有的屬性和方法都隸屬於用戶這樣一個業務模型,滿足單一職責原則!

持不相同的觀點:因爲這個類裏面地址所佔比重比較高,所以應該將地址單獨抽出來,形成一個userAddress類!這樣兩個類的職責就更加單一了!

其實兩種說法都對,但是他們沒有區分業務場景,剛剛也說了,不同的業務模型所對應的設計方案也不盡相同

如果說在社交產品中,地址信息和其他信息完全一致都是做展示用,第一種說法就對,沒有必要拆分,他們確實屬於一個業務模型,但是如果後來產品做大,用戶的地址信息可能會用作用戶的收件地址,那麼第二種方式就對,因爲用戶信息和物流信息並不屬於一個業務模型!

那麼我們還可以在思考,公司越做越好,肯定不止一個社交產品。公司領導希望所有的社交產品的賬號和用戶信息互通,那麼此時我們需要將 userName,sex,phone等信息也拆分出來,以供其他系統使用!

那麼我們如何判斷我們的類是否足夠單一呢?

不同的應用場景、不同階段的需求背景、不同的業務層面,對同一個類的職責是否單一,可能會有不同的判定結果。實際上,一些側面的判斷指標更具有指導意義和可執行性,比如,出現下面這些情況就有可能說明這類的設計不滿足單一職責原則:

  • 類中的代碼行數、函數或者屬性過多;
  • 類依賴的其他類過多,或者依賴類的其他類過多;
  • 私有方法過多;
  • 比較難給類起一個合適的名字;
  • 類中大量的方法都是集中操作類中的某幾個屬性。

總結

事實上,單一職責設計原則正式代碼 高內聚低耦合 設計的基石,他通過拆分不同業務,避免不相關的業務耦合在一起,從而提高了代碼的高內聚;同時因爲類的職責單一,他的耦合性也會相應降低,從而完成開發中一直提倡的 高內聚,低耦合


歡迎關注公衆號 關注公衆號,回覆架構師,提供各類技術的學習資料提供參閱!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章