使抽象类不可见;或者:隐藏我的BananaHuman

时间:2013-08-21 08:31:08

标签: c# inheritance abstract-class

背景:我仍然是C#新手,这是我的第一个继承的大项目。以下故事是我目前情况的简化示例:

假设我有一个名为LivingOrganism的类。每个生物都可以使用这个类作为基础,并继承所有生物体共有的功能。

当我在这些derived课程中工作时,我发现香蕉和人类 are very similar 。虽然它没有多大意义,它们看起来没什么相似之处,但它们显然具有大多数共同的“功能”。

代码重复很糟糕,所以我写了一个新类来降低维护成本。此类称为:BananaHuman。我的Human班级和Banana班级继承自BananaHuman


问题:

我对我的BananaHuman没有任何问题(即我明白这意味着什么以及它存在的原因)。但最终,其他人(即使那些没有(完全)理解继承的人)也必须使用我的 LivingCreatures.dll 。并且他们不会理解为什么 intellisense 在他们输入'B'时建议BananaHuman。

考虑以下代码:

//Valid and makes sense.
foreach(LivingOrganism foo in cityOfNeyYork) { /*embedded statement*/ }

但是想象一下,如果我们用Living Organism替换BananaHuman,会有多奇怪/混乱。

我无法生成BananaHuman privateprotectedprotected internal(命名空间中定义的元素无法以这种方式显式声明)。我也无法将其internal,因为HumanBanana public。如果我尝试这样做,我会收到一条错误消息,指出存在inconsistent accessibility问题。

我觉得我错过了明显的,但我能做什么/应该做什么?我留下了几个选项/问题:

  1. 是否可以“隐藏”BananaHuman以避免混淆?
  2. 我是否应该将BananaHuman重写为非常长的技术,例如DnaRelatedOrganismsType[X],其中“X”描述了它们的独特关系?
  3. 我应该删除BananaHuman,让HumanBanana继承LivingOrganism并在需要更改时进行额外维护吗?
  4. 我还有另一种解决方案吗?
  5. 我四处寻找并且找不到这种情况的“固定模式”。我发现了this一个类似标题的问题,但我不知道答案是否适用,因为看起来他问的是完全不同的东西。

    非常感谢任何帮助!

2 个答案:

答案 0 :(得分:5)

您可以使用EditorBrowsableAttribute并将其应用于您的班级。如果人们使用你的.dll,这将使你的课程从Intellisense中消失。如果引用了项目而不是dll,它仍然可见。

使用类似:

[EditorBrowsable(EditorBrowsableState.Never)]
public class BananaHuman
{
    //....
}

所以如果你给我你的.dll,我不会在Intellisense中看到BananaHuman。但如果我要检查Banana或Human类,我仍然会看到它继承自BananaHuman,因为情况就是这样。 EditorBrowsable属性只是让它从Intellisense中消失,你想要什么。

答案 1 :(得分:0)

现有的答案是一个很好的技术解决方案,可以解决隐藏BananaHuman智能感知的具体问题。但是由于OP也要求改变设计,我认为快速回答为什么BananaHuman的存在是一种代码气味并且它可能应该是一个问题也在问题的范围内。重构的候选人。


您可能听说过五个重要设计原则的SOLID首字母缩略词。 BananaHuman与其中两个相反:单一责任原则(SRP)和开放/封闭原则(OCP)。

香蕉和人类可能共享大量DNA,但就像代码一样,它们也应该被进化,并且可能彼此分开进化。同样的DNA可能并不总是完全共享。 SRP声明一个班级应该只有一个责任或者 - 等同 - 只应该有一个改变的理由。但BananaHuman始终会自动至少有两个可能的原因需要更改 - Banana的规格更改或Human的规格更改。

为什么这是专门针对BananaHuman的情况,而不适用于所有通用基类?因为基类应该代表一个明确定义的概念,就像任何其他类一样。因此,例如Mammal只有在构成哺乳动物概念的特征发生变化时才需要改变。如果一个特定的哺乳动物进化为失去它的头发,那么动物的类会改变,而不是基础Mammal类。另一方面,BananaHuman是定义"香蕉和人类共有的特征",所以它总是至少耦合到两个比一个概念。同样地,香蕉和人类之间可能有几个共同点,彼此之间没有多少关系。把这些全部推到一个班级可以减少凝聚力,并将更多的责任集中到一个地方。

OCP声明软件实体(例如接口,类或方法)应该对扩展开放,但在添加或更改需求时关闭以进行修改。例如,如果您添加了与BananaHuman共享相同特征的其他生物,则您必须更改名称。或者,如果它只共享某些特征,则必须在基类周围进行混乱,如果多次出现,可能会遇到多个继承问题。我确信还有很多其他情况会导致OCP违规。


那么应该你做什么?

好吧,如果您阅读上述内容并认为BananaHuman的特征是不公平的,那实际上它确实映射到一个非常明确的概念,就像Mammal那样......重命名它实际上是什么!这就是你需要做的所有事情,你可能会很高兴。如果名称很长并不重要(虽然理想的简洁性更好,并且你应该确保长度不能表明你将多个东西一起干扰成一系列单词)。

如果那不是答案,那么请研究一下构成优于继承的想法。例如,如果你有多个生命有机体都有肺,而不是创建一个LivingOrganismWithLungs类,创建一个Lungs类,让每个有肺的生物都包含一个实例。如果您可以将常见功能分离到他们自己的类中,那么您可以获得更好的解决方案。

如果这些都不可能(很少见,但可能会发生),那么BananaHuman可能是最好的选择。您必须根据自己的判断来评估SRP和OCP问题与不要重复自己(DRY)违规行为。