16

当你在开发的时候,你什么时候可以确定你的应用程序中是否有很多不必要的类?你应该有多少课程有一定的限制吗?

4

9 回答 9

24

真的没有“课程太多”这样的事情。一个问题可能是“太多的类在做同样的事情”。

如果你觉得你的代码库中有太多的类,一个很好的审计方法是添加一些新的需求。任何迫使您对代码进行一些更改的东西。(当然,在源代码控制的一个单独分支中。)进行这些更改有多难?一个相对简单的更改是否需要您修改大量的类?如果是这种情况,那么很有可能您确实有太多,但问题不在于数字本身。

在许多情况下,这主要是个人喜好问题。代码重用和代码解耦之间通常需要权衡取舍。通过分离所有可能的关注点并拥有许多小类,您可以将所有内容与其他所有内容分离。但是,您经常会发现在这种情况下必须重复代码,因为很多代码可能在做“相同的事情”,但原因略有不同。

另一方面,如果您坚持从不重复代码中的任何内容,那么当您获得更少的类时,您通常也会最终获得更多的耦合,因为单个类将对任何需要类似功能的代码承担多种责任。

在大多数情况下,底线是抵制变革。耦合与重用是人们可以详细争论的问题,但软件刚性是争论变成实际努力(金钱)的地方。测试更改代码的难度。然后尝试以您认为更能接受更改的方式重新安排您的类/逻辑并再次测试它。有显着改善吗?

于 2012-10-14T13:35:36.650 回答
6

一般来说,很多课程意味着你很可能已经非常普遍地解决了你的问题。这通常很好,因为这意味着您希望在最终需要时更容易改变行为。

在开发较小的项目时,有时最好更具体(即不那么通用)以更快地完成任务,这可能会导致更少的课程,但在需要时可能更难更改。

只要类是有序的并且有明确的目的,拥有很多类应该不是问题。

如果类是紧密耦合的,或者某些类的职责没有很好地定义,那么问题可能会是什么。更多关于耦合的信息可以在这里找到。

下面的评论中提到了另一个可能出现的问题。如果你的很多类都有类似的代码,那么你就会遇到重复问题。这通常会导致系统的可维护性降低,因为如果需要对重复的代码进行更改,则必须多次更改。这通常通过继承来解决。

于 2012-10-14T13:23:05.597 回答
5

肯特贝克回答你的问题。Jeff Langr 在“Clean Code A Handbook of Agile Software Craftsmanship”一书中讨论了 Kent Beck 指定的四个设计规则。

(按重要性排序)

  1. 运行所有测试
  2. 不包含重复
  3. 表达程序员的意图
  4. 最小化类和方法的数量

Kent 建议采用务实的方法来保持类和方法的数量较少。他给出了遵守教条规则的例子,例如所有类都应该有接口。通常是的,但有时在某些情况下可能不需要这样做。但是,此规则是简单设计的四个规则中优先级最低的。

(注意,这是 Kent Becks 的观点,不是我的观点!)

于 2016-07-10T22:38:57.953 回答
3

在我目前正在进行的一个项目中,我绝对认为我们使用了太多的类——或者至少,太多的对象/实例。

我们构建了一个基于 PHP/MySQL 的 CMS,其中数据库中的每个记录和字段都表示为 PHP 中的一个对象。这可能会同时产生数万个实例,并且我们不断遇到性能问题/内存不足等问题。

这当然在其他编程语言或其他要求中可能不是问题,但在我看来,性能也是需要考虑的问题。

于 2013-08-26T12:35:10.223 回答
2

正如许多其他人所建议的那样,“这取决于......”

通常,这取决于您的方法、目标以及团队成员的偏好和能力。如果你对单元测试非常严格,你最终可能会得到很多小的、通用的类和依赖注入。这也意味着单个团队成员很难从所有非常非常通用的部分中看到您正在构建的具体整体。

就我个人而言,我更喜欢从构建在两个层次上的 API 的角度来考虑:一个由通用的独立部分组成的较低层次和一个高层次,我使用几个外观、导向器等来向其他编码人员展示一些具体和有用的东西。这很像 iOS 库恕我直言的设计。

于 2012-10-15T08:03:07.673 回答
2

哲学:

有什么东西太多了。你可以有太多和太少的类。有些人喜欢假装更多是免费的,因为他们可以使用诸如搜索之类的工具作为创建过度混乱、难以导航和大型搜索空间的借口。从长远来看,你总会发现一个可衡量的赤字。

我不确定您可以拥有多少个课程的上限。你可以找到方法来无限地增加类,从技术上讲,无限地。如果你不能有太多的类,但你可以无限添加它们,那么由于你想要拥有的类的数量,你永远不会完成你的程序,因此你可以有太多的类。

许多 IDE 可以很容易地创建大量类并将它们与样板生成、自动完成以及总是有 copypasta 之类的东西串在一起。许多工具降低了创建本质上通常是无用的代码的成本,但并没有降低膨胀的成本。即使有助手,不臃肿的代码总是比臃肿的代码便宜(你只能减少税收,而不是消除它)。如果你不关心它,它最终会成为一个问题。即使您有诸如代码扫描,罚款和文件替换等功能,十倍仍然是十倍。这意味着改变十倍,出错十倍,每行消耗的努力量的十分之一。

许多人陷入了认为他们通过添加更多类来降低复杂性的陷阱。事实上,它们倾向于简单地分解复杂性,将事物从与其相关的事物中移开,并以间接的形式增加复杂性层。线性码变成,

不必要的非线性(这是一个段落过多的例子,尽管公平地说,一个更好的例子可能是每个句子一个段落或一个单词太多,当你的段落变成句子时,你就不再有两个独立的东西了,这可能是两个多段的证明,当句子不再是与段落不同的东西时)。

检测:

看待这一点的简单方法是,如果您有路径 A(单个节点/一个函数/类/等),但将其分解为 A->B,您实际上并没有获得任何收益。你只是拿了一张纸,把它撕成两半,放进两个信封里,然后把它寄到目的地。如果事实证明您确实需要一个具有多个边的节点,那么您将有所收获。例如,那将是 A->B,A->C。您可以使用图形分析来嗅出太多的对象。如果你有很大的长链表或许多小链表(甚至可能只有几个),那么你可能会说你有太多的类。并非所有形式的过度对象都如此容易被发现。如果类太多,维护会变得过于复杂,因为您最终会支持一定程度的灵活性以及您只使用其中一小部分的模型。这意味着你的很多代码实际上并不对应于需要做的事情。仅这一点就很难维护,因为该代码的目标是主观的而不是客观的,它也可能是任意的。

您可以使用代码库并减少类的数量,直到您只拥有实际需要的那些。这意味着只有那些需要可移植性(传递数据)、事物行为方式的差异(传递方法)以及合理分离(独立处理主要概念,如持久性、表示等)以及重复数据删除所需的那些。当无法进行良好的设计时,许多程序员会倒退,编写代码并仅在需要时将其拆分以根据需要满足特定目的。

虽然可以衡量,但没有太多类的精确衡量标准,也没有完美的标志。只有提示。例如,所需的最小类数与最大类数之间的较大比率是一个提示。什么是大?我会说 100 次肯定是可疑的,10 次相当可疑,5 次稍微可疑。这可以根据许多参数进行更改。

一个奇怪的措施是压缩你的代码。压缩比越好,膨胀的可能性就越大。这不是一个完美的衡量标准,因为它需要参考点。某些降低压缩率的方法也可能是徒劳的,天真地编码到特定的数字永远不会奏效。

你可以知道你是否有很多类(或接口),如果它们让你做的工作并不能真正帮助你达到最终目标,或者它们是否让你放慢了速度而不是加快了速度。不过,这可能是主观的。如果有人做了太多的课程,这意味着他们将不得不改变他们的习惯,这意味着通常需要支付入场费才能获得更好的编码方法。在项目开始时,这很难检测到,因为添加代码通常非常便宜。还没有什么比这更依赖它,层次很浅,等等。至少在一个项目开始数月甚至一年之后,膨胀、组织不良等成本才变得明显。直到一个项目实际上陷入僵局,人们才会注意到这一点。许多人也不知道需要一年时间的事情是否真的需要一年或六个月。很少有比较点。

