当我们尝试通过继承重用代码时如何解决并行继承层次结构

How to Solve Parallel Inheritance Hierarchies when we try to reuse code through inheritance

最近在一次工作面试中,他们问我“当我们尝试通过继承重用代码时如何解决并行继承层次结构”。我考虑过 AggregationComposition,但基于此制作示例时我有点困惑。

所以我决定把它留待以后加深概念,但经过调查并没有最终形成对该问题的准确答案,有人能给我解释一下这个问题的解决方案或例子吗?

Parallel Inheritance Hierarchies 产生了许多不必要的 classes 并使代码非常脆弱和紧密耦合。 例如,我们有 class Sportsman 及其 Goal

public abstract class Sportsman
{
    public string Name { get; set; }

    public abstract string ShowGoal();
}

具体classFootballer:

public class Footballer : Sportsman
{
    public override string ShowGoal()
    {
        return new FootballerGoal().Get();
    }
}

Runner:

public class Runner : Sportsman
{
    public override string ShowGoal()
    {
        return new RunnerGoal().Get();
    }
}

那么我们有他们的目标:

public abstract class Goal
{
    public abstract string Get();
}

及其具体 classes:

public class FootballerGoal : Goal
{
    public override string Get()
    {
        return "Score 1 goal";
    }
}

亚军目标:

 public class RunnerGoal : Goal
{
    public override string Get()
    {
        return "Run 1 km";
    }
}

现在可以看出,如果我们添加新类型的运动员,那么我们需要在 Goal.

的层次结构中添加一个新的 class

我们可以尝试通过使用依赖注入和提取方法来接口来避免创建该层次结构树。

首先,我们创建界面:

public interface IGoal
{
    string Visit(Sportsman sportsman);
}

然后执行它:

public class FootballerGoal : IGoal
{
    public string Visit(Sportsman sportsman)
    {
        return "Score 1 goal";
    }
}

并在 Footballer class:

中使用
public class Footballer : Sportsman
{
    public string ShowGoal(IGoal goal) 
    {
        return goal.Visit(this);
    }
}

现在我们没有层级树,我们可以这样称呼它:

new Footballer().GetGoal(new FootballerGoal())

更新:

There is a good article about Parallel Inheritance Hierarchies.。它提出了一些方法来解决这个任务。让我展示第三种方式。

Solution 3 Collapse a hierarchy.

Pros:

  • Only maintain One hierarchy

  • Easy to maintain

Cons

  • Breaks SRP fairly often.

所以Footballerclass会喜欢这个:

public class Footballer : Sportsman
{
    public override string ShowGoal()
    {
        return new FootballerGoal().Get();
    }
    
    public string GetGoal()
    {
        return "Score 1 goal";
    }
}

Runner class会喜欢这样:

public class Runner : Sportsman
{
    public override string ShowGoal()
    {
        return new RunnerGoal().Get();
    }

    public string GetGoal()
    {
        return "Run 1 km";
    }
}

c2 wiki 有一个关于并行继承层次结构的页面,其中 ChaoKuoLin 列出了四种可能的解决方案。我在这里解释它们,并为每个解释一些上下文。请参阅原始页面以获取完整说明,包括优点、缺点和示例。

  1. 保持平行层次结构。
    • 当层次结构具有不同的职责并且每个层次结构包含许多方法时。
    • 当需要最大的灵活性时。
  2. 保留其中一个层次结构并将另一个折叠成 class。
    • 当其中一个层次结构可以减少为单个层次结构时 class,例如通过将一些方法移动到另一个层次结构。
    • 当其中一个层次结构包含的方法很少时。
  3. 将两个层次结构合二为一。
    • 当层次结构具有相似的职责并且每个层次结构包含的方法很少时。
  4. 保留部分平行层次结构,其余部分折叠。
    • 当你想在之前的解决方案中找到一个中间地带时。

wiki 中建议的另一个解决方案是 Mix In and it is also suggested in How to solve parallel Inheritance in UI case?