50

我最近发现自己处于解释我写给我公司喜欢雇用的两名​​候选人的(内部)应用程序的位置,以协助维护和添加次要功能。

这是我编写的第一个“生产”应用程序,它有 45k LOC,我花了将近两年的时间“单独”开发它。我还很年轻(18 岁),从头开始编写应用程序,同时被签约为一名离开公司的前开发人员的替身。在设计这种规模的应用程序方面缺乏经验,我尝试使用常见的架构和设计模式。

今天我知道我做了一些严重的过度工程,例如使用断开连接的更改跟踪架构而不是所选 ORM 已经实现的工作单元模式。我可能永远不必去“真正的”三层。

两位候选人在相关平台的内部应用程序开发方面都有 10 年以上的背景。作为他们的一半年龄并且没有什么经验,我确实尊重他们的意见。当我向他们解释应用程序架构时,评论大致如下:

  • 天哪,没有人会付钱让我做那样的事情,我必须把事情做好
  • 坚持框架的功能,不要使用花哨的库/技术
  • 不要包装框架代码。在一个团队中,无论如何,每个人都会编写自己的包装代码。
  • 您使用的是 .NET 3.5?好吧,我们使用的是 2.0。
  • 那些 LINQ 的东西给我带来了什么?所有这些查询组合和投影似乎都太复杂了。

现在我问自己:
我是一名建筑宇航员吗?我怎么知道我在建筑方面走得太远了?过度工程的常见症状是什么?

4

17 回答 17

130

过度工程的常见症状是什么?

解决您没有的问题的代码。

于 2009-12-21T18:57:04.047 回答
28

过度工程的一个非常强烈的警告信号是,当一切都经过如此多的间接性时,很难找到真正实现某些具体的域级功能的代码段。如果你发现你的大部分函数只做很少的具体工作而只是调用其他虚函数,你可能会遇到问题。

于 2009-12-21T23:36:52.200 回答
22

无聊

无聊是过度设计代码的良好前兆。我承认,当我得到第一份工作时,我觉得自己没有得到充分利用。我只是觉得无聊。当我感到无聊时,我会编写代码。不仅仅是任何代码——代码的大教堂。

不认真,我对我的代码和抽象有一个脑海中的画面,就像带有金色突出尖顶的大塔,玻璃缟玛瑙的飞扶壁,一个由拱形圆顶支撑的美妙拱顶,上面有漂亮的几何窗饰等等。

看到这些模式一起为我自己工作真是令人着迷,但回想起来,我完全为自己留下的不敬虔的混乱感到羞耻。

如果您正在编写自己的框架和 DSL 代码来消磨不那么刺激的工作时间,那就停下来。最好把时间花在阅读Wards Wiki或写一本开源书籍上,或者您可能只是想要求管理层做更多的工作。

于 2009-12-21T20:25:55.537 回答
11

至于你是否是一名建筑宇航员的问题:如果你意识到让你领先于很多人的危险。你也不想走你的牛人的路,听起来他们中的一些人已经变成了老顽固的抱怨者。

过度工程是优先级问题的结果,导致系统的某些部分受到过多关注。因此,过度设计最明显的症状是您可以看到系统的其他部分因缺乏关注而受到伤害。

(过度设计也有一种趋势,使系统面临更大的不良设计风险,因为在决定过度设计哪些方面时会增加复杂性和容易出错的推测数量,但正如评论指出的那样,这不会自动跟随。)

于 2009-12-21T18:50:23.847 回答
11

编写自己的框架

奇怪的是,有人已经这样做了。更重要的是,他们已经做得比你做得好 1000 倍。不仅如此,他们所做的任何事情都可能已经成为行业标准,因此学习技术将使您在其他工作中更具竞争力。

在我工作的最后一家公司,一名程序员在他的大部分任期内都独自完成他的项目。他编写了公司最受欢迎的应用程序之一,并被广泛认为是团队中最好的——但在我看来,他有一个讨厌的习惯,即从头开始编写他需要的一切。

他编写了自己的依赖注入框架,他自己的 ORM,一个单元测试框架(莫名其妙地,它看起来和行为都与 NUnit 非常相似——他为什么不使用 NUnit?),一个用于创建工厂对象的框架(一个“工厂工厂”我称之为)。

请注意,代码实际上非常出色,但重点是什么?

编写更好的核心库

在我现在的公司,程序员似乎总是编写大量无用的代码来复制 .NET 框架中已经存在的功能。

除其他外,他们写道:

  • 用于 ASP.NET webforms 中表单身份验证的活动目录框架 - 莫名其妙,因为 ASP.NET 内置了此功能。
  • 网站的可热交换主题和皮肤——同样莫名其妙,因为代码的功能不如内置的 ASP.NET 主题,并且需要多 1000% 的膨胀。
  • 他们莫名其妙地编写了自己的类型化数据集和数据适配器。这些对象提供的功能少于 VS 将为您自动生成的类型化数据集,同时需要比 NHibernate 域对象更多的样板代码。

要么他们不太了解这个框架,要么他们认为它是出了名的不足。

我能想到的例子很少,重新实现的库比原始库更好(参见Jane Street Core LibraryC5 Generic Collections for .NETa real currency class),但很可能,你不会写更好的标准库。

于 2009-12-21T20:37:37.997 回答
10

对于大多数内部业务应用程序,您的大部分代码应该关注实现业务问题,而不是与业务无关的技术问题(例如您的“断开连接的更改跟踪架构”)。当前可用的框架非常成熟,支持最常见的用例。如果您正在发明新技术或(在业务应用程序开发的上下文中)只是为了包装而包装一些其他现有框架或库,那么您可能做错了。理想情况下,您构建的每一块架构都应该可以追溯到某些业务需求。把事情简单化。

于 2009-12-21T19:28:29.747 回答
9

恕我直言,您收到的关于您的应用程序的大多数评论实际上并不是关于过度工程,因为过度工程与技术无关。这是关于建筑的。新技术可以在合理的时间内学习和理解。理解过度设计的应用程序通常要困难得多,有时甚至是不可能的。这使得第 2、4 和 5 点无效。第一点并不真正有效,因为您显然是按原样编写应用程序而获得报酬的,如果它有效,那么您在这里没有问题。

这是我的“快速测试”,以确定应用程序是否倾向于过度设计:

  • “一切”的包装器:包装器很有用,但很容易过度使用。检查你是否只包装真正需要包装的东西。(我基本上包裹了自己的包装纸一次。我知道我在说什么;-)。)
  • 重新发明轮子:经典。这很常见,您已经提到过。你是否实现了一些功能,因为你需要想要的?您的框架做了哪些其他可用库没有做的事情?
  • “感觉”过度设计:这是最重要的一点,也是最难看到的一点。看看你的代码,看看哪些部分感觉过于复杂。然后问问你自己是否有更简单的方法来实现它,以及为什么你没有选择这种方式。如果你没有得到好的答案,这部分可能是过度设计的。

这些只是我用于我的应用程序的快速提示。不能保证它们是“过度工程检测”的全部和全部。

于 2009-12-21T20:22:42.523 回答
9

当一个同事的电脑飞过你的脑袋,因为他们在过去的 6 个小时里一直在尝试使用你荒谬的框架出现一个可怕的对话框,但没有成功,这就是一个非常具体的症状。

于 2009-12-22T00:11:15.490 回答
8

在查看过度设计的事物时,会想到避免使用YAGNIDRYKISS 。如果有许多部分似乎已部分完成,并且许多代码部分似乎有一个,“如果发生这种情况怎么办?如果发生这种情况怎么办?” 感觉它,那将是另一点。忽略OO 设计的良好原则SOLID 原则将是另一个需要注意的问题。如果您认为自己编写了完美的代码,那将是另一个麻烦的迹象,因为很少有人编写无法以某种方式改进的东西。

IMO,请注意,有些人可能对您的工作过于挑剔,因为任何代码库的一部分都可能涉及人们以某种​​方式喜欢事物,例如方法、测试和变量的命名约定。就是那样子。现在,您可能需要弄清楚如何在冲突说服/影响等情况下处理人,在这些情况下,有一些工具可以提供帮助。

于 2009-12-21T18:37:22.547 回答
7

为您的应用程序提供内在功能的插件

让我们面对现实吧,可插拔架构简直是太性感了,写起来也很有趣。但是,这是您需要问自己的另一个领域,“我真的需要这样做吗?”。

如果您不需要对应用程序进行临时添加,不要指望任何人编写第三方扩展,并且您的应用程序的范围相当明确,那么您不需要可插拔架构。

您不应该编写插件来支持应用程序中的内在功能。假设您正在编写一个 Paint 程序;您可能会支持插件以多种格式保存文件,但您不需要可插入的撤消管理器或文件浏览对话框。

于 2009-12-21T23:36:54.203 回答
5

你不是建筑宇航员。一方面,LINQ 非常简单、基本且有用。.NET 3.5 也是如此。

同时,你是团队中的新手,即使他们喜欢你的所作所为,也会受到一些嘲笑。

用一粒盐把这一切都拿走。接受他们的批评,点头,然后和他们一起喝杯啤酒。

