为什么我们在设计模式中需要这么多课程?

我是老年人的初级开发人员,我在理解他们的思想和推理方面苦苦挣扎。

我正在阅读域驱动设计(DDD),无法理解我们为什么需要创造这么多的课程。如果我们遵循这种设计软件的方法,我们最终会得到20-30个类,最多可以替换为两个文件和3-4个函数。是的,这可能很麻烦,但它更易于维护和阅读。

任何时候我想看看某种 EntityTransformationServiceImpl 做什么,我需要遵循许多类,接口,它们的函数调用,构造函数,它们的创建等等。

简单的数学:

  • 60行虚拟代码与10个类X 10(假设我们有完全不同的逻辑)= 600行凌乱的代码与100个类+更多来包装和管理它们;不要忘记添加依赖注入。
  • 阅读600行凌乱的代码=一天
  • 100个课程=一周,仍然忘记哪一个做什么,何时

每个人都说这很容易维护,但为了什么?每次添加新功能时,都会添加五个包含工厂,实体,服务和值的类。我觉得这种代码比乱码慢得多。

比方说,如果你在一个月内写出50K LOC杂乱的代码,DDD的东西需要大量的评论和更改(我不介意在这两种情况下进行测试)。一个简单的添加可能需要一周,如果不是更多

在一年内,你写了很多乱码,甚至可以多次重写它,但是使用DDD风格,你仍然没有足够的功能来与凌乱的代码竞争。

请解释。为什么我们需要这种DDD风格和大量模式?

UPD 1: I received so many great answers, can you guys please add comment somewhere or edit your answer with the link for reading list (not sure from which to start, DDD, Design Patterns, UML, Code Complete, Refactoring, Pragmatic ,... so many good books), of course with sequence, so that I can also start understanding and become senior as some of you do.

198
@ user1318496此 是上述问题的副本。你只是不知道你究竟在问什么。你的问题的本质与OO与函数式编程或设计模式以及与组织有关的一切无关。你真正想要答案的问题是,“为什么要将代码分成不同的原子单元?”。现在,尽管许多开发人员可能认为这个问题的答案是不言而喻的,但我认为这是一个非常有效的问题。许多开发人员正在以另一种形式挣扎于另一种形式,因为他们刚刚开始。
额外 作者 Vassilis,
“是的,这可能很混乱,但它更易于维护和阅读。”如果它凌乱,它怎么可读和可维护?
额外 作者 Bigballs,
@Jared:“哪个更容易测试?”:一如既往,这取决于什么样的测试。如果你有100个琐碎的类,并且所有的系统行为都是在它们如何链接在一起的,那么一般来说,生成的系统并不比简单的整体系统更容易编写集成和QA测试。你确实有100多个单元测试,断言“你还没有改变你的设计”,但那又怎么样? OP可能有夸大和/或代码可能没问题,但如果这个比例确实是每6行幼稚代码1个类来解决同样的问题那么有理由考虑它是否过度设计:-)
额外 作者 TraumaPony,
通过“解决同样的问题”我当然意味着完全解决,这是OP可能未能看到此代码库的作者看到的另一个领域。 600行代码大部分时间工作都与100个类不同,它们位于一个框架中,从外部证明了​​有用的定理,可以推断出代码实际的工作原理!
额外 作者 TraumaPony,
@EricLippert:如果具有100个类的代码具有易于向软件用户解释的功能,那么天真的代码没有,那么是的,这是一个很好的类比。我认为墙上的洞和玻璃窗之间的区别很容易归类为,“天真的解决方案实际上不是解决方案,因为它不能满足我们的实际用户要求”。因此,如果被黑客攻击的代码在一天内工作但无法集成到运行实际面向用户的服务的软件堆栈中,那么这就是编写更多类的示例灌篮参数。
额外 作者 TraumaPony,
@Jared:你已经打赌,100级比单块更容易测试。我只回应了这个说法。改变代码的难易程度是针对巨石的完全不同的论点:-)
额外 作者 TraumaPony,
应该注意的是,OOP不是一个通用的最佳工具,因此通过添加更多类(特别是继承)来解决问题的每个(部分)可能并确实导致不良的,不必要的复杂设计。问题是提出一个好的设计需要时间和经验丰富的开发人员,而且两者都很昂贵。如果它现在正在 ,那么商业通常是设计杂乱的。
额外 作者 Rorick,
你不这样做,除非你在做Java。当你拥有的只是Java时,一切看起来都像是一个类。
额外 作者 Krzysztof Klimonda,
简而言之,编写优雅的代码更加困难并且需要更长的时间,因此人们会选择不完整但结构严谨的代码。
额外 作者 Mr Rogers,
也许应该重新访问 one's 定罪的?
额外 作者 innaM,
@Polygnome:我打算输入完全相同的评论。初级开发人员的另一个选择评论特征是“我觉得这种代码比混乱的代码慢得多。”如果你花一半时间跑到墙上,那么你跑得最快就没有好处!慢是平滑的,光滑的是快的。
额外 作者 CodeCharming,
这个问题就像是在问“为什么我们需要这么多的部件才能建造一个窗户?所有这些术语都带有窗扇和腰带,双挂和三层玻璃窗,这太令人困惑了。如果我想透过墙看到我只是钻孔,在墙上钻一个洞,完成。为什么窗户制造商想让一切变得如此简单?
额外 作者 CodeCharming,
我开始对user1318496一般的类感觉一样。也许它更像是个人偏好而不是其他任何东西。
额外 作者 Graham,
@Graham语言功能在某种程度上取决于个人偏好,但这与此问题正交。功能代码也可以编写,以便于创建和轻松修改。
额外 作者 R. Schmitz,
@ 9000我认为你所说的将被称为COP - “面向类的编程”?如果你在没有给出关于实际拥有对象的声音的情况下添加类,那么你就不会做OOP ...
额外 作者 R. Schmitz,
我们真的不需要它们......
额外 作者 Vector,
我是唯一一个认为可能使用 EntityTransformationServiceImpl 来解决一个简单问题的20-30个类的人是否过度工程化?我猜你是指Java,它的文化是在设计模式中抛出设计模式,直到代码库难以理解......
额外 作者 Solomonoff's Secret,
我认为这里有一个很好的问题,但它隐藏在夸张和挫折背后。 @ user1318496,您可能会受益于稍微改写一下您的问题。
额外 作者 Warpspace,
@jamesqf也许你认为它是一种宗教皈依。我认为它是 Religion - > Religion 类型的函数。
额外 作者 Chad,
其中一些答案和评论正在相互讨论。我认为考虑两个不同的问题可能会有所帮助,例如: “为什么要使用DDD /设计模式/等而不是'简单'代码一般”(可测试性,解耦等)和“为什么这个特定的代码库使用比我想象的课程多得多吗?“ (过度工程,语言特定的解决方法(例如没有lambdas),过时的风格(例如Java现在有lambdas),货物结算,技术债务等。
额外 作者 Warbo,
我发现你的数学代码和他们的理解是非常可笑的。我真的很想看到你使用的数字背后的推理的详细说明。
额外 作者 Euphoric,
“阅读600行凌乱的代码=一天”。那是错的......我读课的时间呈指数增长,所有的分支和分支错误预测等......在600行,我处于舒适区的边缘。在1000,通常需要一个月才能完全理解课程....
额外 作者 ArTs,
F22 Raptor不是通过将宜家的固定装置仓库浇注到飞机形模具中而建造的。
额外 作者 ArTs,
您的域中是否存在这些类概念?如果没有,为什么它们存在?如果他们这样做,那么他们就有商业价值,对吗?如果你编写600行凌乱的代码而不是20-30代表域概念的类,那么你将来会有什么灵活性?
额外 作者 Rob Crawford,
OOP通常是反模式,纯OOP几乎总是反模式。 OOP往往会渗透到代码中的各种位置,尤其是可测试性。设计模式利用锤子(OOP)驱动你散落的所有这些钉子,过度使用OOP。如果你有一个名为 ProviderEntityProcessor 的类,你可能会过度使用OOP,但你肯定能找到很多人谁知道如何OOP指甲。
额外 作者 Rich Remer,
请注意,这种编写代码的特殊方式不是模式的问题,而是编写代码的人。你可以编写一个包含很多模式的丰富软件,可能有40或50个类;简单。但如果你过度设计......那么你最终会得到20个课程,这些课程可以用3个课程或4到5个程序功能完成。重要的技能是识别一个课程何时会被打破,以便它不会成为一个神级。
额外 作者 marstato,
在OP的防守中,他可能指的是一个很好的例子,就是用3或4个低值条件替换简单的开关,并且状态模式无处不在。我已经看到这种情况发生了,结果真的可以是一百个而不是一百行代码。维护也受到损害。
额外 作者 Sentinel,
无法相信没有人提到单一责任原则(如果你遵循它,往往会产生许多类)和一般的SOLID原则。
额外 作者 sizzlecookie,
@ user1318496 - 你不必担心,有很多成功的公司分享你的意见。他们一直在招聘,因为需要更多的开发人员来维护快速编写的代码.. :)
额外 作者 Fabio,
几十年前,我在一家商店工作,我们从(我认为)DOS 2.0升级到DOS 3.0。在第一个版本中,只有一个目录中包含所有内容。在后者中,引入了子文件夹。我的老板问:“为什么我们需要所有这些子文件夹?为什么我不能把所有东西放在一个文件夹中,所以我知道在哪里找到它?”美好时光。 :)
额外 作者 David Lang,
设计模式不仅仅是可维护性,还涉及可重用性。
额外 作者 Randy E,
冒着踩脚趾的风险,因为你的语言很糟糕。不要把它拿得比它更多:由于各种原因,一种糟糕的语言通常是正确的技术选择,但这并不能使它变得不那么糟糕。至于你的实际问题,正确性比可读性更重要。或者略微区别:可读性至关重要,因为它能够推断正确性。那么哪个更容易测试,你的100个班级还是你的单片神级?我打赌100个简单的课程。
额外 作者 Sujan,
@SteveJessop是的,测试系统在两种情况下都同样复杂。在多类案例中可能更是如此。不同之处在于:使用整体结构可能更容易找到其中进行更改,因此可能无限时间修复可能会破坏它的东西。在拆分代码库中找到正确的代码段更难,但是已知数量,并且进行更改是微不足道的。我宁愿自信而不是掷骰子,但YMMV和一个尺码并不适合所有人。对于任何小于10k行的东西,我可能更喜欢整体。也许。
额外 作者 Sujan,
@Theodoros Chatzigiannakis:这只是一次宗教皈依。问题在于对软件开发方法的不假思索崇拜。你只是改变了神性,但保持不假思索地坚持仪式。
额外 作者 Ruks,
@Rob Crawford:重新“...... 600行凌乱的代码与20-30个类......”,你在这里有两个不相关的东西。完全可以编写600行清晰,注释良好的代码,就像可以将其转换为20-30个凌乱的类一样。
额外 作者 Ruks,

10 答案

这是一个优化问题

一位优秀的工程师理解,没有目标,优化问题毫无意义。你不能只是优化,你必须优化 的东西。例如,您的编译器选项包括优化速度和优化代码大小;这些有时是相反的目标。

我想告诉我的妻子,我的桌子已针对添加进行了优化。它只是一堆,而且很容易添加东西。如果我优化检索,我的妻子会更喜欢它,即组织我的东西,所以我可以找到东西。当然,这使得添加变得更加困难。

软件是一样的。您当然可以优化产品创建 - 尽快生成大量的单片代码 ,不用担心组织它。正如您已经注意到的,这可能非常非常快。另一种方法是优化维护 - 使创建变得更加困难,但使修改更容易或风险更小。这是结构化代码的目的。

我建议成功的软件产品只会被创建一次,但会被修改很多次。经验丰富的工程师已经看到非结构化代码库本身就是一种生命,并且变成了产品,其规模和复杂性都在增长,直到即使很小的变化也很难在不引入巨大风险的情况下进行。如果代码是结构化的,则可以包含风险。这就是我们解决所有这些麻烦的原因。

复杂性来自关系,而不是元素

我在你的分析中注意到你正在研究数量 - 代码数量,类数等等。虽然这些都很有趣,但真正的影响来自于元素之间的关系,它们在组合上爆炸。例如,如果您有10个函数并且不知道哪个函数取决于哪个函数,则您必须担心90个可能的关系(依赖关系) - 十个函数中的每一个都可能依赖于其他九个函数中的任何函数,并且9 x 10您可能不知道哪些函数可以修改哪些变量或数据如何传递,因此编码人员在解决任何特定问题时都会有很多事情需要担心。相比之下,如果你有30个班级,但是他们被巧妙地安排,他们可以有29个关系,例如如果它们是分层的或排列成一堆。

这对您团队的吞吐量有何影响?嗯,依赖性较少,问题更容易处理;编程人员无论何时做出改变,都不必在他们的头脑中玩弄这些东西。因此,最大限度地减少依赖关系可以极大地提高您胜任推理问题的能力。这就是为什么我们将事物分成类或模块,并尽可能紧密地调整范围变量,并使用 SOLID 原则。

323
额外
@jpmc26什么是非平凡的OOP程序(例如> 10k loc)的例子,它使用最少数量的类并且易于维护和读取?此外,我不得不对阅读代码的复杂性持不同意见。即使没有将代码加载到IDE中,也很容易从 RestoreRunner.RunAsyncRestoreCommand 等等。我认为问题在于你错过了NuGets不是一个简单的列表,而是一个带有 PackageReference 的树,这些日子会让你的心智模型陷入困境。
额外 作者 Kyle Hodgson,
@Holger那么Mozilla多年来一直停止向Firefox添加新功能以完全重写他们的浏览器?他们没有。当然你可以替换代码的一部分并且有一个自然的进化,但完全重写是毒药(另一家公司那样做的是IE6和8之间的MS ......对他们来说效果不如Netscape好)
额外 作者 Kyle Hodgson,
@Holger你声称今天“只有从头开始重写或者甚至不存在那么久的浏览器”。这显然是错误的。我怀疑你会发现任何从头开始重写的流行产品,并且仍然像以前一样成功(因为你提到的例子不是真的,我怀疑你知道更好的一个)。自Netscape以来,唯一一个做类似事情的浏览器厂商是IE6之后的微软。他们从90%的大多数人变成了一个小型玩家,尽管将浏览器与地球上最流行的操作系统捆绑在一起......这非常成功。
额外 作者 Kyle Hodgson,
从头开始重写和第二系统综合症是杀死产品的必然方法,这就是为什么还没有人在做这件事。您正在做的是在实现新功能的同时改进现有代码。或者您可能决定根据需要重写应用程序的小部分(通常是安全性或性能,很少可维护性)。但是从头开始重写?乔尔的文章给出了很多很多原因,为什么这是一个如此糟糕的主意。
额外 作者 Kyle Hodgson,
但是,我要注意,过多的类和间接对于理解NOR维护并没有帮助。并且也没有复杂的控制流(也就是回调地狱)。
额外 作者 Owen,
“另一种方法是优化维护 - 使创建变得更加困难,但使修改更容易或风险更小。这就是结构化代码的目的。”我对隐含的概念提出了疑问,即更多的类=更具可维护性。特别是,跨越许多类的散布逻辑通常使得难以在代码中找到总体逻辑概念(特别是没有非常有意识地确保概念非常明显),这极大地降低了可维护性。
额外 作者 jmibanez,
我最近的例子是 NuGet的恢复命令。还原有三个基本步骤:读取包列表,下载它们,然后将它们提取到磁盘上。虽然这些步骤中的每一个肯定都有复杂性,但是很难找到代码中实现的位置,部分原因是对象数量相对较多,部分原因是显示这些概念似乎没有一直是命名和组织的优先事项。
额外 作者 jmibanez,
@Voo我无法弄清楚哪个对象启动了命令;这一切都被OO方法解析为命令行解析。一旦你弄明白它是 RestoreRunner ,你就会试着追踪它的“请求”是如何创建的,这会导致你通过大约4个方法调用,最终调用一个提供者对象,如果我正在阅读对的。寻找树并不奇怪,但只是找到它涉及太多层。我不聪明。我喜欢精心设计的代码,以揭示其背后的意图,而以对象为中心的方法会加剧糟糕的抽象。
额外 作者 jmibanez,
@JohnWu NuGet在模块化方面失败了。它的副库(如 NuGet.Frameworks )是完全没有记录(我甚至都没能找到他们构建的源代码。),命令行工具缺少很多VS插件的功能,而且看代码,需要构建这么多对象获得一个工作意味着你必须对整个代码库有一个非常广泛的理解才能使用它。它已经是有效的单片,所以我不买你的论点。
额外 作者 jmibanez,
@JohnWu除了这是对模式的普遍视图。 DI被虔诚地接受。我知道没有DI支持者说:“这就是解决方案看起来像DI一样好的问题,”他们说,“你应该编写OO代码,这意味着你应该有DI。哦,顺便说一句,你应该遵循SOLID原则“。实际上没有人谈论这些想法应该实际解决的问题。他们只是作为一直被认为适用的规则提供。
额外 作者 jmibanez,
@ R.Schmitz真正优秀的程序员首先不考虑规则。他们考虑解决问题。更重要的是,优秀的程序员会认识到其他程序员何时没有考虑过这种心态并将其推向它,而不是说出规则。
额外 作者 jmibanez,
@ R.Schmitz我说的是,真正优秀的程序员会反思这种想法。他们不认为,“您应该优化代码以提高可读性。”他们认为,“我们需要解决能够理解代码以便及时或其他开发人员进行更改的问题。”第一种是在没有解释问题的情况下提出解决方案。第二个说明问题并且对不同的解决方案保持开放,并且它有一个目标,当你成功或不成功时(即使它仍然是主观的)它更清晰。
额外 作者 jmibanez,
我会为组合使用10×10 = 100:这些函数很可能是递归的,而且特别糟糕的代码,显然不是那么明显。
额外 作者 LessIsMore,
写得很好但是它可能会错过为什么“一次创建但很多次修改”很重要? (如果所有代码都在 EntityTransformationServiceImpl 中,那么在您修复之前,您将被迫学习整个工作原理 - 但是如果格式有问题,那么 Formatter 类处理它,你只需要学习该部分是如何工作的。。在3个月后阅读你自己的代码就像读一个陌生人的代码一样。)我觉得最像我们下意识地想到了这一点,但这可能是因为经验。不是100%肯定这个。
额外 作者 R. Schmitz,
@Holger不,因为在添加新代码时,之前的代码并不像您的编写标准那么重要。如果软件使得添加代码变得非常昂贵,那么那只是凌乱的代码。您可以拥有一个完美的模块化项目,但添加一些快速简便的代码。你也可以在一个神级中拥有一个完整的程序,但是要考虑如何使你的加法可以在以后维护。关于你所说的那些公司,我只听说过一个和它被称为Netscape
额外 作者 R. Schmitz,
@Holger如果公司创建了一个不重写代码库的新产品,那么首先创建。否则你会争辩说今天的AutoCAD和3DSMax等只是1968年UNISURF的“重写”和“使命召唤:现代战争3”只是对DOOM的重写。此外,我并不是说它不会发生,只是在那些情况下,如果没有,它会变得更好。
额外 作者 R. Schmitz,
@Holger你对我所说的以及重写的解释是错误的。使命召唤重写DOOM。这是一个完全不同的开发人员完全不同的产品。当然,你可能会说每一段代码都只是对第一段代码的重写,但这个词只是失去了任何实际意义,你应该然后在这里发一个问题
额外 作者 R. Schmitz,
@Holger Look,Firefox已于2004年发布,即14年; Android在2007年,那是11年。在IT中,这不仅是旧的,而是古代。根据你的理论,Firefox应该是一个“过时的,修补的”(同时),到2006年无法使用的混乱,或者是2010年左右的Android。这只是对可维护代码效率的否定,因为它们现在更好了几乎在每个方面 - 安全性,可用性,功能集,您都可以命名。看起来你只是有一个针对可维护代码的个人议程。
额外 作者 R. Schmitz,
@ jpmc26那是因为优秀的程序员知道必须务实地应用所有“规则”; 所有规则自动获得“除非你有一个非常好的理由”到最后。如果你与教条而不是务实的人一起工作,那对于任何规则来说都是不好的。
额外 作者 R. Schmitz,
@Holger再次错了,你的初始陈述是“软件的设计方式使得创建非常昂贵,因此我们必须保持并维护它,直到我们达到这个完全过时,修补的混乱必须死亡的程度。 “我链接的文章可以让您更深入地了解如果您感兴趣,为什么重写会很糟糕。您的初始声明已被Firefox和Android证明是错误的。之后的所有内容只是一个讨论,评论不适用,所以现在让我们停下来。
额外 作者 R. Schmitz,
@Holger你写的 [A]可能很重要,因为[B]会导致[C] 。这就是你的“可能”所适用的。你的理论是[B]导致[C]。不适用的两个巨大例子证明[B]不会导致[C]。如果你的观点是别的,那么我并没有反驳这一点,只是表明你的一个前提不正确。
额外 作者 R. Schmitz,
@ jpmc26我把“规则”放在引号中,因为它们是指南。规则不能以“你应该”开头。如果一个人甚至不遵循“你应该优化你的代码以便于阅读”或“你应该遵循你团队的代码约定”这样的基本指导原则, “除非你有充分的理由不去”,否则它们更像是一个”真正的程序员“而不是”真正优秀的程序员“。
额外 作者 R. Schmitz,
@ jpmc26足够公平,但在你被告知“你应该遵循你团队的代码约定”之后问5个为什么问题对你的职业生涯不利。有时总是添加解释太明显了。
额外 作者 R. Schmitz,
@ jpmc26“我对更多类更具可维护性这一概念提出质疑。”当然,取决于“更多”的操作数是什么。在这种情况下,它是“50K LOC凌乱的代码”。所以我不确定我是否同意,在OP的帖子的特定背景下。
额外 作者 John Wu,
Nuget可能不是一个很好的例子,因为它的模块化可能更多地受外部需求的驱动,而不是架构师的过度热情 - 毕竟,它是一个用于管理可扩展框架的可扩展框架,它在Visual Studio扩展中作为PowerShell命令行开始运行。现在我想到了,如果所有这些东西都是用monlithical写的,Nuget甚至都不存在......
额外 作者 John Wu,
@Holger在我的帖子中,我讨论了两个可能的优化目标。当然,你也可以针对其他目标进行优化 - 例如“最小化重写次数”(这与优化可维护性非常相似)或“最大限度地重写”,与产品易于创建的优化相似但不相同(还有其他要求,如向后兼容性和特征奇偶校验)。我没有规定目标,我只想强调差异,作为解释精心设计理由的手段。
额外 作者 John Wu,
@Taw“通过各种手段和不惜一切代价发现设计模式。”这听起来像是设计决策(输出)而不是设计目标(输入)。当您将所需的输出前加载到优化输入中时,该过程不会增加太多的值(它不能通过内生性测试),等等。你做错了。听起来你的建筑师只有一个“宠物 NFR ,”这总是很糟糕。
额外 作者 John Wu,
@JohnWu这种解释是我读过的最好也更容易理解的解释。
额外 作者 Les H,
当我第一次学习编码时,我在一个课程中编写了一个程序,它运行良好,但是做到一个简单的改变就会引入4或5个错误,这需要我花几天时间来解决。我开始研究设计并将所有内容分解成大约10个类,我的维护问题就消失了。我在这个重构后引入了0个bug,做了很多改动。我认为这是一个必须学习难以真正理解为什么值得的方法的教训。
额外 作者 reggaeguitar,
@Voo取决于“最小类数”的定义。我们有一个由数千个类组成的应用程序,我知道如果我们使用某些常用的框架和“最佳实践”,我们有20倍的数量......
额外 作者 Holger,
@ R.Schmitz这是一个有趣的观点。 “创建一次但很多次修改”可能很重要因为软件的设计方式使得创建非常昂贵,因此我们必须保持并维护它,直到我们达到这一点为止过时的,修补过的烂摊子已经死了。不是每个人都遵循这种模式有些公司发布的软件也总是在新版本开发的那一天开始,从头开始编写,包含从开发以前版本中学到的所有经验教训,以及新到的技术。
额外 作者 Holger,
@ R.Schmitz你知道那个时代的所有浏览器都是基于原始的马赛克,包括例如名称仍然存在的Internet Explorer?您怎么看?今天有多少浏览器仍以此为基础?今天,只有浏览器从那时起从头开始重写(不止一次),或者甚至不存在那么久。在理论世界中,第一个浏览器可以用模块化,可扩展,面向未来的方式编写,为未来带来的任何变化做好准备。实际上,不是。要求和技术变化太大。
额外 作者 Holger,
@ R.Schmitz所以你说它会更好如果“使命召唤:现代战争3”是基于Doom的源代码而且它是Doom糟糕的软件设计的标志,它不是“T?或者你真的在确认我的观点,旧软件将被新软件取代,因为无限更新旧软件是一种错觉吗?
额外 作者 Holger,
@Voo我没有看到我声称所有软件开发人员都这样做的地方。也许你是对的,Firefox从未经过重新设计,从Netscape重新设计,从头开始重新设计。也许这表明Firefox的设计有多好。或者,或许,你即将开始一场火焰战,因为Firefox的质量被认为是非常有争议的。不要打开那些虫子。
额外 作者 Holger,
@ R.Schmitz看来你完全忽略了这一点。新软件是“重写”旧软件还是全新产品并不重要。关键是,它是 new 软件,旧的维护是多么容易并不重要。你现在明白了吗?旧软件会死。旧软件将被新软件取代。这是不可避免的事实。您只能影响新的是您的竞争对手还是竞争对手。但您也可以优化您的软件进行维护,使新软件的开发变得昂贵,并尽量避免永远编写新软件......
额外 作者 Holger,
@Voo确切地说,微软是唯一一个从头开始重写浏览器的人。现在,除了当时存在的Microsoft(Netscape 4或更早版本)之外的名称任何浏览器,这些浏览器今天仍然存在(没有重大改写,换句话说,仍然基于Mosaic代码)。好吧,实际上Netscape也进行了重写,但将其外包给了Mozilla基金会,这导致了Firefox的发展。所以重写“杀死”了产品,因为它催生了新产品。您认为基于旧代码的Netscape浏览器今天仍然可以存在吗? Mozilla不同意。
额外 作者 Holger,
@JohnWu你描述优化的方式是不同目标的功能非常好,我不反对。这是一个非常好的答案。
额外 作者 Holger,
@R.Schmitz除了Firefox的主要赞助商曾经决定编写自己的名为Chrome的浏览器,而不是基于Firefox,你是对的。 Firefox很古老。但是,你想证明什么?我最初的声明是,今天,没有任何浏览器基于Mosaic,因为所有现有的浏览器,从那时起都已完全重写,或者比它更年轻。考虑到Mosaic来自1993年,Firexfox 比它更年轻。所以你没有证明我错了。除此之外,我不反对可维护的代码,从来没有说过。相反,我反对只知道成长的代码。
额外 作者 Holger,
@ R.Schmitz你正在脱离这个短语。句子是“”创建一次,但修改了很多次,很多次'可能很重要,因为软件的设计方式使得创建非常昂贵...... “。这是一种关于可能适用于某些数量软件项目的模式的推测。你已经错过了这一点,开始讨论樱桃挑选的软件,两个例子IIRC,已经维持了很长时间,忽略了做了死亡的所有数十亿软件。只有两个例子,这种推测性陈述并未“证明是错误的”。
额外 作者 Holger,
很好的答案,但..:另一种方法是优化维护意味着这是唯一一个OP抱怨的。还有优化以通过各种方式并且不惜一切代价地发现设计模式。关键字是 smart ,只是创建了很多类,它不是给定的。 DI是一个伟大/可怕的例子,正如一位智者所写的那样,你得到一个50美元的流行语概念。
额外 作者 pong,
一个很好的答案,但有一个明显的遗漏:很有可能“600行杂乱的代码”无法正确测试,而这仅仅是OP中所倡导的方法的破坏。
额外 作者 Nath,

嗯,首先,可读性和可维护性通常在旁观者眼中。

你可读的东西可能不是你的邻居。

可维护性通常归结为可发现性(在代码库中发现的行为或概念的容易程度)和可发现性是另一个主观的事情。

DDD

DDD帮助开发团队的方法之一是建议一种特定的(但仍然是主观的)组织代码概念和行为的方式。这个约定使得发现事物变得更容易,因此更容易维护应用程序。

  • 域概念编码为实体和聚合
  • 域行为位于实体或域服务
  • Aggregate Roots确保一致性
  • 持久性问题由存储库处理

这种安排不是客观地更容易维护。但是,当每个人都理解他们在DDD环境中运行时,它可以更容易地维护可测量

类 help with maintainability, readability, discoverability, etc... because they are a well known convention.

In Object Oriented settings, 类 are typically used to group closely related behaviour and to encapsulate state that needs to be controlled carefully.

我知道这听起来很抽象,但你可以这样思考:

With 类, you don't necessarily need to know how the code in them works. You just need to know what the class is responsible for.

类 allow you to reason about your application in terms of interactions between well defined components.

在推理应用程序的工作原理时,这可以减轻认知负担。您无需记住 600行代码所实现的内容,而是可以考虑 30个组件如何进行交互。

而且,考虑到这30个组件可能跨越 3层您的应用程序,您可能每次只需要推理大约10个组件。

这似乎很容易管理。

摘要

从本质上讲,你所看到的高级开发者所做的是:

They are breaking down the application into easy to reason about 类.

然后他们将这些组织成易于推理层。

They are doing this because they know that, as the application grows, it becomes harder and harder to reason about it as a whole. Breaking it down into Layers and 类 means that they never have to reason about the entire application. They only ever need to reason about a small subset of it.

64
额外
除了较小的类更容易更换/重构。
额外 作者 Pieter,
“使用Classes,您不一定需要知道它们中的代码是如何工作的。您只需要知道该类负责的内容。”类绝对不是实现这一目标的唯一方法,尽管在某些情况下它们可能是一种有用的方法。更重要的是,它并没有使用实现这一目标的类,它是开发人员的良好的设计选择。课程不是一种神奇的酱汁,无论如何都可以为您提供。
额外 作者 jmibanez,
根据OP,谁“在理解他们的思维,推理方面苦苦挣扎”,似乎制作易于推理的代码背后的推理并不那么容易推理。
额外 作者 Erb,
是的,过度工程很糟糕。杀小狗也是如此。这里没有人提倡。 logicallyfallacious.com/tools/lp/Bo/LogicalFallacies/169/ …
额外 作者 Warpspace,
此外,软件工程环境中的“优雅”通常是狡猾的词。 en.wikipedia.org/wiki/Weasel_word
额外 作者 Warpspace,
任何组织代码的方法都可以这样说。一切都取决于维护团队理解为什么代码按原样组织。这是使用完善和理解的惯例的重点。
额外 作者 Warpspace,
@ jpmc26我同意,上课并不神奇。但是,它们通常为封装和公共合同等事项提供语言级支持。
额外 作者 Warpspace,
@ icc97听起来你说的是经验,背景以及约定和模式的知识会产生巨大的差异。而且一个初级开发者可能缺乏这些东西,这使得他们很难用这些东西记下代码。
额外 作者 Warpspace,
过度工程提供更易于维护或可理解的代码 - 恰恰相反,尽管你声称。谁能够更优雅地解决问题,谁需要 AddressServiceRepositoryProxyInjectorResolver ?设计模式的设计模式只会导致不必要的复杂性和冗长。
额外 作者 Ian Yates,
很棒的答案!对于不同意你的评论我感到很惊讶
额外 作者 reggaeguitar,
如果他们首先将其分成类并且然后将这些类组合成层,那么难怪事情会变得混乱。你想以相反的方式做到这一点(另外,你想要开始介绍类)。
额外 作者 Clearer,
然后只需要一把猴子扳手就可以打破整个模式
额外 作者 user2890248,

请解释一下,为什么我们需要这种DDD风格,很多模式?

首先,注意:DDD的重要部分不是模式,而是开发工作与业务的一致性。 Greg Young评论说蓝皮书中的章节是在错误的订单中。

但是对于你的具体问题:往往会有比你想象的更多的类,(a)因为正在努力将域行为与管道区分开来,(b)因为正在做出额外的努力来确保这些概念在域模型中明确表达。

坦率地说,如果你在域中有两个不同的概念,那么它们在模型中应该是不同的,即使它们碰巧在内存表示中共享相同的

实际上,您正在构建一种特定于域的语言,该语言使用业务语言描述您的模型,以便域专家应该能够查看它并发现错误。

Additionally, you see a bit more attention paid to separation of concerns; and the notion of insulating consumers of some capability from the implementation details. See D. L. Parnas. The clear boundaries empower you to change or extend the implementation without the effects rippling throughout you entire solution.

这里的动机是:对于作为企业核心竞争力的一部分的应用程序(意味着它获得竞争优势的地方),您将希望能够以更好的变体轻松且廉价地替换域行为。实际上,您希望程序的某些部分能够快速发展(状态如何随着时间的推移而发展),以及您希望缓慢改变的其他部分(状态如何存储);额外的抽象层有助于避免无意中将一个与另一个相互耦合。

公平地说:其中一些也是面向对象的脑损伤。 Evans最初描述的模式基于他15年前参与的Java项目;状态和行为紧密结合在一起,导致您可能更愿意避免的并发症;请参阅Stuart Halloway的 Perception and Action ,或内联代码

无论您使用何种语言,以功能方式编程都会带来好处。你应该在方便时做到这一点,并且在不方便的时候你应该认真考虑决定。 Carmack,2012

29
额外
我认为这是最好的答案。虽然我绝不会出售DDD,但这个答案至少可以解释它究竟是什么以及它的实际动机,而不是吸引那些毫无意义的普通陈词滥调。
额外 作者 jmibanez,
John Carmack是IMO的一个很好的例子,因为他以编写既简洁又易于理解的代码而闻名,同时表现也很出色。当你使用先进的技术跟踪他的代码时,这一点尤为明显 - 拥有更好的CPU和内存,你可以看到许多东西从“这需要真正简洁”到“这需要非常清晰/孤立/ ...... ”。我所知道的最实用的程序员之一:)
额外 作者 Luaan,
在我读到这篇文章之前,我将添加自己的答案。我要强调第一段,目的是在软件中对业务概念进行建模,以使软件与业务需求保持一致。公平地说,驱动项目的分析师还应该表达驱动器交付时间的多少,并且应该相应地调整代码/测试质量或完整性。
额外 作者 Sentinel,

其他答案中有很多好处,但我认为他们错过或者没有强调你犯下的一个重要的概念错误:

您正在比较理解整个计划的努力。

这对大多数程序来说都不是一个现实的任务。 即使是简单的程序也包含如此多的代码,因此在任何给定时间都无法管理所有代码。 您唯一的机会是找到与手头任务相关的程序部分(修复错误,实现新功能)并使用它。

如果你的程序由庞大的函数/方法/类组成,这几乎是不可能的。 您必须了解数百行代码才能确定此代码块是否与您的问题相关。 根据您提供的估算,您可以轻松地花一周时间查找您需要处理的代码。

将其与具有小函数/方法/类的代码库进行比较,这些函数/方法/类被命名并组织到包/命名空间中,这使得查找/放置给定逻辑的位置变得明显。 如果在许多情况下正确完成,您可以直接跳到正确的位置来解决您的问题,或者至少到一个启动调试器的位置可以带您到几个跳跃的正确位置。

我曾在两种系统中工作过。 对于可比较的任务和可比较的系统尺寸,差异很容易达到两个数量级。

这对其他活动的影响:

  • 使用较小的单位,测试变得更容易
  • 较少的合并冲突,因为两个开发人员处理同一段代码的机会较小。
  • 减少重复,因为它更容易重复使用(以及首先找到碎片)。
26
额外

因为测试代码比编写代码更难

从开发人员的角度来看,很多答案都给出了很好的推理 - 维护可以减少,代价是首先编写代码更加费劲。

However, there is another aspect to consider - testing can only be fine-grained as your original code.

如果你把所有东西写成一个整体,你可以编写的唯一有效的测试是“给定这些输入,输出是否正确?”。这意味着发现的任何错误都是“在那堆巨大的代码中的某个地方”。

当然,您可以让开发人员与调试器坐在一起,找到问题的确切位置并进行修复。这需要大量资源,并且很难利用开发人员的时间。想象一下,你遇到的小错误导致开发人员需要再次调试整个程序。

解决方案:许多较小的测试可以精确定位每个特定的潜在故障。

这些小测试(例如单元测试)具有检查代码库的特定区域并帮助在有限范围内查找错误的优点。这不仅可以加快测试失败时的调试速度,而且还意味着如果所有小测试都失败了 - 您可以更容易地在较大的测试中找到失败(即,如果它不在特定的测试函数中,则必须在交互中它们之间)。

应该很清楚,要进行较小的测试意味着您的代码库需要分成较小的可测试块。在大型商业代码库上执行此操作的方法通常会导致代码看起来与您正在使用的代码类似。

正如旁注:这并不是说人们不会把事情“拖得太远”。但是有一个合理的理由将代码库分成更小/更少连接的部分 - 如果做得合理的话。

19
额外
虽然您的答案仅涉及测试和可测试性,但这是一个最重要的问题,其他一些答案完全被忽略,因此+1。
额外 作者 Nath,

请解释一下,为什么我们需要这种DDD风格,很多模式?

我们中的许多人(大多数......)确实不需要他们。理论家和非常先进,经验丰富的程序员通过大量研究和他们的深刻经验撰写关于理论和方法的书籍 - 这并不意味着他们所写的一切都适用于日常实践中的每个程序员。

作为初级开发人员,阅读诸如您提到的书籍以扩大您的观点并让您了解某些问题是很好的。当您的高级同事使用您不熟悉的术语时,它还可以避免您变得尴尬和沮丧。如果你发现一些非常困难并且看起来没有意义或看起来有用的东西,不要为此而自杀 - 只要把它归咎于你有这样一个概念或方法。

在您的日常开发中,除非您是学者,否则您的工作就是找到可行的,可维护的解决方案。如果您在书中找到的想法无法帮助您实现这一目标,那么只要您的工作被认为是令人满意的,那么现在就不要担心。

可能有一段时间你会发现你可以使用你所读到的一些东西,但最初并没有“得到”,或者可能没有。

17
额外
你是说在你20年的经验中,从来没有相同的解决方案出现在问题的一部分吗?每次有两个类相互交互时,你总是创建一个全新的解决方案吗?很好,每个人都一遍又一遍地使用模式,书籍只在那里,所以当我们谈论它们时,我们用相同的方式命名。
额外 作者 Rob Hunter,
虽然在纯粹的水平上这是正确的,但听起来像DDD和其他模式只是理论。他们不是。它们是实现可读和可维护代码的重要工具。
额外 作者 Moogie,
@vikingsteve OP不是评论设计模式普遍过度使用的编程大师。 OP是学徒,认为他们在他的商店过度使用。我知道初学者 - 我会对我现在编写的代码有同样的想法,但初学者 - 我有很多个人项目失败,因为他们最终太复杂了。
额外 作者 R. Schmitz,
@Vector我对这些编辑有多好感到有点困惑。对于我来说,这从“似乎自以为是”变为“100%同意”,绝对是+1!
额外 作者 R. Schmitz,
@Luaan如果你已经关心设计精良,有条理,有条理的OOP,我几乎不能称那个初学者 - 你,但是过度使用是不好的。然而,问题更多的是关于OP如何不能真正理解这些问题解决的问题。如果你不知道原因,似乎没有理由 - 但实际上,你还不知道它。
额外 作者 R. Schmitz,
@JensSchauder - 我根据你的评论编辑了答案。我同意简单地称之为“理论”通常是不正确的。 “方法论”更合适。
额外 作者 Vector,
@ R.Schmitz - 我读了评论并意识到我的语言不是很准确,我的立场听起来有点极端。
额外 作者 Vector,
@PeteKirkham OP的困境是设计模式的过度使用。你真的需要一个 AccountServiceFacadeInjectResolver (我在工作系统中找到的真实例子) - 答案很可能是没有。
额外 作者 Ian Yates,
@R.Schmitz那就是说,初学者我有很多个人项目失败,因为他们努力设计精心设计,组织,结构化,OOP ......所有这些都是工具。需要对它们进行适当的理解和使用,而且你很难将锤子归咎于拧紧。令人惊讶的是,似乎需要很多洞察力和经验来理解这个简单的事情:)
额外 作者 Luaan,
根据我的经验,奇怪的是我发现AccountServiceFacadeInjectResolver在不使用DDD的代码中更频繁地输入内容。基于Spring的应用程序似乎几乎完全是与填充框架而不是问题域有关的类。
额外 作者 Rob Crawford,

由于你的问题涉及很多方面,有很多假设,我会挑出你问题的主题:

为什么我们在设计模式中需要这么多课程

我们不。没有普遍接受的规则表明设计模式中必须有许多类。

有两个关键指南可用于决定放置代码的位置,以及如何将任务切割为不同的代码单元:

  • 凝聚力:任何代码单元(无论是包,文件,类还是方法)都应该属于。即,任何特定的方法都应该有一个任务,并且做得很好。任何类都应该负责一个更大的主题(无论可能是什么)。我们想要高凝聚力。
  • 耦合:任何两个代码单元应尽可能少地相互依赖 - 特别应该没有循环依赖。我们想要低耦合。

