吃透單一職責原則,100倍效果提升程式碼質量

碼農談IT發表於2023-10-26

來源:mikechen的網際網路架構

設計模式 7 大原則中,單一職責原則最簡單,但又較難運用。

單一職責原則的核心思想是:讓專業的人,做專業的事。

就如同醫生和護士,各有自己特定的職責,且專注於自己的職責。

吃透單一職責原則,100倍效果提升程式碼質量單一職責原單一職責原則既是架構設計的基石,也是 Java 面試高頻必考點,非常重要。

大家好,我是mikechen。

本篇,我會透過【圖例+原始碼】結合,詳解單一職責原則

  • 單一職責原則是什麼

  • 為什麼要遵循單一職責原則

  • 單一職責原則的 UML 類圖

  • 單一職責原則的 3 大優點

  • 單一職責原則的實現原理及示例


PS.

Mike 已將本文內容歸納到《Java面試突擊合集》PDF,方便大家面試通關。

《Java面試突擊合集》PDF 已收錄了 600+ 道大廠面試真題,約 14 萬字,350頁。

助力面試突擊,提高面試通關率末自取

吃透單一職責原則,100倍效果提升程式碼質量


01
   什麼是單一職責原則


單一職責原則,英文全稱 Single Responsibility Principle,又稱為單一功能原則


單一職責原則是指一個類要職責單一,只負責一個特定的功能或任務,而不應該承擔過多的職責,使得程式碼更容易理解、維護和擴充套件。

 

02
  為什麼要遵循單一職責原則


遵循單一職責原則,每個類都專注於一個明確的職責,可以降低程式碼的複雜性,提高程式碼的可維護性。


如果一個類承擔了太多的職責,就會難以維護和修改。


  • 一個職責的變化,可能會削弱這個類實現其他職責的能力;


  • 當客戶端需要該物件的某一個職責時,需要將其它不需要的職責全部包含進來,這樣會導致程式碼冗餘。


單一職責原則的 UML 類圖:

吃透單一職責原則,100倍效果提升程式碼質量

在 UML 類圖中,虛線箭頭表示依賴關係,常用在方法、引數等,由依賴方指向被依賴方。

在實踐了單一職責原則的 UML 類圖中,不屬於 Employee 的兩個職責,則被分成了兩類:

  • FinancialApartment 類


  • HRApartment 類


我們再來看一個具體的示例。

假設:

一個醫院系統中,有一個名為 Doctor(醫生)的類,這個類負責管理醫生的資訊和診斷患者。
























class Doctor {    private String name;    private String specialization;
   public Doctor(String name, String specialization) {        this.name = name;        this.specialization = specialization;    }
   public void diagnosePatient(Patient patient) {        // 實現診斷患者的邏輯    }
   public void prescribeMedicine(Patient patient, String medicine) {        // 實現給患者開藥的邏輯    }
   public void scheduleSurgery(Patient patient, Date date) {        // 實現安排手術的邏輯    }
   // 其他與醫生資訊相關的方法和屬性}


在這個示例中,Doctor 類不僅管理醫生的資訊,還包含了與患者的診斷、處方藥和手術安排相關的方法。

在 Doctor 類中,包含了 4 種不同的邏輯:

  • 診斷患者;


  • 給患者開藥;


  • 安排手術;


  • 其他與醫生資訊相關的方法和屬性。


這個邏輯看似沒問題,但不符合單一職責原則,因為這四種邏輯各不相同。

這樣的設計會帶來什麼影響呢?

我們想象下,如果業務發生了變化,就需要去修改當前這個類。牽一髮而動全身,這樣設計顯然不合理。

因此,我們需要對類中的不同責任進行拆分:


































class Doctor {    private String name;    private String specialization;
   public Doctor(String name, String specialization) {        this.name = name;        this.specialization = specialization;    }
   // 醫生資訊管理的方法和屬性
   // ...}
class Patient {    // 患者資訊管理的方法和屬性
   // ...}
class MedicalStaff {    public void diagnosePatient(Doctor doctor, Patient patient) {        // 實現診斷患者的邏輯    }
   public void prescribeMedicine(Doctor doctor, Patient patient, String medicine) {        // 實現給患者開藥的邏輯    }
   public void scheduleSurgery(Doctor doctor, Patient patient, Date date) {        // 實現安排手術的邏輯    }}


現在,將醫生的資訊管理、以及患者的診斷、處方藥、手術安排等功能,分別分配給不同的類:

  • Doctor 類:負責醫生的資訊管理;


  • Patient 類:負責患者的資訊管理;


  • MedicalStaff 類:負責醫療工作。


使用單一職責原則,將這些職責分離開來,讓每個類都有自己明確的職責,且只有一個職責。

由於職責區分,各個類的變化不會相互影響,程式碼容易理解和維護。

這裡可能有同學會問,怎麼判斷是否需要分離職責?或者說分離職責的依據是什麼?

是否需要分離職責,主要則取決於變化:

  • 當變化發生,隻影響其中一個職責,需要拆分;


  • 如果變化都影響到這兩個職責,則不需要拆分。


03
  單一職責原則的三大優點


根據以上,我們總結出了單一職責原則的優點:

  • 可以降低類的複雜性;


  • 可以提高程式碼的可讀性、可維護性;


  • 可以降低變更引起的風險。

 

04
  單一職責原則的實現示例


示例:

在一個電商訂單處理系統中,有兩個職責:訂單管理、發貨處理。

我們可以使用單一職責原則,將這兩個職責分離開來:

























































// 訂單管理類負責處理訂單的建立、修改和查詢class OrderManager {    public void createOrder(Order order) {        // 實現訂單建立邏輯    }
   public void updateOrder(Order order) {        // 實現訂單修改邏輯    }
   public Order getOrder(int orderId) {        // 實現訂單查詢邏輯        return null;    }}
// 發貨處理類負責處理訂單的發貨操作class ShippingProcessor {    public void shipOrder(Order order) {        // 實現訂單發貨邏輯    }}
// 訂單類,包含訂單的相關資訊class Order {    private int orderId;    private String customerName;    private String shippingAddress;    // 其他訂單資訊的成員變數和方法
   public Order(int orderId, String customerName, String shippingAddress) {        this.orderId = orderId;        this.customerName = customerName;        this.shippingAddress = shippingAddress;        // 初始化其他訂單資訊    }
   // 其他訂單操作的方法和屬性訪問器}
public class Main {    public static void main(String[] args) {        OrderManager orderManager = new OrderManager();        ShippingProcessor shippingProcessor = new ShippingProcessor();
       // 建立訂單        Order newOrder = new Order(1, "John Doe", "123 Main St");        orderManager.createOrder(newOrder);
       // 查詢訂單        Order retrievedOrder = orderManager.getOrder(1);
       // 發貨訂單        shippingProcessor.shipOrder(retrievedOrder);    }}


在這個示例中:

  • OrderManager 類:負責訂單的建立、修改和查詢;


  • ShippingProcessor 類:負責訂單的發貨操作。


每個類都有明確的職責,且只負責一個職責,避免了不必要的複雜性和依賴關係。


總結
 

本文重點介紹了單一職責原則的核心知識點。

單一職責原則的核心思想讓每個類專注於一個明確的職責,以提高程式碼的可維護性和可讀性用於類、介面、方法中。

單一職責原則的難點在於如何將類的職責劃分得清晰明瞭,如何識別和消除不必要的職責......。在實際開發中,我們很容易將不同的責任歸類在一起,或者過度拆分。

是否需要拆分職責,參考思路:當變化發生,隻影響其中一個職責,那就需要拆分。如果變化都影響到這兩個職責,那就不需要拆分。

合理拆分職責的前提:設計者需要對業務足夠了解、同時具備較強的分析設計能力及相關實踐經驗,這個部分在 Mike 的架構專題中有詳解。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024924/viewspace-2991288/,如需轉載,請註明出處,否則將追究法律責任。

相關文章