【抽象那些事】不必要的抽象

抽象型坏味

不必要的抽象

在软件设计中引入实际上不需要的抽象时,将导致这种坏味。

##为什么不可以有不必要的抽象?

抽象实体应该具有单一而重要的职责。如果创建的没必要或是只是为了方便,它们承担的职责微不足道,甚至没有承担任何职责,这违反了抽象原则。

##不必要的抽象的潜在原因

使用的是面向对象语言,思维却是过程型编程思维

过程型思维常常会创建执行功能而不是表示事物的类。这种类通常只有一两个方法,而这些方法操作的数据位于独立地“数据类”中。

使用不合适的语言功能

例如,使用"常量类"而不是枚举。这增加了不必要的类。

过度设计

例如,为了表示与Customer对象相关联的客户ID,创建一个名为CustomerID的类。更好的设计是:在Customer对象中使用一个字符串来存储客户ID。

示例分析一

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;
}

我们上面分析过,使用"常量类"而不是枚举,增加了不必要的类。可以使用枚举替换掉"常量类",消灭掉不必要的类。

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

示例分析二

一个电子商务应用程序,其中包含两个类:BestSellerBook和Book。每当客户程序要创建畅销书时,都创建一个BestSellerBook实例。在BestSellerBook内部只是将所有方法都委托给Book类,别的什么都不做。显然抽象BestSellerBook是多余的,因为其行为和抽象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);
    }
}

对于功能有限,不值得创建的类,应将其删除。

我们可以将BestSellerBook类删除,并且在Book类中添加属性IsBestSeller。这样在客户程序创建Book实例时需要指出图书是否为畅销书,可设置属性IsBestSeller,而不用像以前那样创建BestSellerBook类的实例。

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

现实考虑

###设计模式中的委托抽象

有些设计模式(如代理模式、门面模式和适配器模式)使用了委托,其中包含了一个看似不必要的类。例如,在对象适配器模式中,Adapter类看似只是将客户端请求委托给Adaptee类的相应方法。但是,Adapter类承担了明确而具体的职责:调整Adaptee类的接口,以满足客户端的需求。所以,判断抽象是否多余,还要具体情况具体分析。

对象适配器模式UML图

总结:

  1. 包含多余的抽象会增加设计的复杂性,影响整个设计的可理解性。
  2. 职责独特而明确的抽象有可能得到重用,而当抽象不承担任何职责或承担的职责微不足道时,就不太可能在其它地方重用。 ​

参考:《软件设计重构》


作者:撸码那些事

声明:本文为博主学习感悟总结,水平有限,如果不当,欢迎指正。如果您认为还不错,不妨关注一下下方的【微信公众号】按钮,谢谢支持。转载与引用请注明出处。

微信公众号:

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一个会写诗的程序员的博客

函数式编程与面向对象编程[2]: 静态类型语言的表达力 静态类型语言与动态类型语言函数式编程与面向对象编程[2]: 静态类型语言的表达力 静态类型语言与动态类型语言

像Java或者C#这样强类型的准静态语言在实现复杂的业务逻辑、开发大型商业系统、以及那些生命周期很长的应用中也有着非常强的优势

911
来自专栏Android机动车

Java 基础(一)——重新理解面向对象

如何利用对象完成真正有用的工作呢?必须有一种办法能向对象发出请求,令其做一些实际的事情。接口就是对一个对象的行为进行规范,使对象具有做某些事情的能力。

751
来自专栏IT派

细思极恐-你真的会写java吗?

自2013年毕业后,今年已经是我工作的第4个年头了,总在做java相关的工作,终于有时间坐下来,写一篇关于java写法的一篇文章,来探讨一下如果你真的是一个ja...

1915
来自专栏phodal

如何在业务代码中提升:创建领域特定语言

981
来自专栏程序人生

重构:撰写合格的代码

在「代码重构之道」里,我犯了个懒,讨论了什么情况下需要考虑重构,以及工具和方法来促进重构,但对如何重构代码本身,或者说:如何把烂代码转化成好代码,或者至少是合格...

3698
来自专栏Java面试通关手册

一份送给Java初学者的指南

我自己总结的Java学习的系统知识点以及面试问题,目前已经开源,会一直完善下去,欢迎建议和指导欢迎Star: https://github.com/Snailc...

1603
来自专栏一个会写诗的程序员的博客

第1章 JVM语言家族概览

所谓编程语言只是一个抽象的规范,而编译器是这个规范的实现,它是在这个规范的严格定义下被实现的.

2083
来自专栏程序你好

面向对象编程:对象之间的关系类型

1012
来自专栏编程

写好Java代码的30条经验总结

无可厚非你是一名程序员,但你真的是一个优秀的程序员吗?答案可不一定了。想要成为一个优秀的程序员,有着良好的代码编写习惯是必不可少的。下面就让我们来看看Java代...

2245
来自专栏玄魂工作室

CTF实战30 CTF题目练习和讲解五(讲解部分)

该培训中提及的技术只适用于合法CTF比赛和有合法授权的渗透测试,请勿用于其他非法用途,如用作其他非法用途与本文作者无关

2113

扫码关注云+社区

领取腾讯云代金券