Welcome to Manicprogrammer Sign in | Join | Help

Plugins Dependencies

Today we had a heated argument about how to make presenters independent, in the sense that I want to be able to change only one Plugin and let it participate in the others.

The easiest solution I can see is using a Dependency Injection Container (likely Windsor) to perform the injection of Plugins. What do I mean? Let's use an example:

As you can see I have the PluginBase class and three Plugins:

  1. Persistence
  2. Validation
  3. Log

At this moment you're probably thinking: "Hmm... Guess I'll pass on this NMVP thing... The guy can't even add...". Well let's forget the Log4NetPlugin right now ok?

Imagine I am authoring those 3 Plugins. The most logical thing is to make them integrated, since I want the events in both Validation and Persistence plug-ins to get logged when they happen. I also want the presenters that feature BOTH Persistence and Validation Plugins, to perform validation before saving.

So I build my Plugins integrated and both the Persistence and Validation Plugins now use the Log Plugin.

WOW!!! Wait a minute sir, you told me in the beginning that you weren't coupling the Plugins, thus allowing me to inject my own implementation without having to rewrite everything from scratch.

That's right! I did! And I'll show how...  Let's say everything works as a charm, but now I want to enable logging via Log4Net (I told you to forget it for some time, didn't I? Hey IT'S BACK). I just build a Log4Net Plugin that inherits from LogPlugin, and override the log method to call on the Log4Net engine.

Wow! NEAT! But hey you're tricking me! How will the other Plugins know that they should use the Log4Net implementation instead of the original one?

That's where the DI Container comes in handy! In the configuration file for your application you'd specify that whenever a call for LogPlugin is made like this:

HostPresenter.GetPlugin<LogPlugin>()

The container should return the instance of the Log4Net Plugin. You could do it with any other plugin without disrupting the other plugins.

Ok, ok, that's right. If I inherit and override stuff, and whatever it'll work. But if I don't WANT to inherit from your plugin?

Ok, fair enough. I won't force you to do that. You just build your plugin the way you prefer and hook into my plugin's events. So if you want to log the events from the PersistencePlugin, just subscribe them and log whatever you want to, with whatever level of log you need. The PersistencePlugin won't even notice your plugin there (remember, if you don't include the decorator for Log the PersistencePlugin does not call on it).

Hmm... Sounds interesting... But I sincerely smell something... What if I don't want to hook onto those events?

Well I guess I saw that coming. Ok, you don't want to inherit, and you don't want to hook into the event lifecycle? Fair enough. I guess now you can count on the plugins to help you out. The PersistencePlugin for instance, requires a Save method on the presenter. If you need to include a call to your custom plug-in between the OnBeforeSave and OnAfterSave, just use this code in your Presenter:

this.GetPlugin<MyCustomPlugin>().MyCustomMethod(); //This syntax is not defined yet. It's subject to change.

This way you'll still integrate your own plug-in into the PersistencePlugin lifecycle. You'll have to do it in every presenter, though, so this is not a path I'd like to trail.

Nah, you're just making up stuff now.

Hmm... One could say that... But to be technically right, I'm making up everything so far, since none of this is built. There's one last way of using the plugin infrastructure with NMVP. Creating your own plugins that integrate with each other in any way you see fit, and then using them in your presenters. Some immediate reasons I see that you'd benefit from this approach:

  1. Custom-Tailored bundled functionality
  2. High-level of reuse
  3. Standardization of the development process
  4. Productivity

On that last one let me compare the code with and without the plugin infrastructure.

Without

//I want persistence, validation and logging.
public class MyCustomPresenter : Presenter<IMyCustom> {
    private ErrorMessages messages = new ErrorMessages();
    private Logger log;
    
    public MyCustomPresenter(IMyCustom view) {
        this.View = view;
        this.log = new Logger();
    }

    public void Save() {
        log.Log("OnBeforeSave");
        RaiseOnBeforeSave();
        if (Validate()) {
            //do a bunch of persistence code.
            log.Log("OnAfterSave");
            RaiseOnAfterSave();
        }
        else {
            log.Log("OnSaveCancelled");
            RaiseOnSaveCancelled();
            throw new InvalidOperationException(this.messages.ToString());
        }
    }

    public bool Validate() {
        bool isValid = false;

        log.Log("OnBeforeValidate");
        
        //perform a bunch of validation code.
        
        if (isValid) {
            log.Log("OnValidateSuccessful");
            RaiseOnValidateSuccessful();
            return true;
        }
        else {
            log.Log("OnValidateError");
            RaiseOnValidateError();
            return false;
        }
    }

    //implement all the Raise methods.
}

Now the code with NMVP 1.0.0:

[PersistencePlugin]
[ValidationPlugin]
[LogPlugin]
public class MyCustomPresenter : Presenter<IMyCustom> {

    public void Save() {
        this.GetPlugin<LogPlugin>().Log("Custom Save began...");
        //perform persistence.
        this.GetPlugin<LogPlugin>().Log("Custom Save finished...");
    }

    //don't need to include validate method since 
    //I'll rely on AOP Validation.
}

Conclusion

Now who's fooling who friend? You want that don't you? So help us out. Like I told ya before. I'm just making all this up. Before implementing it I want to know that you guys think that's a feasible and useful framework. Please provide feedback. Anything. Call me crazy, lol. I don't care. Hope to hear from you all soon. I know you're out there.

#115

Published Tuesday, August 21, 2007 2:19 AM by heynemann

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

No Comments


Enter the text you see in the image:

Leave a Comment

(required) 
required 
(required)