6

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

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

当我在学习其中一些derived课程时,我发现香蕉和人类非常相似。尽管它没有多大意义,而且它们看起来一点也不像,但它们显然具有大部分共同的“功能”。

代码重复是不好的,所以我写了一个新类来降低维护成本。这个类被称为:BananaHuman。我的Human班级和Banana班级继承自BananaHuman.


问题:

我的 BananaHuman 没有问题(即我理解它的含义以及它存在的原因)。但最终,其他人(甚至那些不(完全)理解继承的人)将不得不使用我的LivingCreatures.dll。他们不明白为什么当他们输入“B”时智能感知会提示 BananaHuman。

并考虑以下代码:

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

但是想象一下,如果我们Living OrganismBananaHuman.

我不能 make BananaHuman privateprotected或者protected internal(命名空间中定义的元素不能以这种方式显式声明)。我也做不到,internal因为Human而且Banana 必须public。如果我尝试这个,我会收到一条错误消息,指出存在inconsistent accessibility问题。

我觉得我错过了显而易见的事情,但我能/应该做什么?我有几个选项/问题:

  1. 是否可以“隐藏”BananaHuman以避免混淆?
  2. 我是否应该重写BananaHuman一些非常长且技术性的东西,例如DnaRelatedOrganismsType[X]“X”描述它们的独特关系?
  3. 当需要更改某些内容时,我是否应该删除BananaHuman、让HumanBanana继承并进行额外的维护?LivingOrganism
  4. 还有另一种我完全想念的解决方案吗?

我四处搜寻,找不到适合这种情况的“固定模式” 。我发现这个问题的标题相似,但我不知道答案是否适用,因为他似乎在问一些完全不同的问题。

任何帮助是极大的赞赏!

4

2 回答 2

5

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

像这样使用:

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

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

于 2013-08-21T08:36:38.177 回答
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) 违规行为必须由您自己判断。

于 2014-06-12T23:35:41.653 回答