【问题标题】:Looking for other ways to hide abstract inheritance method from a subclass?寻找其他方法来隐藏子类的抽象继承方法?
【发布时间】:2015-07-22 15:18:57
【问题描述】:

我希望继承方法 Invoke() 对最终子类 ConcreteExecutablePlugin 隐藏。

整体情况:

public abstract class PluginBase
{
     public abstract void Invoke(IDictionary parameters);
}

public abstract class JobPlugin : PluginBase
{
     protected void CheckParameter(){//....}
     public bool IsActive(){//....}
}

public class ConcreteJobPlugin : JobPlugin
{
      public override void Invoke(IDictionary parameters){//...}   
}

public abstract class ExecutableJobPlugin : JobPlugin 
{
     protected abstract void Initialize(IDictionary parameters);
     public sealed override void Invoke(IDictionary parameters)
     {
          //final realization of Invoke() method
     }
}

public class ConcreteExecutablePlugin : ExecutableJobPlugin
{
     //here i want method Invoke() has been already hiden
     //!!!OR use base.Invoke() better?

     protected override void Initialize(IDictionary parameters)
     {
           //concrete plugin initialization
     }
}

我只找到了一种解决方案。现在,我正在为此使用密封。您如何看待这个解决方案?

你知道隐藏抽象继承方法的另一种方法吗?

【问题讨论】:

  • 基本抽象方法被声明为public,这表明它在设计上是有意可见的?
  • @StuartLC,实际上,我已经有了更多层次的继承
  • 您不能在派生类型中隐藏公共方法。或许应该改成protected 或者通过接口实现?
  • @LasseV.Karlsen 你觉得使用密封解决这个问题怎么样?
  • 您仍然无法隐藏您继承的公共方法。使类密封只会阻止声明新的后代类型。

标签: c# oop inheritance overriding abstract


【解决方案1】:

回复:可见性

公共方法表明它对所有人可见的设计意图 - 如果这不是设计意图,请相应地更改方法的可见性,例如protected(但显然任何子类都可以访问),或者如果允许使用Invoke 的所有类都在同一个程序集中,则可以将Invoke 声明为protected internal abstract

回复:密封

根据 Lasse 的观点,sealed override 方法会在继承过程中破坏多态 virtual / override 链,但它仍然不能改变基方法是公共的这一事实。但是,将sealed 应用于一个类将阻止其他类继承它,从而限制对所有受保护方法的访问。

解决方案

我认为根本问题与过度使用继承有关 - 看似您想继承功能以获得重用,但同时需要将链中某个点的访问限制为“不可信”子类。除了关于创建方法internal + 将所有“值得信赖的”子类移动到基类程序集中这一点之外,在使用完整的子类链时您几乎无法控制。

我相信通过接口解耦你的层次结构,并应用composition over inheritance的原则,会更好地实现你所追求的。事实上,Decorator pattern 看起来是一个选项。

您还可以通过将“最后一个可信赖”子类 (ExecutableJobPlugin) 设为 sealed * 来设置“可信赖性”边界。

示例:

// Expose  just what is visible to your final Subclass on the interface
public interface IExecutableJobPlugin
{
    bool IsActive { get; set; }
    void CheckParameter();
    void Initialize(IDictionary parameters);
}

// Sealed will prevent other classes from inheriting this class.
public sealed class ExecutableJobPlugin : JobPlugin, IExecutableJobPlugin 
{
    // Default implementation. NB, not abstract
    public void Initialize(IDictionary parameters) {}

    // This isn't visible on the interface
    protected override sealed void Invoke(IDictionary parameters)
    {
        //final realization of Invoke() method
    }
}

public class ConcreteExecutablePlugin : IExecutableJobPlugin
{
    // Compose a decoupled IExecutableJobPlugin instead of direct inheritance
    private readonly IExecutableJobPlugin _wrappedJobPlugin;
    public ConcreteExecutablePlugin(IExecutableJobPlugin wrapped)
    {
        _wrappedJobPlugin = wrapped;
    }

    // Invoke() isn't on the interface so cannot be accessed here
    public void Initialize(IDictionary parameters)
    {
        // Call 'super' if needed.
        _wrappedJobPlugin.Initialize(parameters);
        //concrete plugin initialization code here ...
    }

    public bool IsActive
    {
        get { return _wrappedJobPlugin.IsActive; }
        set { _wrappedJobPlugin.IsActive = value; }
    }

    public void CheckParameter()
    {
        _wrappedJobPlugin.CheckParameter();
    }
}

备注

  • 因为ConcreteExecutablePlugin 不再是PluginBase 的子类,如果您将方法PluginBase.Invoke 更改为protectedConcreteExecutablePlugin 将无法访问它(除了反射等黑客攻击)。
  • 组合(née 基)类ExecutableJobPlugin 所需的所有“重用”方法和属性都需要在ConcreteExecutablePlugin 中重新连接。虽然有些乏味,但它确实允许额外的拦截,例如横切关注点,例如日志记录。
  • ExecutableJobPlugin 类可能不再是抽象的,因为组合工作需要一个实例。
  • 理想情况下,ExecutableJobPlugin 应该从外部注入(而不是 new 在内部注入)
  • 通过接口解耦提高了类层次结构的可测试性
  • * 密封 ExecutableJobPlugin 不会阻止其他人继承公共超类,如 PluginBaseJobPlugin。为了防止这种情况,您可以将所有基类保留在同一个程序集中并将它们标记为internal,或者继续应用接口解耦/装饰器模式,而不是在整个链中继承。

该模式显然可以在您的类层次结构的多个级别重复,并且应该应用interface segregation principle 以确保您的界面保持精简和集中。

【讨论】:

    【解决方案2】:

    在派生类中隐藏公共方法是不可能的。 public 的全部意义在于该方法可以在基类该类的任何后代中访问。

    您可以尝试使用protected,然后在派生插件中简单地使用Initialize 之类的public 方法,或者您可以尝试使用internal(尽管这可能不是更可取的)。

    【讨论】:

    • 我添加了更多信息
    猜你喜欢
    • 1970-01-01
    • 2015-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多