如果他们要求您更改它,那么您的评论是“jeez .. 我知道我做错了,但它有效,而且更改起来太麻烦了”。

于 2009-12-21T20:26:46.910 回答
3

尝试评估你是否做了一些事情来最小化代码预期生命周期内的工作。这包括维护和开发。

在代码生命周期中,请记住代码的生命周期与应用程序的生命周期不同。最好先编写一个快速原型,然后在更好地理解领域后重新设计/重构。在这种情况下,生命周期非常短,因此请保持简单。

此外,不同的应用需要不同的工程量。如果这个应用程序失败了,它会以生命为代价吗?即它是机械心脏的控制器,还是航天飞机导航火箭的控制器?还是无聊青少年的联系人列表?

于 2009-12-22T09:11:12.630 回答
3

在更通用和更高级别的方法上,

I feel that when there's a gap between the complexity of the problem and
the complexity of the solution, then you have a clear case of overengineering. 

有什么方法可以做到这一点?解决你没有的问题,看到问题实际上更复杂,试图预测未来太多,构建太通用的东西等等。

于 2014-10-29T11:55:49.380 回答
2

您是否在合理的时间范围内实施了您的项目?它现在工作吗?如果是这样,您可能可以放松一点,因为过度工程最严重的问题之一是实际上从来没有完成任何有用的事情。

你谈话的人也可能有一些很好的建议,但这并不意味着你需要把他们所说的一切都当作福音。例如,在一个新项目上使用最新版本的 .NET 并没有让我感到担心。他们真的在抱怨吗?

于 2009-12-21T19:16:56.743 回答
2

其中一半是老人(我在他们的联盟中)坚持他们所知道的久经考验的真正蒸汽机。另一半是真的,但并没有真正告诉你任何新的东西。

除此之外,Jeff Sternal 的回答非常出色。

于 2009-12-21T20:49:42.250 回答
1

作为一个已经专业开发软件 24 年的人,我只能说这个问题 - 对特定问题所需的抽象级别进行调用 - 是我日常设计和编程工作中最困难的部分。

在“旧”时代,工程不足很普遍,这导致了所有结构化的方法和实践。现在,我们似乎走上了另一条路。没有简单的答案,有时你只会在事后才知道你是设计不足还是过度设计;)

我有很多事后的经历,我对自己说:“我希望我没有把这件事复杂化”,但也“如果我能听到脑后的那个小声音,让它变得更复杂一点就好了”这里是通用的”。

我还没有提炼出打电话的绝对规则……

我怀疑我容易过度设计,所以我把这张照片作为我电脑的壁纸

于 2015-03-22T14:08:59.047 回答
0

有点想提供一个不同的观点,但我认为这个词应该被删除,取而代之的是更合适的描述。当许多被描述为“过度工程”的东西具有完全相反的品质时(好像工程太多,或者把它看得太认真) ,应该产生最复杂的解决方案)。例如,通常会看到过度工程和糟糕的测试程序之间的相关性(至少我经常看到这两者并存),以及什么样的工程过度执行最少的正式测试?

我说,从我经常听到软件经理和偶尔的不那么精通 SE 的恐龙的角度来看,他们将这个术语扔给任何试图降低系统维护成本或提出更完善的测试程序和安全标准。外行人很容易认为,任何对声音工程、测试的认真关注,以及不以他们想要的 sprint-like snap-snap-snap 速度生成代码都是“过度工程化”。

有很多人真正认识到“过度工程”的真正味道,但这个词至少可以说是具有误导性的。

至于症状,除了已经提供的优秀之外,对我来说,能够正确评估和重新评估您面前的工作是一个盲点。程序员可以在他们的想象中构建世界,沉迷于高耸的概念。虽然这是一个无聊而笼统的答案,但这可能导致我们不再看到实际问题和优先事项就在我们眼皮底下的危险。概念化太深往往会在我们面前形成一个盲点,使问题过于复杂,与现实和实际的用户端需求脱节。对我来说,需要注意的最大症状围绕着心理学。

围绕生产的各种职业都容易受到这种基本趋势的影响,即由于过于痴迷于概念(例如:电影导演)而忽略了需要解决的实际和直接问题。对于一般的编程,对抗它的一种方法是支持朴素、简单、实用,在将某些东西概念化到第 n 级之前尽早编码(在我们过分爱上任何想法之前为我们争取更多时间重新评估)。拥抱这些理想不会错得太远,但对我来说,关键是要避免形成阻碍我们正确评估自己工作的盲点。过度设计的代码库不会在一夜之间产生——它需要在错误的方向上进行大量的专门工作才能产生它,

于 2015-11-17T20:00:36.153 回答