模塊化原則倡導利用集中和分解等手法創建高內聚、低耦合的抽象。 為了理解模塊化的含義及其很重要的原因,來看看一本書的極端情況。假設一本書像講一個長故事一樣闡述其中的內容,中間沒有任何停頓,也沒有章節。試問面對這樣的圖書,讀者將作何反應呢?我估計心中一定有千萬隻草泥馬在崩騰吧。如果這本書根據內容分為不同 ...
模塊化原則倡導利用集中和分解等手法創建高內聚、低耦合的抽象。
為了理解模塊化的含義及其很重要的原因,來看看一本書的極端情況。假設一本書像講一個長故事一樣闡述其中的內容,中間沒有任何停頓,也沒有章節。試問面對這樣的圖書,讀者將作何反應呢?我估計心中一定有千萬隻草泥馬在崩騰吧。如果這本書根據內容分為不同的章節(模塊)進行講述,情況是不是就完全不一樣了呢?同樣,設計軟體時,遵循模塊化原則也很重要。需要指出的是模塊化通常是一個系統級考慮因素,指的是如何將抽象組織成邏輯模塊。但是我們這裡的術語模塊指的是類級抽象:具體類、抽象類和介面。模塊化的目標是創建高內聚、低耦合的抽象。
應用模塊化原則的實現手法
- 將相關的數據和方法集中在一起:每個抽象都必須是內聚的,即抽象應將相關的數據和操作它們的方法集中在一起。
- 將抽象分解為易於管理的規模:將大抽象分解為規模適中(既不太大也不太小)的小抽象。大類不僅難以理解,而且難以修改,因為這種類實現的職責可能交織在一起。
- 創建非迴圈依賴:抽象之間不應該存在迴圈依賴。否則修改一個抽象可能引起連鎖反應,波及整個設計。
- 限制依賴關係數:創建扇入和扇出低的抽象。扇入指的是有多少個抽象依賴於當前的抽象,因此修改扇入高的抽象時,可能需要修改大量依賴於它的抽象。扇出指的是當前抽象依賴於多少個其它的抽象,高扇出意味著修改很多抽象時都可能影響當前抽象。為避免潛在的修改引發連鎖反應,減少設計中抽象之間的依賴關係數很重要。
違反模塊化原則導致的壞味
我們這篇博客主要講解分析拆散的模塊化壞味,對於其它模塊化壞味將在後面的博客講解分析。
拆散的模塊化
應集中放在一個抽象中的數據和方法分散在多個抽象中,將導致這種壞味。
常見表現形式如下:
類被用作數據容器,其中沒有任何操作這些數據的方法
類的方法更多的被其它類成員調用
為什麼不能有拆散的模塊化?
如果抽象只包含數據成員,而操作這些數據成員的方法分散在多個抽象中時,原本應屬於一個抽象的成員分散在多個抽象中時,將導致這些抽象之間緊密耦合。違反了模塊化原則。
拆散的模塊化潛在原因
以過程型思維使用面向對象語言
過程型語言傾向於將數據和操作它的函數分開,從而導致這種壞味。
不熟悉既有設計
大型的項目設計很複雜。在這樣的項目中,每位開發人員通常只負責系統中很小的一部分,不瞭解設計的其它部分。這可能導致成員被放置到錯誤的類中。
示例分析
來看一個設備管理應用程式。在這個應用程式中,與設備相關的數據存儲在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/
聲明:本文為博主學習感悟總結,水平有限,如果不當,歡迎指正。如果您認為還不錯,不妨點擊一下下方的【推薦】按鈕,謝謝支持。轉載與引用請註明出處。
微信公眾號: