拥有实现23个接口的类,我可能会认为这是一种代码味道,甚至是一种反模式。如果确实是反模式,您会怎么称呼它?还是只是不遵循单一责任原则?
拥有实现23个接口的类,我可能会认为这是一种代码味道,甚至是一种反模式。如果确实是反模式,您会怎么称呼它?还是只是不遵循单一责任原则?
Answers:
王室:他们没有做任何特别疯狂的事情,但是他们拥有十亿个头衔,并且以某种方式与大多数其他王室成员有联系。
somehow
我认为这个反模式被命名为“万事通”,或者也许是“太多帽子”。
如果必须给它起个名字,我想我叫它Hydra:
在希腊神话中,莱纳水牛(Lernaean Hydra,希腊文:ερναίαὝδρα)是一种古老的无名蛇形的长吻水兽,具有爬虫类特征(作为其名字的佐证),拥有许多头-诗人提到的头比花瓶画家所能想象的还要多。油漆,每砍下一个头,它又长出两个-毒气甚至如此致命,甚至她的踪迹都是致命的。
与之特别相关的是,它不仅有很多头脑,而且还在不断增长,而且不可能因此而被杀死。这就是我对这类设计的经验。开发人员只是不断地在其中塞入越来越多的接口,直到它在屏幕上无法容纳为止,到那时,它就深深地植根于程序的设计和假设中,认为将其拆分是无望的前景(如果尝试,实际上通常最终需要更多接口来弥合差距)。
由于“九头蛇”造成的厄运即将来临的一个早期迹象是,频繁地将一个接口投射到另一个接口,而没有进行过多的健全性检查,并且通常将其投射到没有意义的目标,例如:
public void CreateWidget(IPartLocator locator, int widgetTypeId)
{
var partsNeeded = locator.GetPartsForWidget(widgetTypeId);
return ((IAssembler)locator).BuildWidget(partsNeeded);
}
很明显,当脱离上下文时,此代码有些混乱,但是发生这种情况的可能性与对象增长的更多“头部”有关,因为开发人员凭直觉知道他们始终在处理同一个生物。
祝您好运,对实现23个接口的对象进行任何维护。您不会在此过程中造成附带损害的机会微乎其微。
在上帝对象想到的; 知道如何做的单个对象。这是由于对两种主要设计方法的“内聚”要求的依从性较低。如果您有一个带有23个接口的对象,则您有一个对象,该对象知道如何使其用户拥有23种不同的事物,而这23种不同的事物很可能不遵循单个任务或系统区域。
在SOLID中,尽管此对象的创建者显然试图遵循接口隔离原则,但他们违反了先前的规则;单一责任原则。有一个原因是它是“固体”;考虑设计时,S始终是第一位的,其他所有规则都遵循。
在GRASP中,此类的创建者忽略了“高凝聚力”规则。与SOLID不同,GRASP教导对象不必具有单一职责,但最多应具有两个或三个非常紧密相关的职责。
23只是个数字!在最可能的情况下,它足够高到足以发出警报。但是,如果我们问,在将一个标签称为“反模式”之前,最多的方法/接口是多少?是5还是10或25?您意识到数字实际上不是答案,因为如果10好,11也可以是-然后是任何整数。
真正的问题是关于复杂性。我们应该指出的是冗长的代码,通过任何措施的方法或大尺寸的类的数量实际上是不复杂的定义。是的,越来越多的代码(更多的方法)的确使新手难以阅读和掌握。它还处理各种功能的潜在变化,大量的异常情况以及针对各种情况而发展的算法。这并不意味着它很复杂-只是很难消化。
另一方面,人们希望在数小时内进出的相对较小的代码仍然很复杂。这是我认为代码(不必要)复杂的时候。
可以在这里使用面向对象设计的所有智慧来定义“复杂”,但是在这里我将限制显示“太多方法”何时表示复杂性。
共同知识。(aka耦合) 很多时候,当事物作为类编写时,我们都认为它是“不错的”面向对象的代码。但是关于其他类的假设实质上破坏了实际所需的封装。当您具有“泄漏”有关算法内部状态的深层细节的方法时,将使用有关服务类内部状态的关键假设来构建应用程序。
重复次数过多(闯入) 当具有相似名称但与工作相矛盾的方法-或与功能相似的名称相矛盾时。很多时候,代码不断发展,以支持针对不同应用程序的稍微不同的接口。
角色过多 当班级不断增加侧边功能并随着人们的喜欢而不断扩展时,才知道班级确实是两个班级。令人惊讶的是,这些都是从真正的开始要求,并且没有其他班级可以做到这一点。考虑到这一点,有一个类Transaction,它告诉交易细节。到目前为止看起来不错,现在有人需要在“交易时间”(UTC之类)之间进行格式转换,随后,人们添加规则以检查某些事物是否在某些日期以验证无效交易。-我不会写整个故事,但是最终,交易类在其中构建了整个压延机,然后人们开始使用其中的“仅压延机”部分!这是非常复杂的(想像),为什么我要实例化“事务类”以具有“日历”将提供给我的功能!
API的(一致性) 当我执行book_a_ticket()时-我预订了票!不管它要进行多少检查和处理,这都是非常简单的。现在,当时间流开始影响这一点时,它变得很复杂。通常,将允许“搜索”以及可能的可用/不可用状态,然后为了减少后n次操作,您开始在票证中保存一些上下文指针,然后引用该指针来预订票证。搜索不是唯一的功能-在许多此类“附带功能”之后,情况变得更糟。在此过程中,book_a_ticket()的含义意味着book_that_ticket()!而这可能是难以想象的复杂。
您可能会在非常进化的代码中看到很多这样的情况,并且我确信很多人都可以添加方案,其中“这么多的方法”根本没有意义或者没有做您显然想的。这是反模式。
我的个人经验是,当项目开始合理地自下而上时,许多本应该由他们自己拥有真正的上课的东西-仍然埋藏或更糟的是,仍然在不同的上课之间分配,并且耦合增加。大多数情况下,本来应该得到10个类,但只有4个类,很可能其中许多类具有多用途的混乱和大量的方法。称其为神与龙,这就是坏。
但是您确实遇到了非常一致的非常大的类,它们确实有30种方法-而且仍然非常干净。他们可以很好。
类应具有正确数量的接口;不多不少
如果不查看所有这些接口在该类中是否有用,那么说“太多”将是不可能的。声明一个对象实现一个接口意味着如果您希望编译该类,则必须实现其方法。(我假设您正在查看的类是这样做的。)如果接口中的所有内容都已实现,并且那些实现相对于该类的内部特性做了某些事情,那么很难说实现不应该存在。我可以想到的唯一情况是,符合那些条件的接口在哪里不属于外部:当没有人使用它时。
某些语言允许使用Java extends
关键字之类的机制对接口进行“子类化” ,无论编写该接口的人可能都不知道。也有可能所有23个指标都不够充分,以至于将它们汇总起来是没有意义的。
它的声音被认为是每个属性都有一个“ getter” /“ setter”对,这对于典型的“ bean”来说是正常的,并且将所有这些方法推广到了接口。那么如何称其为“ has bean ”。还是在过多豆类的众所周知的影响之后,出现了更多的拉贝赖斯式的“肠胃气胀”。
当在不同环境中使用通用对象时,接口的数量通常会增加。在C#中,IComparable,IEqualityComparer和IComparer的变体允许在不同的设置中进行排序,因此您可能最终实现了所有这些设置,其中一些可能不止一次,因为您可以实现通用的强类型版本和非通用的版本,此外,您可以实现多个泛型之一。
让我们以一个示例场景为例,假设一个网上商店,您可以在其中购买信用额度,并可以用它们购买其他东西(照片网站经常使用此方案)。您可能有一个继承相同基础的“ Valuta”类和一个“ Credits”类。Valuta有一些漂亮的运算符重载和比较例程,使您可以进行计算而不必担心“货币”属性(例如,在美元中加磅)。积分分配更简单,但具有其他一些独特的行为。希望能够将它们相互比较,最终可以在两个接口上同时实现IComparable和IComparable以及比较接口的其他变体(即使它们使用通用实现,无论它是在基类中还是在其他地方)。
在实现序列化时,将实现ISerializable,IDeserializationCallback。然后实现撤消重做堆栈:添加了IClonable。IsDirty功能:IObservable,INotifyPropertyChanged。允许用户使用字符串编辑值:IConvertable ...列表可以一直打开...
在现代语言中,我们看到了一种不同的趋势,该趋势有助于将这些方面分开并将它们放在核心课程之外的自己的课程中。然后,通过使用注释(属性)将外部类或方面与目标类相关联。通常可以使外部方面类或多或少具有通用性。
属性(注释)的使用会受到影响。一个缺点是轻微的(初始)性能损失。(通常是情感上的)缺点是诸如封装的原理需要放松。
总会有其他解决方案,但是对于每个漂亮的解决方案,都有一个权衡或陷阱。例如,使用ORM解决方案可能要求将所有属性声明为虚拟。序列化解决方案可能需要您的类使用默认构造函数。如果您使用依赖注入,则可能最终在单个类上实现23个接口。
在我眼中,按照定义,23个接口不必很糟糕。它背后可能有一个经过深思熟虑的方案,或者是一些原则性的决定,例如避免使用反射或极端封装的信念。
每当切换工作或必须建立在现有架构上时。我的建议是首先完全熟悉,不要试图将所有内容重构得太快。听原始的开发人员(如果他还在的话),并尝试弄清楚所看到的内容背后的想法和想法。提出问题时,这样做不是为了分解问题,而是要学习...是的,每个人都有自己的金锤,但是可以收集的锤子越多,与同事的相处就越容易。
我要说的是,该限制应小于23,而应大于5或7,
但是,这不包括那些接口继承的任何数量的接口或基类实现的任何数量的接口。
(因此,N +任意数量的继承接口,其中N <=7。)
如果您的类实现了太多的接口,则可能是上帝类。