如果你看看你的课程,你可以学到一些东西。OO 有多少代码,FO(面向对象与面向功能)有多少?

面向功能的意思是实际做某事并直接对最终结果有贡献的代码。这包括您必要的代码。它可能包含除赋值和强制转换之外的运算符。通常是条件语句、分支、读取数据、选择行动方案并采取适当的行动方案,例如数据生成、变异或针对 IO 获取/存储。

面向对象意味着简单地使用类来表示概念。这段代码几乎把你的程序代码变成了一种声明性语言。如果你的大多数类只是为了帮助繁重的类型检查、表示等,那么你可能有太多了。这方面的标志是类和方法,它们的部分名称可能是允许您缩小这些类的变量。一个非常强烈的迹象是,如果这些课程的大部分内容是拳击。简单地分配变量,但不做任何其他事情。这实际上是结构过多且通常缺乏重复数据删除、动态编码或抽象的情况。

在明显的情况下,如果一个类根本没有被使用,那么它就是一个太多的类(如果它的死代码)。诸如类相同但名称不同的东西也是一个好兆头。

原因:

除了使创建和连接事物变得非常容易的机制(当您抽象并动​​态地鼓励避免取代但破坏 IDE 的好习惯时,这些机制往往会破坏),这可能是由许多事物驱动的。我经常因为试图绝对完美地表示所有内容而陷入困境,但是 OO 实际上不够灵活,无法做到这一点,而且它经常被证明是 YAGNI。一个常见的问题是缺乏抽象,您通常会将变量展开到顶级语言结构中,如前所述(它还链接到设计以将所有内容直接暴露给 IDE)。这可能不仅暴露了缺乏动态编码,而且还暴露了预处理器或类似的使用。这看起来基本上是一棵定义了所有叶子的树。尽可能使用您希望避免为每个可能的叶子定义一个类的类。一个极端的树扩展标志可能是你有一个类来处理一个基元的每一种可能的用法。这也可能是更极端的迹象。所有类的展开笛卡尔积。在这种情况下,当两者之间通常没有实际差异时,您不会简单地获得 Leg 的类,而是 CatLeg、DogLeg 等的类。有些人可以出于极端主义的类型检查来阻止某人将 DogLeg 放在 CatLeg 上。这是一种令人讨厌且常见的反模式。这也可能是更极端的迹象。所有类的展开笛卡尔积。在这种情况下,当两者之间通常没有实际差异时,您不会简单地获得 Leg 的类,而是 CatLeg、DogLeg 等的类。有些人可以出于极端主义的类型检查来阻止某人将 DogLeg 放在 CatLeg 上。这是一种令人讨厌且常见的反模式。这也可能是更极端的迹象。所有类的展开笛卡尔积。在这种情况下,当两者之间通常没有实际差异时,您不会简单地获得 Leg 的类,而是 CatLeg、DogLeg 等的类。有些人可以出于极端主义的类型检查来阻止某人将 DogLeg 放在 CatLeg 上。这是一种令人讨厌且常见的反模式。

太多课程的最大驱动因素之一是尝试从云中遵循并不真正适用于您的情况的标准。在这种情况下,您不是在针对您的问题进行编程。您正在编程以响应其他人的问题。

这在诸如 SOLID 之类的东西中很常见。了解和理解 SOLID 等原则非常重要,能够应用它们,但知道何时不应用它们也很重要。

在教授具有大型库的 OOP 语言时,会大量使用此原则。如果您正在创建一个希望分发给全世界的 OOP 库,可能有数百万人拥有每个可以想象的用例,那么您希望遵守 OOP 原则,这些原则导致很多事情被分解成接口和类,以便它们可以以不同的方式使用,因此一项功能不会有很高的机会引入另一个可能不需要的功能。你必须考虑到,在这个世界上,你不想创建人们可能不得不分叉的库。人们不想这样做,因为他们随后成为了他们正在重用的代码的维护者,否则这些代码将会非常损失总拥有成本。

