多少抽象太多了?


75

在面向对象的程序中:抽象太多了?多少才对?

我一直都是个小人物。我了解高级封装和抽象背后的概念,但总是本能地认为添加太多内容只会使程序感到困惑。

我总是尝试拍摄一些抽象的东西,而没有留下任何空白的类或层。如有疑问,我将尝试在现有层中添加一些内容,而不是在层次结构中添加新层。

但是,最近我遇到了更多高度抽象的系统。一种系统,其中可能需要在层次结构中稍后进行表示的所有内容都预先放置了。这导致了很多空层,一开始似乎是不好的设计。但是,经过深思熟虑,我逐渐意识到,保留那些空的层将使您有更多的空间可以在将来使用而无需进行大量重构。它使您拥有了在旧功能之上添加新功能的更大能力,而无需进行几乎相同的调整旧功能。

这样做的两个风险似乎是您可能弄错了您需要的图层。在这种情况下,仍然需要进行大量的重构以扩展代码,并且仍然会有大量的未使用层。但是,取决于您花费多少时间来进行初始抽象,将其搞砸的机会以及如果正确就可以在以后节省的时间-仍然值得尝试。

我能想到的另一个风险是过度执行该风险而永远不需要所有额外层的风险。但这真的很糟糕吗?额外的类层真的那么贵吗,如果不使用它们,会造成很大的损失吗?这里最大的开销和损失将是时间,这些时间是与各个层一起预先损失的。但是,当人们可以使用抽象代码而不是更底层的代码时,大部分时间仍可以节省下来。

那么什么时候太多呢?在什么时候空层和多余的“可能需要”抽象变得过大?多少太少?最好的地方在哪里?

您在职业生涯中是否找到可靠的经验法则来帮助您判断所需的抽象量?


8
一段绳子有多长???;)
Goz

2
应该是CW恕我直言,因为它没有一个答案
二进制杞人忧天

4
为什么Java应用程序比C#倾向于具有更多抽象?
2013年

@Goz取决于语言,例如Pascal说它确实有最大长度,但是聪明的(c)人知道它没有。
jave.web 2014年

太多的抽象是死亡游行的内容。我正在研究一个大型的旧应用程序,该应用程序是如此抽象,以至于当在界面上单击按钮时,它实际上要花费数小时才能找到执行的代码。该应用程序以非常有害的方式结合了WPF,依赖注入和模拟。过多的抽象是维护代码库的障碍。这是一个重要的话题,应该加以讨论。太多的抽象实际上会使好的公司和组织陷入一片废墟。这并不夸张。
Doug Kimzey

Answers:


23

那么什么时候太多呢?在什么时候空层和多余的“可能需要”抽象变得过大?多少太少?最好的地方在哪里?

我认为这些问题没有明确的答案。需要经验来发展对“太多”和“太少”的感觉。也许某些度量或质量控制工具的使用可以有所帮助,但是很难一概而论。这主要取决于每种情况。

以下是一些可能会激发您寻求答案的链接:

开发就是要在任何软件工程工作中所存在的各种紧张关系之间找到适当的平衡。


7
+1每次您考虑添加抽象层时,请考虑其好处。仅出于抽象考虑,抽象没有意义。
CiscoIPPhone

1
这是关于使框架过于通用的危害的流行说法。有趣的事! describe.joelonsoftware.com/default.asp?joel.3.219431.12
Ben Power

30

抽象的目的是要从特定属性中排除公共属性,例如在数学运算中:

ab + ac => a(b + c)

现在,您通过两个操作而不是三个操作来执行相同的操作。这个因素使我们的表达更加简单。

文件系统是抽象的典型示例。例如,您希望程序能够写入多种存储设备:笔式驱动器,SD卡,硬盘驱动器等。

如果没有文件系统,则需要实现直接磁盘写入逻辑,笔驱动器写入逻辑和SD卡写入逻辑。但是所有这些逻辑都有一个共同点:它们创建文件和目录,因此可以通过创建抽象层并为硬件供应商提供接口来执行特定工作来抽象化这些共同点。

事物具有共同属性的越多。抽象可能更有益:

ab + ac + ad + ae + af

至:

a(b + c + d + e + f)

这会将9次操作减少到5次。

基本上,每个好的抽象都将系统的复杂性大致降低了一半。

您总是至少需要两件事共享一个公共属性才能使抽象有用。当然,您可以将一件事情拆散,使其看起来像是抽象,但这并不意味着它有用:

10 => 5 * 2

如果您只有一个实体,则无法定义单词“ common”。

因此,回答您的问题。如果它们使您的系统尽可能简单,则您将拥有足够的抽象。

(在我的示例中,加法连接了系统的各个部分,而乘法定义了抽象与具体的关系。)


该答案描述了使用抽象使代码保持DRY的方式,但没有涉及抽象的其他重要/有意义的用法-例如SOLID或GRASP模式,以及强制将关注点分离或使用一致性的元编程合同。
jchook '16

