使用Composition时如何避免子类回调?

时间:2018-02-28 02:58:01

标签: oop inheritance delegates event-listener composition

所以我倾向于赞成组合而不是继承,我希望这个问题的非继承答案。

当超类中有一些代码需要调用子类中的代码时,似乎存在使用组合的情况。这使得不可扩展的继承层次结构首先破坏了使用组合的目的。这是C#中问题的演示(尽管这是一个普遍的oop问题):

public interface IChemistry
{
    void SeparateAtom(Atom atom);
    void BreakBond(Bond bond);
}

public class BaseChemistry : IChemistry
{
    public void SeparateAtom(Atom atom)
    {
        //possible extra logic here
        for(int i=0;i < atom.BondCount;i++)
        {
            //maybe extra logic here etc.
            BreakBond(atom.Bonds[i]);
        }
    }

    public void BreakBond(Bond bond)
    {
        //do some bond breaking logic here
    }
}

public class RealisticChemistry : IChemistry
{
    private BaseChemistry base;

    public RealisticChemistry(BaseChemistry base)
    {
        this.base = base;
    }
    public void SeparateAtom(Atom atom)
    {
        //subclass specific logic here perhaps
        base.SeparateAtom(atom);
    }

    public void BreakBond(Bond bond)
    {
        //more subclass specific logic
        base.BreakBond(bond);
    }
}

正如您在此设计中所看到的,存在一个明显的问题。当子类&#39;调用SeparateAtom()方法,它执行它自己的一些逻辑,然后将其余的委托给基类,然后基类将调用基类上的BreakBond()方法,而不是子类。

我可以为此考虑各种解决方案,而且几乎所有解决方案都有相当大的挫折:

  • 复制并粘贴。在这种情况下最糟糕的选择是简单地复制基类中的循环(和其他逻辑)&#39; SeparateAtom()方法,子类&#39;一。我不觉得有必要解释为什么复制和粘贴不是最佳做法。另一个选择可能是将循环中的一些额外逻辑打包到额外的方法中,这样它就只是复制的循环。但是仍然会复制对其他方法的调用,并且将内容分解为多个方法可能会破坏封装。例如,如果某些逻辑依赖于SeparateAtom()的特定上下文,并且如果被不熟悉代码的人在非上下文中调用,则会导致错误的数据呢?
  • 听取或观察基类中的断键事件。这个解决方案对我来说似乎有问题,因为基类功能应该扩展的方式变得不清楚。例如,如果没有先验知识,如果有人试图扩展类,他们可能会直观地实现上面的设计,并将监听器解释为可选的,而实际上如果想要扩展绑定行为则需要它。
  • 使基类需要委托。例如,基类可能需要引用在IBondBreakDelegate内调用的BondBreak()。这与听众方法有类似的问题,因为组合和其他方法的混合使得基类的预期用法不清楚。此外,即使现在有一个实际需要的委托,从而使预期的用法更加清晰,基类现在不再能够自行运行。此外,如果需要使用附加子类扩展层次结构(例如public class MoreRealistiChemistry等),那么如何通过合成扩展委派行为呢?
  • 委托所有内容而不是撰写。我不想沿着这条路走下去,因为当课程需要额外的功能时,所需的代表数量会增加(或代表中的方法数量会增加)。如果某些委托行为是可选的呢?然后,要么为子类实现的每个行为都需要单独的可选委托,要么最终在子类中有许多空方法体。

一般来说,当我致力于某种设计时,我想全心全意地这样做。当然,在现实世界中有很多警告。但我觉得这个必须如此普遍,以至于有人可能知道一个好的解决方法。有什么想法吗?

1 个答案:

答案 0 :(得分:1)

(由于我的声誉评分很低,我无法添加评论,但我想指出两件事。)

首先,您的代码将无法编译,因为您的类没有实现接口IChemistry

其次,“赞成组合而不是继承”只是一个无意中不加区别地应用的指导原则。您应该考虑解决方案所需的模型,如果面对组合或继承的选择,则更喜欢组合。在这个用例中,继承(更具体地说,专业化)似乎是明智的方法。