这些原则也增加了很多开销,如果你的代码库有一个有限的用户范围,完全在你的控制之下等等,那么如果你按照“你应该的方式”进行分布式处理,那么你可能有太多的代码代码。即使您确实有分布式代码,有时它可能过于极端,无法事先满足所有用例,有时您必须找出最有可能需要的东西,然后其他一切都将按需更改。对于一个小型图书馆,您可以承担大量额外的工作。对于大型代码库,您必须在开销最有可能收回成本的地方进行锻炼。

反例:

在理想的世界中,您只根据您的即时需求进行极简编码。由于过度行为不会自我暴露,因此存在一种偏见。不足之处是。如果你太少,它会直接呈现出来。这在 DRY 中很常见。添加一个功能后,您将添加另一个功能。您复制并粘贴第一个,然后更改下半部分。这两个函数的共同上半部分是重复代码,这立即表明它们需要进行重复数据删除。您可以通过创建第三个函数来做到这一点。您知道这不是一个功能太多,因为您有客观可证明的理由来创建它。在编写供他人使用的代码时,这种方法变得更加困难。我说的其他人不一定是指任何人。我的意思是那些不能直接访问代码库的人,通常是陌生人。本质上是那些不能在需要时轻松/快速/便宜地分解您的代码的人。如果你不迎合这样的受众,那么你就不必担心过早地分解你的代码。

我最近在网上使用了一个课程太少的图书馆。它包含一个具有多种职责的类。它需要一个文件句柄(作为原始类型)来写入,然后根据调用的方法(例如 addImage、addText 等)自动输出适合于它生成的流的 HTTP 标头。

在理想的世界中,这个类不应该对输出做出假设。用户可能希望输出到文件系统、内存、TCP 流等。它只需要提供一个带有简单写入方法的接口或使用标准库中的一个。在我的情况下,我只需要通过字符串连接输出它,但为了实现这一点,我必须打开一个映射到内存的伪文件(这通常是不可能的,但语言允许它作为一种黑客攻击)。

我多次使用来自所有来源的随机库遇到这个问题。在某些情况下,应该在哪里应用分离会很明显,有时则不是。如果有疑问,太少仍然胜过太多,因为您保证最终会发现它。我倾向于观察到,如果你添加任何你不确定的东西,你最终会陷入严重的膨胀领域。如果你做一次,你可能会做两次,然后这就会成为一种习惯。

于 2018-01-24T17:49:43.843 回答
1

一个应用程序可以都在一个代码文件中,或者每个原子化函数都可以在自己的文件中,唯一受影响的是可维护性。可维护性可能意味着您自己浏览代码的能力,也可能意味着其他人如何理解代码,或者是否可以构建新版本。

我认为没有任何通用的指导方针总是适用的,这取决于很多事情。例如,与使用 C# 或 C++ 编码相比,使用 JavaScript 编码时通常使用更少(和更大)的文件,这些文件包含更多不相关的功能。

如果您使用的是 Visual Studio 2012,那么http://msdn.microsoft.com/en-us/library/bb385910.aspxhttp://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
于 2012-10-14T13:30:14.640 回答
1

我认为这取决于哪个区域有大量课程。如果有许多包含公共业务逻辑的静态类,这将被认为是不好的,因为静态类应该只用于公共帮助方法。静态类不应该包含通用的业务逻辑。

如果不同的层有不同的类来保存基本相同的数据。这将被认为是不好的,因为 DTO 类不应跨层复制。

但是,如果在适当分解需求后创建了类,那么我认为拥有大量类实际上是件好事。

于 2012-10-14T13:33:11.227 回答
1

这完全取决于您的项目。这取决于您的要求。
类必须是最小的,没有不需要的类
类必须是最大的,因为它们都分别包含有属性。

于 2012-10-14T13:14:05.050 回答