【封裝那些事】 缺失封裝


mark

缺失封裝

沒有將實現變化封裝在抽象和層次結構中時,將導致這種壞味。

表現形式通常如下:

  • 客戶程序與其需要的服務變種緊密耦合,每當需要支持新變種或修改既有變種時,都將影響客戶程序。
  • 每當需要在層次結構中支持新變種時,都添加了大量不必要的類,這增加了設計的復雜度。

為什么不能缺失封裝?

開閉原則(OCP)指出,類型應對擴展開放,對修改關閉。也就是說應該通過擴展(而不是修改)來改變類型的行為。沒有在類型或層次結構中封裝實現變化時,便違反了OCP。

缺失封裝潛在的原因

未意識到關注點會不斷變化

沒有預測到關注點可能發生變化,進而沒有在設計中正確封裝這些關注點。

混合關注點

將彼此獨立的各個關注點聚合在一個層次結構中,而不是分開時,如果關注點發生變化,可能導致類的數量呈爆炸式增長。

幼稚的設計決策

采用過於簡單的方法,如為每種變化組合創建一個類時,可能導致設計無謂的復雜。

示例分析一

假設有一個Entryption類,它需要使用加密算法對數據進行加密。可供選擇的加密算法有很多,包括DES(數據加密標准)、AES(高級加密標准)、TDES(三重數據加密標准)等。Entryption類使用DES算法對數據進行加密。

public class Encryption
{
    /// <summary>
    /// 使用DES算法進行加密
    /// </summary>
    public void Encrypt()
    {
        // 使用DES算法進行加密
    }
}

假設出現了新需求,要求使用AES算法對數據進行加密。

最差的方案出現了:

public class Encryption
{
    /// <summary>
    /// 使用DES算法進行加密
    /// </summary>
    public void EncryptUsingDES()
    {
        // 使用DES算法進行加密
    }

     /// <summary>
    /// 使用AES算法進行加密
    /// </summary>
    public void EncryptUsingAES()
    {
        // 使用AES算法進行加密
    }
}

這種方案有很多不盡如人意的地方:

  • Encryption類變得更大、更難以維護,因為它實現了多種加密算法,但是每次只使用一種。
  • 難以添加新算法以及修改既有算法,因為加密算法是Encryption類不可分割的部分。
  • 加密算法向Encryption類提供服務,但是與Encryption類緊緊耦合在一起,無法在其它地方重用。

不滿意就重構,首先使用繼承進行重構,會有2種方案可以選擇:

選擇1:

讓Encryption類根據需求繼承AESEncryptionAlgorithm或DESEncryptionAlgorithm類,並提供方法Encrypt()。這種方案帶來的問題是Encryption類在編譯階段就將關聯到特定的加密算法,更嚴重的是類之間的關系並不是is-a關系。

/// <summary>
/// AES算法加密類
/// </summary>
public class AESEncryptionAlgorithm
{
    /// <summary>
    /// 使用AES算法進行加密
    /// </summary>
    public void EncryptUsingAES()
    {
        // 使用AES算法進行加密
    }
}

/// <summary>
/// DES算法加密類
/// </summary>
public class DESEncryptionAlgorithm
{
    /// <summary>
    /// 使用DES算法進行加密
    /// </summary>
    public void EncryptUsingDES()
    {
        // 使用DES算法進行加密
    }
}

public class Encryption: AESEncryptionAlgorithm
{
    /// <summary>
    /// 使用算法進行加密
    /// </summary>
    public void Encrypt()
    {
        EncryptUsingAES();
    }
}

選擇2:

創建子類AESEncryption和DESEncryption,它們都擴展了Encryption類,並分別包含加密算法AES和DES的實現。客戶程序可創建Encryption的引用,這些引用指向特定子類的對象。通過添加新的子類,很容易支持新的加密算法。但是這種方案的問題是AESEncryption和DESEncryption將繼承Encryption類的其它方法,降低了加密算法的可重用性。

public abstract class Encryption
{
    /// <summary>
    /// 使用算法進行加密
    /// </summary>
    public abstract void Encrypt();
}

/// <summary>
/// AES算法加密類
/// </summary>
public class AESEncryption : Encryption
{
    /// <summary>
    /// 使用 AES算法進行加密
    /// </summary>
    public override void Encrypt()
    {
        // 使用 AES算法進行加密
    }
}

/// <summary>
/// DES算法加密類
/// </summary>
public class DESEncryption : Encryption
{
    /// <summary>
    /// 使用 DES算法進行加密
    /// </summary>
    public override void Encrypt()
    {
        // 使用 DES算法進行加密
    }
}

最佳的選擇是使用策略模式:

  • 可在運行階段給Encryption對象配置特定的加密算法
  • 可在其它地方重用層次結構EncryptionAlgorithm中定義的算法
  • 很容易根據需要支持新的算法
/// <summary>
/// 算法加密接口
/// </summary>
public interface EncryptionAlgorithm
{
   void Encrypt();
}

/// <summary>
/// DES算法加密類
/// </summary>
public class DESEncryptionAlgorithm : EncryptionAlgorithm
{
    public void Encrypt()
    {
        //使用 DES算法進行加密
    }
}
/// <summary>
/// AES算法加密類
/// </summary>
public class AESEncryptionAlgorithm : EncryptionAlgorithm
{
    public void Encrypt()
    {
        //使用 AES算法進行加密
    }
}

public class Encryption
{
    private EncryptionAlgorithm algo;

    public Encryption(EncryptionAlgorithm algo)
    {
        this.algo = algo;
    }
 
    /// <summary>
    /// 使用算法進行加密
    /// </summary>
    public void Encrypt()
    {
        algo.Encrypt();
    }
}

示例分析二

支持使用不同算法(DES和AES)對各種內容(Image和Text)進行加密的設計。

最簡單最直觀的的設計:

mark

在這個設計中,有兩個變化點:支持的內容類型和加密算法類型。對於這兩個變化點的每種可能組合,都使用了一個類來表示。這樣會有一個嚴重的問題:假設現在要求支持新加密算法TDES和新內容類型Data,類的數量呈爆炸性增長。因為變化點混在了一起,沒有分別進行封裝。

mark

使用橋接模式進行封裝:

mark

使用橋接模式,分別封裝這兩個關注點的變化。現在要引入新內容類型Data和新加密算法TDES,只需要添加兩個新類。既解決了類數量呈爆炸增長的問題,又增加了根為接口EncryptionAlgorithm層次結構中的加密算法的可重用性。

總結

  1. 不相關的關注點混在一起,抽象將變得難以重用。

  2. 對業務中可能的變化點,要給予擴展點,保證開閉原則(OCP),對擴展開放,對修改關閉。

參考:《軟件設計重構》

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM