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

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

提问人:Leandro Toloza 提问时间:2/24/2022 更新时间:2/27/2022 访问量:859

问:

最近在一次面试中,他们问我“当我们试图通过继承重用代码时,如何解决并行继承层次结构”。我想到了聚合组合,但我对基于此制作一个示例有点困惑。

所以我决定把它留待以后再深化概念,但在调查之后,它最终并没有形成这个问题的确切答案,有人可以向我解释一个解决方案或例子吗?

OOP 继承设计 模式 语言无关

评论


答:

3赞 StepUp 2/25/2022 #1

并行继承层次结构使许多不必要的类,并使代码非常脆弱且紧密耦合。 例如,我们有 class 和它的 's。SportsmanGoal

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

    public abstract string ShowGoal();
}

和混凝土类:Footballer

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();
}

及其具体类别:

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

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

首先,我们创建接口:

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

然后实现它:

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

并在课堂上使用它:Footballer

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

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

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

更新:

有一篇关于并行继承层次结构的好文章。它提出了一些解决此任务的方法。让我展示第三种方式。

解决方案 3:折叠层次结构。

优点:

  • 仅维护一个层次结构

  • 易于维护

缺点

  • 经常中断 SRP。

所以班级是这样的:Footballer

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

班级会这样:Runner

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

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

评论

0赞 jaco0646 2/25/2022
最后一行代码仍然显示并行层次结构。这似乎解决不了任何问题。
0赞 StepUp 2/25/2022
@jaco0646请您详细说明一下它是如何表现出平行层次结构的吗?
0赞 Julian 9/18/2023
他是对的。您仍然有相同的 4 个类。这很好,但是您对接口的引入使事情变得混乱,但没有任何改变。至少不是关于平行层次结构,你可能有更松散的耦合,但我看不出这一点,因为无论如何,它总是相同的两个类相互交谈。
1赞 jaco0646 2/25/2022 #2

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

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

wiki 中建议的另一个解决方案是 Mix In,在如何解决 UI 情况下的并行继承?