【Abstract those things】Unnecessary abstraction

mark

unnecessary abstraction

This bad smell is caused when abstractions that are not actually needed are introduced into the software design.

Why can't there be unnecessary abstractions?

An abstract entity should have a single and important responsibility. If created unnecessarily or just for convenience, they assume trivial or no responsibility, which violates the principle of abstraction.

Potential causes of unnecessary abstraction

Object-oriented language is used, but the thinking is procedural programming thinking

Procedural thinking often creates classes that perform functions rather than represent things. Such classes usually have only one or two methods, and the data that these methods operate on are in separate "data classes".

Using inappropriate language features

For example, use "constant classes" instead of enums. This adds unnecessary classes.

over-engineered

For example, to represent the customer ID associated with the Customer object, create a class called CustomerID. A better design: use a string in the Customer object to store the customer ID.

Example analysis one

public class FormattableFlags
{
    /// <summary>
    /// 禁止显式实例化这个类
    /// </summary>
    private FormattableFlags()
    {
    }
    /// <summary>
    /// 将输出左对齐
    /// </summary>
    public  const int LEFT_JUSTIFY = 1;
    /// <summary>
    /// 将输出转换为大写
    /// </summary>
    public const int UPPERCASE = 2;
    /// <summary>
    /// 要求输出使用替换类型
    /// </summary>
    public const int ALTERNATE = 3;
}

As we analyzed above, using "constant classes" instead of enumerations adds unnecessary classes. You can replace "constant classes" with enums, eliminating unnecessary classes.

public enum FormattableFlagsEnum
{
    /// <summary>
    /// 将输出左对齐
    /// </summary>
    LEFT_JUSTIFY = 1,
    /// <summary>
    /// 将输出转换为大写
    /// </summary>
    UPPERCASE = 2,
    /// <summary>
    /// 要求输出使用替换类型
    /// </summary>
    ALTERNATE = 3
}

Example analysis two

An e-commerce application with two classes: BestSellerBook and Book. Whenever a client program wants to create a bestseller, an instance of BestSellerBook is created. Inside BestSellerBook just delegate all methods to the Book class and do nothing else. Obviously the abstract BestSellerBook is redundant since it behaves exactly the same as the abstract Book.

/// <summary>
/// 图书类
/// </summary>
public class Book
{
    /// <summary>
    /// 价格
    /// </summary>
    public decimal Price { get; private set; }
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="price"></param>
    public Book(decimal price)
    {
        this.Price = price;
    }
    /// <summary>
    /// 修改图书价格
    /// </summary>
    /// <param name="price">图书价格</param>
    public void ModifyPrice(decimal price)
    {
        this.Price = price;
    }
}
/// <summary>
/// 畅销图书类
/// </summary>
public  class BestSellerBook
{
    private Book book = null;
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="price"></param>
    public BestSellerBook(decimal price)
    {
        book = new Book(price);
    }
    /// <summary>
    /// 修改图书价格
    /// </summary>
    /// <param name="price">图书价格</param>
    public void ModifyPrice(decimal price)
    {
        book.ModifyPrice(price);
    }
}

Classes with limited functionality that are not worth creating should be removed.

We can delete the BestSellerBook class and add the property IsBestSeller to the Book class. In this way, when the client program creates a Book instance, it needs to indicate whether the book is a bestseller, and the property IsBestSeller can be set instead of creating an instance of the BestSellerBook class as before.

/// <summary>
/// 图书类
/// </summary>
public class Book
{
    /// <summary>
    /// 价格
    /// </summary>
    public decimal Price { get; private set; }
    /// <summary>
    /// 是否畅销书
    /// </summary>
    public bool IsBestSeller { get; private set; }
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="price">价格</param>
    /// <param name="isBestSeller">是否畅销书</param>
    public Book(decimal price, bool isBestSeller)
    {
        this.Price = price;
        this.IsBestSeller = isBestSeller;
    }
    /// <summary>
    /// 修改图书价格
    /// </summary>
    /// <param name="price">图书价格</param>
    public void ModifyPrice(decimal price)
    {
        this.Price = price;
    }
    /// <summary>
    ///  修改是否畅销书
    /// </summary>
    /// <param name="isBestSeller">是否畅销书</param>
    public void ModifyIsBestSeller(bool isBestSeller)
    {
        this.IsBestSeller = isBestSeller;
    }
}

realistic considerations

Delegate Abstraction in Design Patterns

Some design patterns (such as Proxy, Facade, and Adapter) use delegation, which includes a seemingly unnecessary class. For example, in the Object Adapter pattern, the Adapter class appears to simply delegate client requests to the appropriate methods of the Adaptee class. However, the Adapter class takes on a clear and specific responsibility: to adjust the Interface of the Adaptee class to meet the needs of the client . Therefore, to judge whether the abstraction is redundant, it is necessary to analyze the specific situation.

mark

Summarize:

  1. Including redundant abstractions increases the complexity of the design and affects the understandability of the overall design.

  2. Abstractions with unique and well-defined responsibilities are likely to be reused, whereas abstractions with no or trivial responsibilities are less likely to be reused elsewhere.

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.

WeChat public account:


Guess you like

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