1. 程式人生 > >【模塊化那些事】 拆散的模塊化

【模塊化那些事】 拆散的模塊化

影響 創建 關註 cor ima () href 實現 col

模塊化原則倡導利用集中和分解等手法創建高內聚、低耦合的抽象。

技術分享圖片

為了理解模塊化的含義及其很重要的原因,來看看一本書的極端情況。假設一本書像講一個長故事一樣闡述其中的內容,中間沒有任何停頓,也沒有章節。試問面對這樣的圖書,讀者將作何反應呢?我估計心中一定有千萬只草泥馬在崩騰吧。如果這本書根據內容分為不同的章節(模塊)進行講述,情況是不是就完全不一樣了呢?同樣,設計軟件時,遵循模塊化原則也很重要。需要指出的是模塊化通常是一個系統級考慮因素,指的是如何將抽象組織成邏輯模塊。但是我們這裏的術語模塊指的是類級抽象:具體類、抽象類和接口。模塊化的目標是創建高內聚、低耦合的抽象。

應用模塊化原則的實現手法

技術分享圖片

  • 將相關的數據和方法集中在一起:每個抽象都必須是內聚的,即抽象應將相關的數據和操作它們的方法集中在一起
  • 將抽象分解為易於管理的規模:將大抽象分解為規模適中(既不太大也不太小)的小抽象。大類不僅難以理解,而且難以修改,因為這種類實現的職責可能交織在一起。
  • 創建非循環依賴:抽象之間不應該存在循環依賴。否則修改一個抽象可能引起連鎖反應,波及整個設計。
  • 限制依賴關系數:創建扇入和扇出低的抽象。扇入指的是有多少個抽象依賴於當前的抽象,因此修改扇入高的抽象時,可能需要修改大量依賴於它的抽象。扇出指的是當前抽象依賴於多少個其它的抽象,高扇出意味著修改很多抽象時都可能影響當前抽象。為避免潛在的修改引發連鎖反應,減少設計中抽象之間的依賴關系數很重要。

違反模塊化原則導致的壞味

技術分享圖片

我們這篇博客主要講解分析拆散的模塊化壞味,對於其它模塊化壞味將在後面的博客講解分析。

拆散的模塊化

應集中放在一個抽象中的數據和方法分散在多個抽象中,將導致這種壞味。

常見表現形式如下:

  • 類被用作數據容器,其中沒有任何操作這些數據的方法

  • 類的方法更多的被其它類成員調用

?

為什麽不能有拆散的模塊化?

如果抽象只包含數據成員,而操作這些數據成員的方法分散在多個抽象中時,原本應屬於一個抽象的成員分散在多個抽象中時,將導致這些抽象之間緊密耦合。違反了模塊化原則。

拆散的模塊化潛在原因

以過程型思維使用面向對象語言

過程型語言傾向於將數據和操作它的函數分開,從而導致這種壞味。

不熟悉既有設計

大型的項目設計很復雜。在這樣的項目中,每位開發人員通常只負責系統中很小的一部分,不了解設計的其它部分。這可能導致成員被放置到錯誤的類中。

示例分析

來看一個設備管理應用程序。在這個應用程序中,與設備相關的數據存儲在DeviceData類中,而處理這些設備數據的方法由Device類提供。DeviceData類只有公共數據成員,沒有任何方法。而Device類包含一個類型為DeviceData的對象,並提供了訪問和操作該數據成員的方法。

技術分享圖片

這些數據和方法原本應該集中放在一個類中,卻分散在了Device和DeviceData類中,顯然存在"拆散的模塊化"壞味。

代碼實現:

public class DeviceData
{
    /// <summary>
    /// 設備ID
    /// </summary>
    public string DeviceID { get; set; }
    /// <summary>
    /// 設備位置
    /// </summary>
    public string DevicePath { get; set; }
    /// <summary>
    /// 是否可用
    /// </summary>
    public bool Enabled { get; set; }
}
public class Device
{
    private DeviceData deviceData = new DeviceData();
    
    /// <summary>
    /// 獲取設備ID
    /// </summary>
    /// <returns></returns>
    public string GetDeviceID()
    {
        return deviceData.DeviceID;
    }
    /// <summary>
    /// 設置設備ID
    /// </summary>
    /// <param name="deviceID">設備ID</param>
    /// <returns></returns>
    public bool SetDeviceID(string deviceID)
    {
        deviceData.DeviceID = deviceID;
        return true;
    }
    /// <summary>
    /// 是否可用
    /// </summary>
    /// <returns></returns>
    public bool IsEnabled()
    {
        return deviceData.Enabled;
    }
}

重構"拆散的模塊化"

  • 如果一個方法更多地被另一個類(Target類)而不是定義它的類(Source類)調用,就采用“移動方法”,將這個方法從Source類移到Target類中。

  • 如果一個字段更多地被另一個類(Target類)而不是定義它的類(Source類)使用,就采用“移動字段”,將這個字段從Source類移到Target類中。

技術分享圖片

技術分享圖片

重構後的代碼實現:

public class Device
{
    /// <summary>
    /// 設備ID
    /// </summary>
    private string DeviceID { get; set; }
    /// <summary>
    /// 設備位置
    /// </summary>
    private string DevicePath { get; set; }
    /// <summary>
    /// 是否可用
    /// </summary>
    private bool Enabled { get; set; }

    /// <summary>
    /// 獲取設備ID
    /// </summary>
    /// <returns></returns>
    public string GetDeviceID()
    {
        return DeviceID;
    }
    /// <summary>
    /// 設置設備ID
    /// </summary>
    /// <param name="deviceID">設備ID</param>
    /// <returns></returns>
    public bool SetDeviceID(string deviceID)
    {
        DeviceID = deviceID;
        return true;
    }
    /// <summary>
    /// 是否可用
    /// </summary>
    /// <returns></returns>
    public bool IsEnabled()
    {
        return Enabled;
    }
}

現實考慮

數據傳輸對象

在使用遠程接口的情況下,常常使用數據傳輸對象(DTO)在進程之間傳輸數據,以減少遠程調用數。DTO聚合數據但不包含行為。這是有意為之,為了方便數據同步。



作者:擼碼那些事
來源:http://songwenjie.cnblogs.com/
聲明:本文為博主學習感悟總結,水平有限,如果不當,歡迎指正。如果您認為還不錯,不妨點擊一下下方的推薦按鈕,謝謝支持。轉載與引用請註明出處。
微信公眾號:
技術分享圖片

【模塊化那些事】 拆散的模塊化