views:

346

answers:

2

This is a question about tidyness. The project is already working, I'm satisfied with the design but I have a couple of loose ends that I'd like to tie up.

My project has a plugin architecture. The main body of the program dispatches work to the plugins that each reside in their own AppDomain.

The plugins are described with an interface, which is used by the main program (to get the signature for invoking DispatchTaskToPlugin) and by the plugins themselves as an API contract:

namespace AppServer.Plugin.Common
{
     public interface IAppServerPlugin
     {
        void Register();
        void DispatchTaskToPlugin(Task t);
        // Other methods omitted
     }
}

In the main body of the program Register() is called so that the plugin can register its callback methods with the base class, and then later DispatchTaskToPlugin() is called to get the plugin running.

The plugins themselves are in two parts. There's a base class that implements the framework for the plugin (setup, housekeeping, teardown, etc..). This is where DispatchTaskToPlugin is actually defined:

namespace AppServer.Plugin
{
    abstract public class BasePlugin : MarshalByRefObject,
           AppServer.Plugin.Common.IAppServerPlugin
    {

        public void DispatchTaskToPlugin(Task t)
        {
            // ...
            // Eventual call to actual plugin code
            //
        }

        // Other methods omitted
    }
}

The actual plugins themselves only need to implement a Register() method (to give the base class the delegates to call eventually) and then their business logic.

namespace AppServer.Plugin
{
    public class Plugin : BasePlugin
    {
        override public void Register()
        {
             // Calls a method in the base class to register itself.
        }
        // Various callback methods, business logic, etc...
    }
}

Now in the base class (BasePlugin) I've implemented all kinds of convenience methods, collected data, etc.. for the plugins to use. Everything's kosher except for that lingering method DispatchTaskToPlugin().

It's not supposed to be callable from the Plugin class implementations -- they have no use for it. It's only needed by the dispatcher in the main body of the program. How can I prevent the derived classes (Plugin) from seeing the method in the base class (BasePlugin/DispatchTaskToPlugin) but still have it visible from outside of the assembly?

I can split hairs and have DispatchTaskToPlugin() throw an exception if it's called from the derived classes, but that's closing the barn door a little late. I'd like to keep it out of Intellisense or possibly have the compiler take care of this for me.

Suggestions?

A: 

If the plugins are only really meant to override one method, I suggest you make them implement an interface with just the one method in. Then your current BasePlugin can use composition instead of inheritance to deal with "real" plugin based just on the interface. It may need to pass something into Register so that the plugin can subscribe to events, but that can be expressed as an interface as well, so that the plugin only sees what you want it to.

Jon Skeet
A: 

You could implement DispatchTaskToPlugin privately in BasePlugin by using an explicit implementation

public class Task
{
}

 public interface IAppServerPlugin     
 {        
  void Register();        
  void DispatchTaskToPlugin(Task t);        
 }

abstract public class BasePlugin : MarshalByRefObject, IAppServerPlugin    
{
 public abstract void Register();

 void IAppServerPlugin.DispatchTaskToPlugin(Task t)
 {
 } 
}

public class Plugin : BasePlugin    
{       
 override public void Register()        
 {             
 }   

 public void Something()
 {
  this.DispatchTaskToPlugin(null); // Doesn't compile
  base.DispatchTaskToPlugin(null); // Doesn't compile
 }

}

class Program
{
 static void Main(string[] args)
 {
  Plugin x;
  x.DispatchTaskToPlugin(null); // Doesn't compile
 }
}
marklam
This doesn't seem to do anything useful. In the Plugin class, I can still see DispatchTaskToPlugin as a valid method.
clintp
I've updated the example to show the ways DispatchTaskToPlugin can't be called - are you accessing it some other way?
marklam