在开发时,您何时可以确定应用程序中是否包含许多不必要的类?你应该有多少课程有一定的限制?
答案 0 :(得分:22)
确实没有“太多课程”这样的事情。 可以成为一个问题的是“太多的课程在做同样的事情。”
如果您认为代码库中的类太多,那么审核的好方法就是添加一些新的需求。任何迫使你对代码进行一些更改的东西。 (当然,在源代码控制的一个单独的分支中。)进行这些更改有多难?相对简单的更改是否需要修改吨和吨类?如果是这种情况,那么很有可能你有太多,但问题不在于数字本身。
在很多情况下,这主要取决于个人偏好。在代码重用和代码去耦之间经常需要权衡。通过分离出可能的每一个问题并拥有大量的小班级,你可以解除所有其他问题。但是,在这种情况下,您经常会发现必须重复代码,因为很多代码可能会“做同样的事情”,但原因略有不同。
另一方面,如果你坚持不在代码中重复任何东西,那么当你获得更少的类时,你也经常会得到更多的耦合,因为单个类对任何需要类似功能的代码都有多重责任。 / p>
在大多数情况下,最重要的是抵制变革。耦合与重用是人们可以争论的长度,但软件刚性是争论转化为实际努力(金钱)的地方。测试对代码进行更改的难度。然后尝试以您认为更接受变更并再次测试它的方式重新安排您的类/逻辑。是否有显着改善?
答案 1 :(得分:5)
通常很多课程意味着你可能已经非常普遍地解决了你的问题。这通常是好的,因为这意味着你希望在最终需要的时候更容易改变行为。
在开发较小的项目时,最好更具体(即不那么普遍)以更快地实现事情,这可能会导致课程减少,但在需要出现时可能更难改变。
只要这些类排序良好且具有明确定义的目的,拥有多个类就不成问题。
如果类紧密耦合或某些类的责任没有明确定义,那么问题究竟是什么呢?有关coupling的更多信息,请访问here。
下面的评论中提到了可能出现的另一个问题。如果您的许多类具有类似的代码,则会出现重复问题。如果需要对重复代码进行更改,则必须多次进行更改,这通常会导致系统的可维护性降低。这通常通过继承来解决。
答案 2 :(得分:4)
Kent Beck回答你的问题。 Jeff Langr在“清洁代码敏捷软件工艺手册”一书中讨论了Kent Beck指定的四个设计规则。
(按重要性排序)
肯特建议采用务实的方法来保持课程和方法的数量较低。他举了一些关于教条规则的例子,例如所有类都应该有接口。通常是,但有时候可能没有必要这样做。但是,这条规则是简单设计的四条规则中最低优先级。
(请注意,这是Kent Becks的意见,而不是我的意见!)
答案 3 :(得分:3)
在我目前正在开展的一个项目中,我肯定认为我们使用了太多的类 - 或者至少使用了太多的对象/实例。
我们构建了一个基于PHP / MySQL的CMS,其中数据库中的每个记录和字段都表示为PHP中的对象。这可能导致数万个实例同时出现,并且我们不断遇到性能问题/内存不足等问题。
这当然不是其他编程语言或其他要求的问题,但在我看来,性能也需要考虑。
答案 4 :(得分:2)
正如其他许多人所说,“这取决于......”
通常取决于您的方法,目标以及团队成员的偏好和能力的组合。如果你对单元测试非常严格,你可能最终会得到很多小的通用类和依赖注入。这也意味着个别团队成员很难从所有非常非常通用的部分看到您正在构建的具体整体。
就我个人而言,我更愿意考虑建立在两个层面上的API:较低级别由通用,独立部分和高级别,我使用几个外墙,导演等来呈现具体和有用的东西。其他程序员。这很像iOS库的设计恕我直言。
答案 5 :(得分:1)
这完全取决于你的项目。它取决于您的要求
类必须是最小的,没有不需要的类
从某种意义上说,类必须是最大的,它们都分别包含那些属性。
答案 6 :(得分:1)
一个应用程序都可以在一个代码文件中,或者每个雾化函数都可以在自己的文件中,唯一受影响的是维护。可维护性可能意味着您自己导航代码的能力,也可能意味着其他人可以理解代码,或者是否可以构建新版本。
我认为没有任何关于此的通用指南总是适用,这取决于很多事情。例如,在用JavaScript编码时,通常使用较少(和较大)的文件,这些文件包含的无关功能比用C#或C ++编码时更多。
如果您使用的是Visual Studio 2012,则http://msdn.microsoft.com/en-us/library/bb385910.aspx和http://msdn.microsoft.com/en-us/library/dd264939.aspx会提供有关代码指标和代码分析如何工作的信息。
这是基于我自己的应用程序在Visual Studio 2012中的代码指标报告的示例,值在http://msdn.microsoft.com/en-us/library/bb385914.aspx解释。
Project: <<Projectname>>
Configuration: Debug
Scope: Project
Assembly: <<Path>>
Maintainability Index: 84
Cyclomatic Complexity: 479
Depth of Inheritance: 8
Class Coupling: 189
Lines of Code: 903
答案 7 :(得分:1)
我认为这取决于哪个区域有大量的课程。如果有许多包含公共业务逻辑的静态类,那么这将被认为是错误的,因为静态类应该仅用于常见的帮助器方法。静态类永远不应包含常见的业务逻辑。
如果不同的层有不同的类来保存基本相同的数据。这将被视为不好,因为DTO类不应跨层复制。
但是如果在适当分解需求之后创建了类,那么我认为拥有大量的类实际上是好的。
答案 8 :(得分:1)
哲学:
有太多的东西。你可以有太多和太少的课程。有些人喜欢假装更多是免费的,因为他们可以使用搜索等工具作为创建过于混乱,难以导航和大型搜索空间的借口。从长远来看,你总会发现可衡量的赤字。
我不确定你可以拥有多少课程的上限。你可以找到无限制地从技术上讲,无限地添加课程的方法。如果你不能有太多的课程,但你可以无限地添加它们,那么你永远不会因为你想要的课程数量而完成你的课程,因此你可以有太多的课程。
许多IDE可以很容易地创建大量的类,并将它们与样板生成,自动完成以及总是复制等串联起来。许多工具降低了创建基本上无用的代码的成本,但不会降低膨胀成本。即使有帮助者,未启动的代码总是比臃肿更便宜(你只能减税,而不是消除它)。如果你不关心它,它最终会成为一个问题。即使您有代码扫描,精细和文件替换等内容,然后十倍以上仍然是十倍。它意味着改变十倍,出错十倍以上,每行消耗的努力量的十分之一。
许多人陷入了认为通过增加更多课程来降低复杂性的陷阱。实际上,它们往往只是打破复杂性,将事物从与它们相关的事物中移开,并以间接的形式增加复杂性。线性代码变为,
不必要的非线性(这是一个太多段落的例子,虽然公平一个更好的例子可能是每个句子或单词的一个段落太多,当你的段落成为句子然后你不会&#39 ; t真的有两个单独的东西,这可能是两段很多的证据,当句子与段落不同时。)
检测:
看待这个的简单方法是,如果你有路径A(单个节点/一个函数/类/等),但将其分解为A-> B,你实际上并没有获得任何东西。你刚拿了一张纸,把它撕成两张,把它放在两个信封里,然后把它贴到目的地。如果事实证明你确实需要一个具有多个边缘的节点,那么你就获得了一些东西。例如,这将是A-> B,A-> C.您可以使用图形分析来嗅出太多对象。如果你有很长的链接列表或许多小链接列表(或者甚至可能只有几个),那么你可能会说你有太多的课程。并非所有形式的对象过度都很容易被检测到。由于太多的课程,维护变得过于复杂,因为您最终会支持一定程度的灵活性,而且模型只能使用一小部分。这意味着您的许多代码实际上并不对应于需要完成的任务。仅这一点就很难维持,因为该代码的目标是主观的而不是客观的,它也可能是任意的。
您可以使用代码库并减少类的数量,直到您只有实际需要的类为止。这意味着只有那些需要可移植性(传递数据),事物应该如何表现的变化(传递方法)以及合理分离(独立处理主要概念,如持久性,表示等)和重复数据删除所需的差异。如果无法为良好的设计工作,许多程序员将向后执行,编写代码并仅根据需要将其拆分到特定目的。
虽然可以衡量,但并不是对太多类别或完美符号的准确衡量。只有提示。所需的最小类数与最大类之间的较大比例是一个提示。什么是大的?我会说100次绝对是怀疑,10次相当可疑,5次轻微怀疑。这可以根据许多参数进行更改。
一个奇怪的措施是gzip你的代码。压缩比越好,膨胀的可能性就越大。这不是一个完美的衡量标准,因为它需要参考点。降低压缩比的某些方法也可能是徒劳的,对天生的特定数字编码永远不会起作用。
如果他们让你做的工作并没有真正帮助你实现你的最终目标,或者如果他们让你比他们更慢,那么你可以知道你是否需要很多课程(或界面)正在加快速度。但这可能是主观的。如果有人上课太多,这意味着他们将不得不改变他们的习惯,这意味着通常会有更好的编码方法的入门费。在项目开始时,这很难被发现,因为添加代码通常非常便宜。没有什么比这更依赖于它,层次很浅等等。至少在几个月甚至一年之后,进入一个膨胀,组织不良等项目的成本变得明显。直到一个项目变得几乎陷入僵局,人们才会注意到它。许多人不会知道,如果需要一年的事情应该真的需要一年或六个月。很少有比较点。
如果你看你的课程,你可以选择一些东西。有多少代码是OO,多少是FO(面向对象与面向功能)?
功能导向是指实际执行某些操作并直接对最终结果做出贡献的代码。这包括您必要的代码。它可能包括超出分配和铸造的运营商。通常是条件语句,分支,读取数据,选择行动方案,并采取适当的行动方案,如数据生成,变异或提取/存储IO。
面向对象意味着简单地使用类表示概念。此代码几乎将您的过程代码转换为声明性语言。如果你的大多数课程只是帮助重型检查,表示等,那么你可能会有太多。这些是类和方法的标志,它们的名称部分可以是变量,允许您缩小这些类。一个非常强烈的迹象是,如果这些类正在做的大多数是拳击。简单地分配变量但不做其他任何事情。这实际上是一种过多的结构,通常缺乏重复数据删除,动态编码或抽象。
在明显的情况下,如果一个类从未被使用过,那么它的类太多了(如果它的死代码)。类似但名称不同的类也是一个好兆头。
原因:
这可以通过一些机制来驱动,这些机制可以很容易地创建并将事物连接在一起(当你抽象并动态地做事情时,往往会破坏,以避免取代取代但破坏IDE的良好习惯) 。我经常因为试图完全代表一切而陷入困境,但OO实际上并不够灵活,而且经常证明是YAGNI。一个常见的问题就是缺乏抽象,你通常可以像前面提到的那样将变量展开到顶级语言结构中(它也链接到一个设计中以直接向IDE公开所有内容)。这可能不仅背叛了动态编码的缺乏,而且还出现了预处理器或类似设备的使用。这看起来基本上是一棵树,其中定义了所有叶子。尽可能使用类,您希望避免为每个可能的叶子定义一个类。树扩展到极端的标志可能是你有一个类用于每个可能的原语使用。这也可能是一种更极端的迹象。所有课程的展开笛卡尔积。在这种情况下,如果两者之间通常没有实际的差异,那么你就不能简单地获得一个Leg的课程,而是一个CatLeg,DogLeg等课程。有些人可以通过类型检查的极端主义来阻止有人将DogLeg放在CatLeg上。这是一种令人讨厌且常见的反模式。
太多课程的最大驱动因素之一是尝试遵守云中那些并非真正适用于您的情况的标准。在这种情况下,您不会针对您的问题进行编程。你正在编程以回应其他人的问题。
这在诸如SOLID之类的东西中很常见。了解和理解SOLID等原则,能够应用它们非常重要,但知道什么时候不应用它们也很重要。
当教授具有大型库的OOP语言时,大量使用此原则。如果您要创建一个您希望分发给全世界的OOP库,可能有数百万人拥有每个可以想象的用例,那么您希望遵循OOP原则,这些原则会导致很多内容破坏制作接口和类以便它们可以以不同的方式使用,因此一个功能部件没有很高的机会吸引另一个可能不需要的功能。你必须考虑到,在这个世界上,你不想创建人们可能需要分叉的库。人们不想这样做,因为他们会成为他们重复使用的代码的维护者,否则会损失总体拥有成本。
这些原则也增加了很多开销,如果你的代码库有一个有限的用户范围,完全在你的控制之下,那么你可能有太多的代码,如果你正在做&#? 34;你应该如何&#34;用于分布式代码。即使您确实拥有分发的代码,有时它可能过于极端,无法事先满足所有用例,您有时必须找出最有可能需要的东西,然后根据需要更改其他所有内容。对于一个小型图书馆,你可以负担得起很多额外的工作。对于大型代码库,您必须在开销最有可能为自己付费的情况下进行锻炼。
反例:
在理想的世界中,您只需根据您的直接需求进行最低限度的编码。由于过度行为不是自我暴露,因此这种方法优越的偏见。缺点是。如果你的东西太少,就会直接出现。这在DRY中很常见。添加一个功能后,添加另一个功能。您复制并粘贴第一个然后更改下半部分。这两个函数的常见上半部分是重复代码,它立即自我揭示它们需要进行重复数据删除。您可以通过创建第三个函数来实现。你知道这不是一个功能太多,因为你有一个客观可证明的理由来创建它。在编写供他人使用的代码时,这种方法变得更加困难。其他人我不一定意味着任何人。我的意思是那些没有直接访问代码库的人,通常是陌生人。基本上可以在需要时轻松/快速/廉价地分解代码的人。如果您不为这样的受众群体提供服务,那么您就不必担心过早地破坏您的代码。
我最近在网上使用的课程太少了。它包含一个具有多重职责的类。它需要一个文件句柄(作为基本类型)来写入,然后根据调用的方法(如addImage,addText等)自动输出适合于它生成的流的HTTP头。
在一个理想的世界中,这个阶级不应该对输出做出假设。用途可能希望输出到文件系统,内存,TCP流等。它只需要提供一个简单的写入方法的接口或使用标准库中的一个。在我的情况下,我只需要通过字符串连接输出它,但为了实现这一点,我必须打开一个伪文件映射到内存(这通常不可能,但语言允许它作为黑客)。
我使用所有来源的随机库多次遇到此问题。在某些情况下,应该明确应该采用分离的方式,有时则不应该进行分离。如果有疑问的话,由于你最终会保证能够找到它,所以仍然会有太少的打击。我倾向于观察到,如果你添加任何东西,你并不能确定你最终会陷入沉重的膨胀境地。如果你这样做了,你可能会做两次然后这就成了一种习惯。