[Things about encapsulation] Missing encapsulation

[tap]

mark

missing encapsulation

This bad smell results when implementation changes are not encapsulated in abstractions and hierarchies.

It usually takes the form of:

  • Client programs are tightly coupled to the service variants they require, and any time a new variant needs to be supported or an existing variant is modified, the client program is affected.
  • Whenever a new variant needs to be supported in the hierarchy, a large number of unnecessary classes are added, which increases the complexity of the design.

Why can't encapsulation be missing?

The Open-Closed Principle (OCP) states that types should be open for extension and closed for modification. That is, the behavior of the type should be changed by extension (not modification). OCP is violated when implementation changes are not encapsulated in a type or hierarchy.

Potential causes of missing encapsulation

Not realizing that the focus is constantly changing

Not anticipating that concerns might change, and not properly encapsulating those concerns in the design.

mixed concerns

Aggregating concerns that are independent of each other in a hierarchy, rather than separate, can lead to an explosion in the number of classes if concerns change.

naive design decisions

Taking an overly simplistic approach, such as creating a class for each combination of changes, can lead to unnecessarily complex designs.

Example analysis one

Suppose there is an Entryption class that needs to encrypt data using an encryption algorithm. There are many encryption algorithms to choose from, including DES (Data Encryption Standard), AES (Advanced Encryption Standard), TDES (Triple Data Encryption Standard) and so on. The Entryption class uses the DES algorithm to encrypt data.

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

Suppose a new requirement arises that requires data to be encrypted using the AES algorithm.

The worst case appears:

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

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

This scheme has many shortcomings:

  • The Encryption class becomes larger and harder to maintain because it implements multiple encryption algorithms, but only uses one at a time.
  • It is difficult to add new algorithms and modify existing algorithms, because encryption algorithms are an integral part of the Encryption class.
  • The encryption algorithm provides services to the Encryption class, but is tightly coupled to the Encryption class and cannot be reused elsewhere.

If you are not satisfied, refactor. First, use inheritance to refactor. There are 2 options:

Option 1:

Let the Encryption class inherit the AESEncryptionAlgorithm or DESEncryptionAlgorithm class as required, and provide the method Encrypt(). The problem brought by this scheme is that the Encryption class will be associated with a specific encryption algorithm at the compile stage, and more seriously, the relationship between the classes is not an is-a relationship.

/// <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();
    }
}

Option 2:

Create subclasses AESEncryption and DESEncryption, both of which extend the Encryption class and contain implementations of the encryption algorithms AES and DES, respectively. Client programs can create Encryption references that point to objects of a particular subclass. New encryption algorithms are easily supported by adding new subclasses. But the problem with this scheme is that AESEncryption and DESEncryption will inherit other methods of the Encryption class, reducing the reusability of the encryption algorithm.

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算法进行加密
    }
}

The best option is to use strategy mode:

  • Encryption objects can be configured with specific encryption algorithms at runtime
  • Algorithms defined in the Hierarchy EncryptionAlgorithm can be reused elsewhere
  • It is easy to support new algorithms as needed
/// <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
{
    EncryptionAlgorithm algo;
    /// <summary>
    /// 使用算法进行加密
    /// </summary>
    public void Encrypt()
    {
        algo.Encrypt();
    }
}

Example analysis two

Supports designs for encrypting various content (Image and Text) using different algorithms (DES and AES).

Simplest and most intuitive design:

mark

In this design, there are two points of change: supported content types and encryption algorithm types. For each possible combination of these two change points, a class is used to represent it. There is a serious problem with this: Assuming that support for the new encryption algorithm TDES and the new content type Data is now required, the number of classes explodes. Because the change points are mixed together, they are not packaged separately.

mark

Use bridge mode to encapsulate:

mark

Using the bridge pattern, encapsulate the changes of these two concerns separately. Now to introduce a new content type Data and a new encryption algorithm TDES, just add two new classes. It not only solves the problem of the explosive growth of the number of classes, but also increases the reusability of the encryption algorithm in the hierarchy whose root is the interface EncryptionAlgorithm.

Summarize

  1. When unrelated concerns are mixed up, abstractions become difficult to reuse.

  2. For possible changes in the business, extension points should be given to ensure the Open-Closed Principle (OCP), which is open for extension and closed for modification.

Reference: "Software Design Refactoring"




Source: http://songwenjie.cnblogs.com/
Disclaimer: This article is a summary of bloggers' learning and perceptions. The level is limited. If it is inappropriate, please correct me. If you think it is not bad, you may click the [ Recommended ] button below, thank you for your support. Please indicate the source when reprinting and citing.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325124499&siteId=291194637