http://www.divil.co.uk/net/articles/plugins/plugins.asp
For anyone who has to do some .Net stuff, this is useful to know.
Late binding plugin example for .Net
-
- Minion to the Exalted Pooh-Bah
- Posts: 2790
- Joined: Fri Jul 18, 2003 2:28 pm
- Location: Irvine, CA
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.
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.
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.
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.
-
- Minion to the Exalted Pooh-Bah
- Posts: 2790
- Joined: Fri Jul 18, 2003 2:28 pm
- Location: Irvine, CA
Oh yeah, you still need to use interface but use attribute for discovery. Here is how it might work
And the plugin
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.
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();
}
Code: Select all
[Plugin("name", "description")]
public class Factory : IFactory { /*...*/ }
private class JoeSucksFactory : IFactory { /*...*/ }
private class VinnySucksFactory : IFactory { /*...*/ }
His code does check for public classes, but I still think it's not as elegant.
Last edited by Peijen on Thu Jun 22, 2006 2:40 pm, edited 1 time in total.
-
- Minion to the Exalted Pooh-Bah
- Posts: 2790
- Joined: Fri Jul 18, 2003 2:28 pm
- Location: Irvine, CA
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.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.
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.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.
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).