【问题标题】:Concrete symptoms of over-engineering [closed]过度工程的具体症状[关闭]
【发布时间】:2010-12-28 20:17:15
【问题描述】:

我最近发现自己正在解释我写给我公司喜欢雇用的两名​​候选人的(内部)申请,以协助维护和添加次要功能。

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

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

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

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

现在我问自己:
我是architecture astronaut 吗?我怎么知道我在建筑方面走得太远了?过度设计的常见症状是什么?

【问题讨论】:

  • 这可能是作为一个社区 wiki 的最佳选择 - 对于什么是过度工程可能没有广泛的共识。
  • 有时拥有 10 年以上经验的人是团队中最好的,有时他是职业业余爱好者。有时没有学位的高中生在高年级表现出色,有时他们一生中从未编写过一行代码,并认为编程看起来像这样:youtube.com/watch?v=gwlE1aASc4g。不管它的价值是什么,我们都容易过度设计。我不会汗流浃背,特别是如果那些人还没有真正看过真正的代码的话。只需进行几次大扫除即可删除任何看起来像 DailyWTF 材料的内容并称其为好。
  • 同意 Juliet - 如果你正在从中学习并且它有效,那么不要担心。几乎每个人都变得过于复杂,然后随着您获得更多经验而弄清楚如何以更简单的方式进行编码。至少你想过……

标签: architecture infrastructure


【解决方案1】:

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

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

【讨论】:

  • 您是否同意过于明显地违反 DRY 是“工程不足”、缺乏设计的标志?
  • 当然,违反 DRY 是“工程不足”的标志,缺乏规划。我描述的一些内容是为什么重构有时会如此有用。
【解决方案2】:

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

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

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

【讨论】:

  • 对于单个“更改单元”有多个需要更改的地方并不是过度工程的症状,而是紧密耦合/糟糕的设计/工程不足
  • 这是一个很好的观点,我认为过度设计意味着系统的某些部分得到了过多的关注,结果是系统的其他部分缺乏关注。当我看到过度设计的东西时,通常会有其他部分设计不足。
【解决方案3】:

常见的症状是什么 过度设计?

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

【讨论】:

  • 如我所说,某些基础设施过于矫枉过正。我应该考虑进行(可能是危险/昂贵的)重构吗?
  • 我同意 Juliet 的观点——除非它妨碍扩展或维护您的代码,否则您无需费力。但值得在未来与冲动抗争!
  • 它能解决真正的问题吗?
  • 由此得出的结论可能是“代码会产生以前没有的问题”。 @Just它可能解决一个真正的问题,但这个问题值得解决甚至相关吗?
  • @Johannes,你能请人与你合作进行重构吗?这将是我的建议,因为这给重构带来了一些好处:其他人了解您所做的事情,您了解其他人如何完成工作,并且应用程序得到改进,从而获得 3 个好处。
【解决方案4】:

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

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

【讨论】:

  • 同意。任何关于使用 v3.5 和 LINQ 的抱怨似乎根本不是抱怨,它们似乎是承认不知道或以前使用过它们。这很好,但不应该是投诉
  • 不,他们没有抱怨。这更像是Funka所说的。该应用程序已经投入生产将近半年了,用户和产品所有者都很高兴。考虑到我的经验和情况(合同类型、访问学校等),时间表得到了合理的满足
【解决方案5】:

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

【讨论】:

    【解决方案6】:

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

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

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

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

    【讨论】:

    • LOL 在“包装你自己的包装”!但是恕我直言,包装器通常使代码更具可读性,所以我相信如果你过度使用它是可以的。
    • 有时候,如果你有时间,你应该重新发明轮子。很高兴知道某些东西为什么会转动,而不仅仅是它会转动。
    【解决方案7】:

    无聊

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

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

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

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

    【讨论】:

    • 我可以用它作为域名吗?Cathedralsofcode.com?太美了。
    • 这句话“代码大教堂”一直困扰着我多年,以至于我只是用谷歌搜索找到我在哪里读过它并回到这里:)
    【解决方案8】:

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

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

    对这一切持保留态度。接受他们的批评,点头,然后和他们喝杯啤酒。

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

    【讨论】:

      【解决方案9】:

      编写自己的框架

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

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

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

      请注意,代码实际上很了不起,但重点是什么?

      编写更好的核心库

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

      除其他外,他们写道:

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

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

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

      【讨论】:

      • 哦,关于“更好的核心库”评论,一位 C++ 程序员曾经向我解释说,内置的字符串类非常糟糕,以至于编写自己的字符串实际上是一种通过仪式类。
      • 我是那个有时间做他所做的事情的人。而且,我敢打赌他知道为什么事情会如此运作,而且我敢打赌他将来会更好地挑选现成的东西。有时,您需要知道如何从头开始做事的人。这是否有助于他的底线或公司的底线是另一个问题。
      • 如果说“写得比以往任何时候都好 1000 倍”,但没有暗示“考虑到您在办公室工作的时间”,这可能是不公平的。可能是它好 1000 倍的唯一原因是他们有时间投入,而不仅仅是无聊/时间填充的编码
      【解决方案10】:

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

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

      【讨论】:

        【解决方案11】:

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

        【讨论】:

          【解决方案12】:

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

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

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

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

          【讨论】:

            【解决方案13】:

            当一个同事的计算机因为过去 6 个小时试图使用你荒谬的框架出现一个该死的对话框但未能出现而飞过你的头时,这就是一个非常具体的症状。

            【讨论】:

            • 嗯,这实际上是他们告诉我他们害怕的(他们还没有使用代码库)。它是一个 MDI 应用程序,并使用一些自定义包装函数来启动子对话框等。
            【解决方案14】:

            尝试评估您是否已采取措施在代码的预期生命周期内最大限度地减少工作量。这包括维护和开发。

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

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

            【讨论】:

              【解决方案15】:

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

              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. 
              

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

              【讨论】:

                【解决方案16】:

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

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

                我有过多次后见之明的经历,我对自己说:“我希望我没有把这件事弄得这么复杂”,但也“如果我能听到脑后的那个小声音,并且做到了就好了”这里更通用一点”。

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

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

                【讨论】:

                • 天知道为什么这有 0 票。太好了,即使在 6 年后,我也要感谢你。
                • @slugmandrew 很高兴为您提供帮助,伙计。我应该把那个桌面背景备份:D
                【解决方案17】:

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

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

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

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

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2019-04-26
                  • 2012-08-28
                  • 2011-02-22
                  • 1970-01-01
                  • 2012-05-02
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多