为什么这两个重要?

  • Cohesion:一种做很多事情的方法(例如,老式的CGI脚本,可以在一长串代码中执行GUI,逻辑,数据库访问等)变得笨拙。在撰写本文时,我很想把你的思路放到一个很长的方法中。这很有效,很容易呈现等等,你可以用它完成。后来出现麻烦:几个月后,你可能会忘记你做了什么。顶部的一行代码可能距离底部的一条线几个屏幕;很容易忘记所有细节。方法中任何地方的任何变化都可能会破坏复杂事物的任何行为。重构或重复使用该方法的某些部分将非常麻烦。等等。
  • 耦合:无论何时更改代码单元,都可能会破坏依赖它的所有其他单元。在像Java这样的严格语言中,您可能会在编译期间获得提示(即,关于参数,声明的异常等)。但是许多变化并没有触发这种变化(即行为变化),而其他更动态的语言则没有这种可能性。耦合越高,它就越难以改变任何东西,你可能会陷入停顿,需要完全重写以达到某个目标。

这两个方面是任何选择“在哪里放什么”在任何编程语言中的基础“驱动因素”,以及任何范例(不仅仅是OO)。不是每个人都明白地意识到这些,并且需要时间,通常是几年,才能对这些如何影响软件产生一种根深蒂固的自动感觉。

显然,这两个概念并没有告诉你什么实际。有些人偏错太多,有些人则偏少。有些语言(在这里看你,Java)倾向于偏爱许多类,因为语言本身具有极其静态和迂腐的性质(这不是一个价值陈述,但它就是它的本质)。当您将它与动态和更具表现力的语言(例如Ruby)进行比较时,这一点尤为明显。

另一个方面是有些人订阅敏捷方法,只编写现在所需的代码,并在必要时稍后重构。在这种开发方式中,当您只有一个实现类时,不会创建 interface 。您只需实现具体类。如果,稍后你需要第二堂课,你会重构。

有些人根本就不这样做。它们为任何可以更普遍使用的东西创建接口(或更一般地说,抽象基类);这导致阶级爆炸很快。

同样,有支持和反对的论据,我或你更喜欢哪个并不重要。在您作为软件开发人员的生活中,您将会遇到所有极端情况,从长期的意大利面方法,到开明的,足够大的课堂设计,到令人难以置信的夸张的课程计划,这些课程都是过度设计的。随着您越来越有经验,您将更多地成长为“建筑”角色,并且可以开始在您希望的方向上影响这一点。你会发现自己的黄金中间,你会发现很多人会不同意你,无论你做什么。

因此,保持开放的思想是最重要的一点,在这里,这将是我对你的主要建议,看到你似乎对这个问题感到非常痛苦,从你的其余问题判断......

8
额外

经验丰富的程序员已经了解到:

  • 因为那些微不足道的程序和clasess开始成长,特别是成功的程序。那些简单模式的简单模式无法扩展。
  • 因为为每次添加/更改添加/更改多个工件似乎很麻烦,但您知道要添加什么,并且很容易这样做。 3分钟的打字比3小时聪明的编码。
  • 很多小班并不是“凌乱”,因为你不明白为什么开销实际上是一个好主意
  • 如果没有添加领域知识,“对我来说很明显”的代码对我的队友来说往往是神秘的......加上未来的我。
  • 部落知识可以让项目难以轻松地加入团队成员,使他们能够快速高效地工作。
  • 命名是计算中的两个难题之一仍然是正确的,许多类和方法通常是命名的一个激烈的练习,许多人认为这是一个很大的好处。
7
额外
@vikingsteve你一直在提到一个有缺陷的实现的例子,希望它能证明结构化代码一般来说是个坏主意。那只是没跟踪。
额外 作者 Warpspace,
@Clearer我很确定这里的每个人都同意结构化代码的假设误用是一件坏事。 OP说他们是少年。这就是人们假设他/她不熟悉结构化代码的好处并重申它们的原因。
额外 作者 Warpspace,
但是必要的开销是多少?在我看到的许多情况下,设计模式被过度使用。过度复杂的结果是增加了解,维护,测试和调试代码的难度。当你在一个简单的服务类(我刚刚看到的一个真实的例子)周围有12个类和4个maven模块时,它很快就会变得比你想象的更难管理。
额外 作者 Ian Yates,
@MetaFight OP不是在谈论结构代码。他正在谈论,他所看到的,太多的抽象。如果你有太多的抽象,我争辩说,你很快得到非常非结构化的代码和许多几乎相同的部分,只是因为人们无法应付他们必须处理的东西。
额外 作者 Clearer,

根据用途制作更多的课程和功能将是以特定方式解决问题的有效方法,有助于将来解决任何问题。

多个类有助于识别它们的基本工作,并且可以随时调用任何类。

但是,如果您的getable名称中有许多类和函数,则它们易于调用和管理。 这称为干净代码。

4
额外
对不起,但是你在说什么?
额外 作者 Shizam,
这个答案需要提出它声称以更清晰的英语呈现的想法。似乎答案中的核心思想是较小的类,每个类都有一个明确定义的功能,是一个好主意,但可怕的语法几乎不可能理解。而且,这个断言本身可能还不够。
额外 作者 Stefan Kendall,
@Deduplicator让我们在java中举个例子,如果我们设计我们使用jframes线程和类来继承它们。从一个类我们不能使用java的一些作品进行设计,所以我们需要创建更多的类
额外 作者 David Farrell,

答案到目前为止,所有这些都很好,开始时有一个合理的假设,即提问者遗失了某些内容,而提问者也承认了这一点。提问者也可能是基本正确的,并且值得讨论这种情况是如何产生的。

经验和实践是强大的,如果老年人在大型,复杂的项目中获得经验,只有通过大量的 EntityTransformationServiceImpl 来控制事物,那么他们就会变得快速而舒适地进行设计模式和对DDD的密切关注。即使对于小型程序,使用轻量级方法也会降低效率。作为奇怪的一个,你应该适应,这将是一个很好的学习经验。

虽然在适应的同时,你应该把它作为深入学习单一方法之间平衡的一课,直到你可以使它在任何地方工作,而不是保持多才多艺,知道什么工具可用,而不必是任何一个专家。这两者都有优势,世界都需要它们。

2
额外