Page 1 of 1

Late binding plugin example for .Net

Posted: Tue Jun 20, 2006 4:48 am
by Jason
http://www.divil.co.uk/net/articles/plugins/plugins.asp

For anyone who has to do some .Net stuff, this is useful to know.

Posted: Tue Jun 20, 2006 5:52 pm
by Peijen
Not too bad. I would've done it slightly differently.

First I would use events and callbacks rather than IHost implementation. IHost increases coupling for no good reason. Using events and callbacks the host is the consumer while the plugin is the producer no mixing role. With IHost both host and plugin are consumer and producer at the same time.

Second I would use attribute rather than which interface is implemented for plugin discovery. If you want to use any sort of delegation pattern such as chain of responsibility or flyweight you can't hide private classes because they might implement the same interface. Attribute also allows you to add extra information about the plugin.

Posted: Wed Jun 21, 2006 3:12 am
by Jason
I don't quite understand what you mean. Remind me to ask you about this the next time I see you.

Posted: Wed Jun 21, 2006 11:03 pm
by George
I was playing around with stuff like this for one of my many never-finished test tools. I think I may even have read this article as part of that. It's a very cool idea, but unfortunately I got bogged down writing one component and gave up.

I agree with Peijen's comment about the IHost being pointless for most plugins.

The use of attributes for discovery is interesting, though I think you'd still have to use a common interface per plugin category to be practical. I don't think being aware of the "hidden" class is a problem at all. The entire point of a plugin is that the program is able to work with anything matching the interface. If Plugins A and B both implement your interface, why should you be denied access to Plugin B just because A happens to call it? Limiting choices of plugins should be done by the application, not by the plugin architecture.

Posted: Thu Jun 22, 2006 2:29 pm
by Peijen
Oh yeah, you still need to use interface but use attribute for discovery. Here is how it might work

Code: Select all

public class PluginAttribute : Attribute
{ // implement some properties for the attribute }

public interface IObjectA {}
public interface IObjectB {}
public interface IFactory
{
  IObjectA CreateObjectA();
  IObjectB CreateObjectB();
}
And the plugin

Code: Select all

[Plugin("name", "description")]
public class Factory : IFactory { /*...*/ }

private class JoeSucksFactory : IFactory { /*...*/ }
private class VinnySucksFactory : IFactory { /*...*/ }
Even though JoeSucksFactory and VinnySucksFactory are marked as private they are still visible through Reflection. With attribute, only Factory class would be visible and instantiated by the host, and the Factory class can determine which inner factory to use base on whatever parameter you choose.

His code does check for public classes, but I still think it's not as elegant.

Posted: Thu Jun 22, 2006 2:35 pm
by Peijen
George wrote:I don't think being aware of the "hidden" class is a problem at all. The entire point of a plugin is that the program is able to work with anything matching the interface. If Plugins A and B both implement your interface, why should you be denied access to Plugin B just because A happens to call it? Limiting choices of plugins should be done by the application, not by the plugin architecture.
Let's say your plugin does something with a text editor. You wanted it to behave differently depend on which language is in the text editor. With the example I gave you can have CShareFactory and VBFactory, but you don't necessary want both to be present to elimited jerky mouse. The problem is even more critical if when you run VB plugin for C# causes the computer to be reformatted or something equally bad.

Posted: Fri Jun 23, 2006 1:39 am
by Jason
Well, I still don't understand ...

Posted: Sat Jun 24, 2006 12:28 pm
by George
Peijen wrote:Let's say your plugin does something with a text editor. You wanted it to behave differently depend on which language is in the text editor. With the example I gave you can have CShareFactory and VBFactory, but you don't necessary want both to be present to elimited jerky mouse. The problem is even more critical if when you run VB plugin for C# causes the computer to be reformatted or something equally bad.
Hmm, I'd always believed that implementing an interface should guarantee certain behaviors/expectations beyond just the method signature. It seems like to make a decision, you must be passing some kind of context to your DecisionMakerFactory. But the same context passing must be present in VBFactory (it's the same interface, otherwise we wouldn't be having this discussion). If DecisionMakerFactory is the only class aware of VBFactory's limitations, then VBFactory is silently ignoring that context and not behaving according to the interface. It seems like what you really want is DecisionMakerFactory to implement IPlugin and VBFactory and CSharpFactory to implement the IFactory. IPlugin and IFactory might have similar signatures, but the expectations are different so they can't be the same type.

If I wanted the plugins to be chosen automatically, I would have probably implemented a method along the lines of bool DoISupportThisSituation(SituationDescription) and queried them to decide which to use. That way, each class is aware of its own limitations. In the past, I've always used UI or configuration files (not the standard .NET ones) to determine which plugins to load and used a query method to determine when to use them (though usually I cached the results by creating some kind of list).

Posted: Wed Apr 04, 2007 10:50 pm
by Peijen