一个类中有多少个接口?[关闭]


28

拥有实现23个接口的类,我可能会认为这是一种代码味道,甚至是一种反模式。如果确实是反模式,您会怎么称呼它?还是只是不遵循单一责任原则?


3
好吧,这两种方法都值得怀疑。如果该类有23个只担负单一责任的方法(并非所有类都需要那么多方法,但这并非没有可能-尤其是如果大多数是围绕其他方法的三行便捷包装器),则您的接口太细了; )

所讨论的类是一种实体,并且接口主要是针对属性的。其中有113种,只有四种方法。
乔纳斯·艾夫斯特伦(JonasElfström)2011年

6
我认为接口本质上用于静态编译语言中的鸭子输入。有什么问题?:)
ashes999 2011年

2
只是一个问题:是否为此实体的每个实例填充了全部113个属性?还是在不同的上下文中仅填充了某些属性的某种“公共实体”?
大卫,

1
让我们回到对什么构成“类”的概念上的理解。类是具有明确定义的责任和角色的单一事物。您可以通过这种方式定义23接口对象吗?您能张贴一个足以概括您的课程的(非乔伊斯语)句子吗?如果是这样,那么23个接口就可以了。如果不是,那么它太大,太复杂。
斯蒂芬·格罗斯

Answers:


36

王室:他们没有做任何特别疯狂的事情,但是他们拥有十亿个头衔,并且以某种方式与大多数其他王室成员有联系。


:-))))))))))))))
Emilio Garavaglia

我要强调somehow

23

我认为这个反模式被命名为“万事通”,或者也许是“太多帽子”。


2
瑞士军刀怎么样?
FrustratedWithFormsDesigner

似乎该术语已经用于具有太多方法的接口 :-)尽管在这里也很有意义。
Jalayn

1
@FrustratedWithFormsDesigner瑞士军刀永远不会讨厌拥有名为IValue的接口,该接口包含30个属性,其中20个属性以new修饰符关键字开头。
JonasElfström'11

3
为过多的帽子+1 ...正确隐藏了多头神
ity

1
瑞士军刀是再利用的典范。即,单个“刀片”可以执行may方法,因此可能是一个不好的类比!
詹姆斯·安德森

17

如果必须给它起个名字,我想我叫它Hydra

九头蛇

在希腊神话中,莱纳水牛(Lernaean Hydra,希腊文:ερναίαὝδρα)是一种古老的无名蛇形的长吻水兽,具有爬虫类特征(作为其名字的佐证),拥有许多头-诗人提到的头比花瓶画家所能想象的还要多。油漆,每砍下一个头,它又长出两个-毒气甚至如此致命,甚至她的踪迹都是致命的。

与之特别相关的是,它不仅有很多头脑,而且还在不断增长,而且不可能因此而被杀死。这就是我对这类设计的经验。开发人员只是不断地在其中塞入越来越多的接口,直到它在屏幕上无法容纳为止,到那时,它就深深地植根于程序的设计和假设中,认为将其拆分是无望的前景(如果尝试,实际上通常最终需要更多接口来弥合差距)。

由于“九头蛇”造成的厄运即将来临的一个早期迹象是,频繁地将一个接口投射到另一个接口,而没有进行过多的健全性检查,并且通常将其投射到没有意义的目标,例如:

public void CreateWidget(IPartLocator locator, int widgetTypeId)
{
    var partsNeeded = locator.GetPartsForWidget(widgetTypeId);
    return ((IAssembler)locator).BuildWidget(partsNeeded);
}

很明显,当脱离上下文时,此代码有些混乱,但是发生这种情况的可能性与对象增长的更多“头部”有关,因为开发人员凭直觉知道他们始终在处理同一个生物。

祝您好运对实现23个接口的对象进行任何维护。您不会在此过程中造成附带损害的机会微乎其微。


16

上帝对象想到的; 知道如何做的单个对象。这是由于对两种主要设计方法的“内聚”要求的依从性较低。如果您有一个带有23个接口的对象,则您有一个对象,该对象知道如何使其用户拥有23种不同的事物,而这23种不同的事物很可能不遵循单个任务或系统区域。

在SOLID中,尽管此对象的创建者显然试图遵循接口隔离原则,但他们违反了先前的规则;单一责任原则。有一个原因是它是“固体”;考虑设计时,S始终是第一位的,其他所有规则都遵循。

在GRASP中,此类的创建者忽略了“高凝聚力”规则。与SOLID不同,GRASP教导对象不必具有单一职责,但最多应具有两个或三个非常紧密相关的职责。


我不确定这是上帝对象的反模式,因为上帝不受任何界面的约束。受到如此众多接口的约束可能会降低上帝的无所不能。;)也许这是一个嵌合体对象?
FrustratedWithFormsDesigner

上帝有很多面孔,许多人可以拥有很多东西。如果组合接口的功能使对象“全知”,那不是真的约束上帝,不是吗?
KeithS 2011年

1
除非创建接口的接口对其进行了修改。然后可能需要重新实现上帝以适应界面的变化。
FrustratedWithFormsDesigner

3
您以为是我创建了该类,这很令人讨厌。
乔纳斯·艾夫斯特伦2011年

“您”的使用更多是您的复数形式,指的是您的开发团队。该团队的某人(过去或现在)创建了此类。不过,我会编辑。
KeithS 2011年

8

23只是个数字!在最可能的情况下,它足够高到足以发出警报。但是,如果我们问,在将一个标签称为“反模式”之前,最多的方法/接口是多少?是5还是10或25?您意识到数字实际上不是答案,因为如果10好,11也可以是-然后是任何整数。

真正的问题是关于复杂性。我们应该指出的是冗长的代码,通过任何措施的方法或大尺寸的类的数量实际上是复杂的定义。是的,越来越多的代码(更多的方法)的确使新手难以阅读和掌握。它还处理各种功能的潜在变化,大量的异常情况以及针对各种情况而发展的算法。这并不意味着它很复杂-只是很难消化。

另一方面,人们希望在数小时内进出的相对较小的代码仍然很复杂。这是我认为代码(不必要)复杂的时候。

可以在这里使用面向对象设计的所有智慧来定义“复杂”,但是在这里我将限制显示“太多方法”何时表示复杂性。

  1. 共同知识。(aka耦合) 很多时候,当事物作为类编写时,我们都认为它是“不错的”面向对象的代码。但是关于其他类的假设实质上破坏了实际所需的封装。当您具有“泄漏”有关算法内部状态的深层细节的方法时,将使用有关服务类内部状态的关键假设来构建应用程序。

  2. 重复次数过多(闯入) 当具有相似名称但与工作相矛盾的方法-或与功能相似的名称相矛盾时。很多时候,代码不断发展,以支持针对不同应用程序的稍微不同的接口。

  3. 角色过多 当班级不断增加侧边功能并随着人们的喜欢而不断扩展时,才知道班级确实是两个班级。令人惊讶的是,这些都是从真正的开始要求,并且没有其他班级可以做到这一点。考虑到这一点,有一个类Transaction,它告诉交易细节。到目前为止看起来不错,现在有人需要在“交易时间”(UTC之类)之间进行格式转换,随后,人们添加规则以检查某些事物是否在某些日期以验证无效交易。-我不会写整个故事,但是最终,交易类在其中构建了整个压延机,然后人们开始使用其中的“仅压延机”部分!这是非常复杂的(想像),为什么我要实例化“事务类”以具有“日历”将提供给我的功能!

  4. API的(一致性) 当我执行book_a_ticket()时-我预订了票!不管它要进行多少检查和处理,这都是非常简单的。现在,当时间流开始影响这一点时,它变得很复杂。通常,将允许“搜索”以及可能的可用/不可用状态,然后为了减少后n次操作,您开始在票证中保存一些上下文指针,然后引用该指针来预订票证。搜索不是唯一的功能-在许多此类“附带功能”之后,情况变得更糟。在此过程中,book_a_ticket()的含义意味着book_that_ticket()!而可能是难以想象的复杂。

您可能会在非常进化的代码中看到很多这样的情况,并且我确信很多人都可以添加方案,其中“这么多的方法”根本没有意义或者没有做您显然想的。这是反模式。

我的个人经验是,当项目开始合理地自下而上时,许多本应该由他们自己拥有真正的上课的东西-仍然埋藏或更糟的是,仍然在不同的上课之间分配,并且耦合增加。大多数情况下,本来应该得到10个类,但只有4个类,很可能其中许多类具有多用途的混乱和大量的方法。称其为神与龙,这就是坏。

但是您确实遇到了非常一致的非常大的类,它们确实有30种方法-而且仍然非常干净。他们可以很好。


所涉及的类具有23个接口,这些接口总共包含113个公共属性和四个方法。其中只有一部分是只读的。C#具有自动实现的属性,这就是为什么我在方法和属性之间有所不同的原因msdn.microsoft.com/en-us/library/bb384054.aspx
JonasElfström2011年

7

类应具有正确数量的接口;不多不少

如果不查看所有这些接口在该类中是否有用,那么说“太多”将是不可能的。声明一个对象实现一个接口意味着如果您希望编译该类,则必须实现其方法。(我假设您正在查看的类是这样做的。)如果接口中的所有内容都已实现,并且那些实现相对于该类的内部特性做了某些事情,那么很难说实现不应该存在。我可以想到的唯一情况是,符合那些条件的接口在哪里不属于外部:当没有人使用它时。

某些语言允许使用Java extends关键字之类的机制对接口进行“子类化” ,无论编写该接口的人可能都不知道。也有可能所有23个指标都不够充分,以至于将它们汇总起来是没有意义的。