这是完全不正确的。如果您编写的代码不会在一行中重复出现,那么您将花费大约1年的时间,而打算在完成工作后为其提供维护的人将花费另外半年的时间来理解您的代码。因此,这是完全相反的:您添加的每个抽象->都会增加系统的复杂性。
纽力克

27

多少太少?

当您经常使用“低级”元素时,您会不断感到自己不想这样做。抽象他们。

那么什么时候太多呢?

当您无法定期制作一些代码部分的片段时,必须将它们调试到上一层。您会觉得此特定层没有任何贡献,只是一个障碍。算了吧。

最好的地方在哪里?

我喜欢运用务实的方法。如果您需要抽象并了解抽象将如何改善您的生活,那就去做吧。如果您听说过应该“正式地”增加一个抽象层,但是您不清楚原因,那就不要做,而要先研究。如果有人坚持要抽象一些东西,但不能清楚地说明会带来什么,请告诉他们走开。


11

简而言之,如果代码难以理解,则存在太多的抽象。

现在,这并不是说您应该对所有内容进行硬编码,因为这是最容易编写和读取的代码。

最简单的测试是将其放下几天,然后重新拾起并问问自己,这样做有意义吗?更好的方法是将其提供给其他人,看看他们是否可以将它做成正面或反面。


2
谁能理解“如果代码难以理解,那么就会有太多的抽象”。我已经开始研究极其复杂的代码库,然后在几个月/几年后就可以轻松找到自己的解决方案。另外,我还处理了硬编码的东西,如果不插入几个有意义的抽象就无法理解。
Binary Worrier,2010年

2
我的意思是,如果由于抽象而使代码难于处理,那就太多了。我并不是说很难理解没有抽象的代码,但是您应该可以看一下代码,然后说:“好吧,A点转到B点。这很有意义。”
kemiller2002

我同意凯文。例如,有时我创建一个shell脚本来执行重复的工作,但是后来我忘记了该脚本的工作方式,因此最终只使用基础命令。例如,使用“ du”脚本:执行脚本需要在哪个目录中?将输出文件放置在当前目录中吗?脚本是异步还是阻塞?(我拥有的称为disk_usage的确切脚本基本上是“ nohup du> disk_usage_recursive.txt&”,但我最终只是在每种类型中都键入了此命令)。
Sridhar Sarnobat 2013年

我同意。我看过用2个函数和2个变量创建抽象(类)的代码。这可以通过参数来完成,但是不可以,开发人员可以通过一个接口对其进行抽象,只是为了更改一些布尔值。
Nulik

9

从理论上讲,这应该是仅使用三个(相当简单)变量的简单数学问题:

  • S =使用节省
  • C =额外抽象的成本
  • P =使用概率

如果S * P> C,则代码是好的。如果S * P <C,则不好。

但是,纯粹出于理论上的原因是,您通常无法猜测使用的可能性或使用它所节省的成本。更糟糕的是,您无法猜测甚至通常无法衡量其存在的成本。

至少有人由此得出结论。在TDD中,标准的口头禅是“您将不需要它”(YAGNI)。简而言之,任何不直接对满足其当前要求的代码做出贡献的事情都是一件坏事。从本质上讲,他们得出的结论是,使用的可能性很低,以至于没有必要添加这样的额外代码。

其中一些返回到“自下而上”与“自上而下”的开发。我倾向于将自下而上的开发视为“库开发”-即,您不是在开发特定的应用程序,而是在为应用程序中所需的各种东西开发库。这种想法是,有了一个足够好的库,您就可以相对轻松地开发几乎任何该通用类型的应用程序。

一点也取决于项目的规模。与那些被丢弃和替换得更快的小型项目相比,数十年仍可使用的大型项目证明了更多的长期投资合理性。这在现实生活中也有明显的类似物。您不必担心用不到一星期就扔掉的一次性剃须刀的合身性,表面处理效果或做工方面的麻烦,而不必像购买未来几年将要使用的新车之类的东西那样担心。


2

现实情况是,这取决于您对未来的展望。您要计划可以预见的更改,而无需创建太多额外的层。如果您设计的是在系统之间传输数据,请继续创建一个接口,并将计划的实现用作默认设置。例如,您使用FTP来移动文件,但是知道该标准明年将基于消息(或其他任何消息)。

至于设计中的图层,有时添加的图层使编写较小的类更容易。如果意味着具体的类变得简单明了,可以添加概念层。


1

请参阅RFC 1925的项目(6a),并知道它确实是正确的。通过添加抽象层无法解决的唯一问题是由过多的抽象层引起的问题。(尤其是,每一个抽象都使整个事情变得难以理解。)


1

实际未使用的每个抽象都太多了。系统越简单,更改就越容易。抽象层几乎总是使系统更加复杂。

OTOH,无需任何抽象就可以对复杂的,难以维护的混乱进行编程,而且有时,“标准化”抽象层可以比大多数人自己能够更好地帮助构建系统。

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.