2

I want the inherited method Invoke() to be hidden from the final subclass ConcreteExecutablePlugin.

Whole situation:

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
     }
}

I have found only one solution. Now, I'm using sealed for this. What do you think about this solution?

Do you know another ways to hide abstract inheritance method?

StuartLC
  • 104,537
  • 17
  • 209
  • 285
isxaker
  • 8,446
  • 12
  • 60
  • 87

2 Answers2

2

Re: Visibility

A public method signals the design intention that it is visible to all - if this isn't the designed intention, change the method's visibility accordingly, e.g. protected (but obviously any subclass has access), or if all classes which are allowed to use Invoke are in the same assembly, then Invoke can be declared protected internal abstract.

Re: Sealed

As per Lasse's point, sealed override methods will disrupt the polymorphic virtual / override chain during inheritance, but still, it can not change the fact that the base method is public. However, applying sealed to a class will prevent other classes from inheriting it all, thus restricting access to all protected methods.

Solution

I believe the underlying problem relates to over-using inheritance - seemingly you want to inherit functionality to obtain reuse, but at the same time need to restrict access at some point in the chain to an "untrustworthy" subclass. Other than the point about making methods internal + moving all "trustworthy" subclasses into the base class assembly, you will have little control when using a full chain of subclasses.

I believe that decoupling your hierarchy via interfaces, and applying the principle of composition over inheritance, will better achieve what you are after. In fact, the Decorator pattern looks to be an option here.

You can also set the 'trustworthiness' boundary by making the 'last trustworthy' subclass (ExecutableJobPlugin) as sealed *.

Example:

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

Notes

  • Because ConcreteExecutablePlugin is no longer a subclass of PluginBase, if you change method PluginBase.Invoke to protected, that ConcreteExecutablePlugin will have no access to it (aside from hacks like reflection).
  • All 'reused' methods and properties needed from the composed (née base) class ExecutableJobPlugin need to be rewired in the ConcreteExecutablePlugin. Although somewhat tedious, it does allow for additional interception, e.g. cross cutting concerns like logging.
  • The ExecutableJobPlugin class may no longer be abstract, since an instance will be needed for the composition to work.
  • Ideally, the ExecutableJobPlugin should be injected externally (as opposed to new within)
  • Decoupling via interfaces improves the testability of your class hierarchy
  • * Sealing ExecutableJobPlugin won't however prevent others from subclassing public superclasses like PluginBase and JobPlugin. To prevent this, you could keep all the base classes in the same assembly and mark these as internal, or continue to apply the interface decoupling / Decorator pattern instead of inheritance across the entire chain.

The pattern could obviously be repeated for multiple levels of your class hierarchy, and the interface segregation principle should be applied to ensure that your interfaces remain lean and focused.

Community
  • 1
  • 1
StuartLC
  • 104,537
  • 17
  • 209
  • 285
0

It is impossible to hide public methods in derived classes. The whole point of public is that the method is accessible in the base class and any descendants of that class.

You could try using protected, and then simply have a public method like Initialize in your derived plugins, or you could try using internal (although that would probably not be preferable).

Jashaszun
  • 9,207
  • 3
  • 29
  • 57