我认为这个问题与语言无关。实际代码在C#中。
JonasElfström'11

我没有做任何C#,但它似乎支持类似形式的接口继承。
Blrfl 2011年

是的,一个接口可以“继承”其他接口,依此类推。
JonasElfström'11

我发现有趣的是,许多讨论都集中在应该做什么,而不是实例应该做什么上。应当将具有各种图像和音频传感器的机器人固定在一个公共底盘上,将其建模为具有位置和方向的实体,该实体可以看到,听到并关联视觉和声音。这些功能背后的所有真正逻辑可能在其他类别中,但是机器人本身应支持“查看前方是否有物体”,“侦听该区域中的任何噪音”或“查看前方物体是否看起来像是其他物体”这样的方法。产生检测到的声音。”
2014年

特定的机器人很可能应该以特定的方式将其功能细分为子系统,但是在某种程度上,使用该机器人的实用代码不必知道或关心特定的机器人内的功能是如何细分的。如果“眼睛”和“耳朵”作为单独的对象暴露于外部世界,但是相应的传感器位于共享的扩展臂上,则外部代码可能会要求“耳朵”在地面上同时收听声音它要求“眼睛”越过障碍物。
2014年

1

它的声音被认为是每个属性都有一个“ getter” /“ setter”对,这对于典型的“ bean”来说是正常的,并且将所有这些方法推广到了接口。那么如何称其为“ has bean ”。还是在过多豆类的众所周知的影响之后,出现了更多的拉贝赖斯式的“肠胃气胀”。


它在C#中,并且具有自动实现的属性。这23个界面总共包含113个此类属性。
乔纳斯·艾夫斯特伦(JonasElfström)2011年

1

当在不同环境中使用通用对象时,接口的数量通常会增加。在C#中,IComparable,IEqualityComparer和IComparer的变体允许在不同的设置中进行排序,因此您可能最终实现了所有这些设置,其中一些可能不止一次,因为您可以实现通用的强类型版本和非通用的版本,此外,您可以实现多个泛型之一。

让我们以一个示例场景为例,假设一个网上商店,您可以在其中购买信用额度,并可以用它们购买其他东西(照片网站经常使用此方案)。您可能有一个继承相同基础的“ Valuta”类和一个“ Credits”类。Valuta有一些漂亮的运算符重载和比较例程,使您可以进行计算而不必担心“货币”属性(例如,在美元中加磅)。积分分配更简单,但具有其他一些独特的行为。希望能够将它们相互比较,最终可以在两个接口上同时实现IComparable和IComparable以及比较接口的其他变体(即使它们使用通用实现,无论它是在基类中还是在其他地方)。

在实现序列化时,将实现ISerializable,IDeserializationCallback。然后实现撤消重做堆栈:添加了IClonable。IsDirty功能:IObservable,INotifyPropertyChanged。允许用户使用字符串编辑值:IConvertable ...列表可以一直打开...

在现代语言中,我们看到了一种不同的趋势,该趋势有助于将这些方面分开并将它们放在核心课程之外的自己的课程中。然后,通过使用注释(属性)将外部类或方面与目标类相关联。通常可以使外部方面类或多或少具有通用性。

属性(注释)的使用会受到影响。一个缺点是轻微的(初始)性能损失。(通常是情感上的)缺点是诸如封装的原理需要放松。

总会有其他解决方案,但是对于每个漂亮的解决方案,都有一个权衡或陷阱。例如,使用ORM解决方案可能要求将所有属性声明为虚拟。序列化解决方案可能需要您的类使用默认构造函数。如果您使用依赖注入,则可能最终在单个类上实现23个接口。

在我眼中,按照定义,23个接口不必很糟糕。它背后可能有一个经过深思熟虑的方案,或者是一些原则性的决定,例如避免使用反射或极端封装的信念。

每当切换工作或必须建立在现有架构上时。我的建议是首先完全熟悉,不要试图将所有内容重构得太快。听原始的开发人员(如果他还在的话),并尝试弄清楚所看到的内容背后的想法和想法。提出问题时,这样做不是为了分解问题,而是要学习...是的,每个人都有自己的金锤,但是可以收集的锤子越多,与同事的相处就越容易。


0

“太多”是主观的:编程风格?性能?符合标准?先例?只是简单的安慰/自信?

只要您的代码行为正确且不存在可维护性问题,23甚至可能成为新的规范。然后,有一天我可以说:“您可以使用23个界面完成出色的工作,请参阅:JonasElfström”。


0

我要说的是,该限制应小于23,而应大于5或7,
但是,这不包括那些接口继承的任何数量的接口或基类实现的任何数量的接口。

(因此,N +任意数量的继承接口,其中N <=7。)

如果您的类实现了太多的接口,则可能是上帝类

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.