如何确定抽象级别


35

今天我正在读一本书,叫做“ Clean code”,在作者谈论每个函数的抽象级别时,我遇到了一段,他将某些代码分类为低/中/高抽象级别。

我的问题是确定抽象级别的标准是什么?

我引用书中的段落:

为了确保我们的函数正在做“一件事情”,我们需要确保我们函数中的语句都处于相同的抽象级别。很容易看到清单3-1是如何违反此规则的。其中的概念具有很高的抽象水平,例如getHtml();。其他处于中间抽象级别的其他变量,例如:String pagePathName = PathParser.render(pagePath); 还有其他一些非常低的级别,例如:.append(“ \ n”)。


Answers:


27

作者解释说,在讨论抽象的部分的“从上至下阅读代码”小节中:

[...]我们希望能够像读取一组TO段落一样阅读该程序,每个段落都描述了当前的抽象级别,并在下一层向下引用了后续的TO段落。

  • 为了包括设置和拆卸,我们包括设置,然后包括测试页面内容,然后包括拆卸。
    • 为了包括设置,如果这是套件,则包括套件设置,然后包括常规设置。
      • 为了包括套件设置,我们在父层次结构中搜索“ SuiteSetUp”页面,并添加包含该页面路径的include语句。
        • 要搜索父母...

随之而来的代码将是这样的:

public void CreateTestPage()
{
    IncludeSetups();
    IncludeTestPageContent();
    IncludeTeardowns();
}

public void IncludeSetups()
{
    if(this.IsSuite())
    {
        IncludeSuiteSetup();
    }

    IncludeRegularSetup();
}

public void IncludeSuiteSetup()
{
    var parentPage = FindParentSuitePage();

    // add include statement with the path of the parentPage
}

等等。每次深入函数层次结构时,都应该更改抽象级别。在上面的示例中IncludeSetupsIncludeTestPageContentIncludeTeardowns都处于相同的抽象级别。

在书中给出的示例中,作者建议将大功能分解为非常具体的小功能,并且只做一件事。如果操作正确,则重构后的功能将类似于此处的示例。(重构版本在本书的清单3-7中给出。)


10

我认为要了解此问题,您需要了解什么是抽象。(我太懒了,找不到一个正式的定义,所以我敢肯定我会被迷住了,但是在这里...)抽象是当您采用一个复杂的主题或实体并隐藏其大部分细节时同时展示仍定义该对象本质的功能。

我相信这本书给你的例子是一所房子。如果仔细看一下房子,就会发现它是由木板,钉子,窗户,门组成的...但是,尽管旁边没有照片,但它的卡通图画仍然是房子。其中许多细节。

与软件相同。就像本书所建议的那样,无论何时进行编程,都需要将软件视为分层。一个给定的程序很容易超过一百层。在底部,您可能具有在CPU上运行的汇编指令,在更高级别上,这些指令可以组合在一起以形成磁盘I / O例程,在更高级别上,您不需要使用磁盘I /。直接使用O,因为您可以使用Windows功能简单地打开/读取/写入/查找/关闭文件。这些都是抽象,甚至在您获得自己的应用程序代码之前。

在您的代码中,抽象层继续。您可能具有较低级别的字符串/网络/数据操作例程。在更高级别上,您可以将这些例程组合到定义用户管理,UI层,数据库访问的子系统中。这些子系统的又一层可以合并到服务器组件中,这些服务器组件一起成为更大的企业系统的一部分。

这些抽象层中每一层的关键在于,每个抽象层都隐藏了上一层所暴露的细节,并提供了一个非常干净的界面,供下一层使用。要打开文件,您不必知道如何编写单个扇区或要处理的硬件中断。但是,如果您开始沿抽象层链向下移动,则肯定可以跟踪Write()函数调用,一直到发送给硬盘控制器的确切指令为止。

作者要告诉您的是,当您定义类或函数时,请考虑一下自己是哪一层。如果您有一个用于管理子系统和用户对象的类,则该类不应执行低级字符串操作,也不应该包含仅用于进行套接字调用的一堆变量。这将违反跨抽象层的行为,而且违反一种类/功能只能做一件事(SRP-单一责任原则)。


2

我的问题是确定抽象级别的标准是什么?

该抽象级别应该是显而易见的。如果它是问题域的一部分,而不是编程语言的一部分,那么它是抽象的。很难比“高度抽象” ==“不真实” ==“问题域”更清楚。和“不是抽象==具体==语言的一部分”。确定抽象级别应该很简单。根本不应该有任何微妙之处。

.append("\n")不是抽象的。它只是将一个字符放在字符串上。那将是具体的。不是抽象的。

String pagePathName = PathParser.render(pagePath);处理字符串。具体的东西。部分关于具体的编程语言功能。部分使用抽象的“路径”和“解析器”概念。

getHtml(); 抽象。处理“标记”和非琐碎,具体的语言功能。

摘要==不是语言功能。

具体==语言功能。


1
如果您将抽象定义为一种描述程序员创建的事物的质量,即程序员生成的抽象,那么我倾向于同意您的词语定义。但是所有编程语言都是对更具体事物的抽象。编程语言的选择在很大程度上决定了您从什么级别开始。
罗伯特·哈维

1

我认为抽象级别很简单...如果代码行未直接实现方法的单一职责,则它是另一个抽象级别。例如,如果我的方法名称为SaveChangedCustomers(),并且将所有客户列表作为参数,则唯一的责任就是将列表中已更改的所有客户保存:

foreach(var customer in allCustomers)
{
    if (CustomerIsChanged(customer)
        customer.Save();
}

通常,您将找到确定客户是否已更改嵌入foreach循环的逻辑,而不是调用CustomerIsChanged()方法。确定客户记录是否已更改不是此方法的责任!这是不同的抽象级别。但是,如果确定的逻辑仅仅是一行代码呢?没关系!!!它是一个不同的抽象级别,需要在此方法之外。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.