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

不必要的抽象

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

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

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

不必要的抽象的潜在原因

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

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

使用不合适的语言功能

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

过度设计

例如,为了表示与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类的接口,以满足客户端的需求。所以,判断抽象是否多余,还要具体情况具体分析。

总结:

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

参考:《软件设计重构》

原文发布于微信公众号 - 撸码那些事(lumanxs)

原文发表时间:2018-05-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

Python进阶必读汇总

前言 昨天翻到了一本在github开源的书: Intermediate Python. 就有了此文, 梳理了一下一些之前翻到的对python语言细节点的答案, ...

3045
来自专栏Create Sun

设计模式--------工厂模式

前言   两个月前写过一次设计模式系列,但很不满意就删除了。显然希望能做到学以致用,而非看书总结,设计模式绝对是你解决问题的一个思路,但在这有点自己的小建议,设...

3368
来自专栏分布式系统和大数据处理

RPG设计(角色技能与Strategy模式)

看过一些设计模式方面的书籍和文章,虽然很正式,很权威,(也觉得有那么一点刻板),总是觉得让人不那么好靠近。于是,我思考着像写故事一样来写下自己对设计模式的理解。...

2122
来自专栏晨星先生的自留地

一道小CTF

2854
来自专栏CDA数据分析师

震惊小伙伴的Python单行代码

几年前,函数式编程的复兴正值巅峰,一篇介绍 Scala 中 10 个单行函数式代码的博文在网上走红。很快地,一系列使用其他语言实现这些单行代码的文章也随之出现,...

2107
来自专栏web编程技术分享

你可曾见过如此简单粗暴的JavaScript解说 -- js脚本运行机制

2966
来自专栏程序员互动联盟

【答疑解惑】推荐给新手的Java学习资料

国际惯例,每天更新答疑解惑。网友们在群里有很多问题讨论,小编挑几个很有代表性的问题给大家叨叨几句。 一、关于Java学习资料: 昨天有网友对于Java群中资料少...

2824
来自专栏大数据钻研

如何设计优雅的类结构

注:正文中的引用是直接引用作者作者的话,两条横线中间的段落的是我自己的观点,其他大约都可以算是笔记了。 「Clean Code」这本书从这一章开始文风有些变化,...

2696
来自专栏何俊林

Android Multimedia框架总结(十)Stagefright框架之音视频输出过程

前言:上篇文中最后介绍了数据解码放到Buffer过程,今天分析的是stagefright框架中音视频输出过程: 先看下今天的Agenda: 一张图回顾数据处理...

2238
来自专栏更流畅、简洁的软件开发方式

《你必须知道的.net》读书笔记 001——1.1 对象的旅行

    好久没看书了,上次看书的时候还是一年前了,一个偶然的机会,比较系统的看了一下OO的基础,封装、继承、多态等,当时真的是很不会,看了也是一知半解,迷迷...

1929

扫码关注云+社区