Monday, June 04, 2007
Posted on Monday, June 04, 2007 10:12:28 PM (Mountain Daylight Time, UTC-06:00)  Comments [2] | 
Categories: .NET | Fundamentals | Software

During the ramp up for my current enterprise GIS development project I created a few simple VB.NET sample projects which illustrate some architectural practices I want the team to use.

The first one I'm going to share covers the creation of a plugin framework, and the use of the Supervising Controller pattern to expose user interface logic to unit tests. In order to keep the focus on the pattern, and not on the complexities of what the code is doing, this sample does not use any ArcObjects. It's just a very simple example of how you could create "property page" type functionality that can be extended via plugins.

What is a Plugin-Framework and why would I want one?

Using a plugin framework is a way to design an application so that other functionality can be added at a later time without needing to modify the original source code. Putting this in ArcGIS terms, this is similar to adding commands into ArcMap via it's plugin framework.

If you are building large complex applications, supporting a plugin model essentially enables the system to be extended in a very managed way. Thinking back to ArcGIS - by simply implementing ICommand, you can add alot of functionality into their application, and ESRI does not have to give you the source code, nor do you need to manage a very complex C++ build process.

Supervising Controller Pattern

Just a quick warning - we're diving into formal object oriented design patterns here. Previously known as Model View Presenter, this is a common object oriented design pattern.  Martin Fowler (who invented/named it) has officially retired the "Model View Presenter" pattern, and created two new ones - Supervising Controller and Passive View. You can read up on the details of these patterns by following those links, but the quick and dirty explanation is that they focus on extracting logic from an User Interface (windows or web form) into "controller" classes. (A little pattern nugget to impress your friends with: Autonomous View is the official name for  the "put all the code in the form" pattern) These controllers handle most of the User Interface logic - and since they are regular old classes - they are much easier to write unit tests for.

One of the prime reasons to use Supervising Controller is for testability. Assuming the view is hard to test, by moving any complex logic into the controller, we put the logic in a place that's easier to test. - Martin Fowler

At first blush this seems like more work - and it actually is more coding. BUT - if you write tests for the controllers (not covered in the sample), it will make your code more robust and manageable over the long term.

The Plugin Framework

The plugin part is pretty straight forward. We define an interface for the plugin, and implement it in the classes which we are going to plug-into the application.

This is exactly the same as implemeting ICommand. The difference is that instead of registering a COM class with the windows registry, we add the plugin by editing the host application's config file...

<propertypageplugins>

    <propertypageplugin name="Editor"  type="Research.EditorPluginView, Research.ExamplePlugin"  />

    <propertypageplugin name="Some Different Plugin"  type="Research.EditorPluginView, Research.ExamplePlugin"  />

  </propertypageplugins>

In order to load the plugins into the application we create a configuration section handler - which I've posted about in the past. For more details, I'd suggest checking out this MSDN article by Roy Osherove. I'd also suggest looking at the code, since there are some other interesting bits in there that I'm skipping over to keep this somewhat short.

 
The Host Application

The plugins must plug-into something - thus the host application. This is simply a windows form that will list the registered plugins, and then show the selected plugin interface on the right side of the form. The image below shows it running.


The Plugins & Supervising Controller

The plugins are located in the "ExamplePlugin" assembly. In fact there is only one plugin - "EditorPlugin " - I just register it twice. The pluing itself is a super simple text editor. As I metioned earlier, the plugins themselves implement the Supervising Controller pattern. Typically this is used with "forms", but in order to be pluggable, we are working with user controls. And to be fun, I'm using using inherited user controls.

Inheriting the user control forces a common look and feel - granted in this case, it's very simple (just the group box), but in our real app, it's got a lot more going on.

To keep things clear, I have setup a standard naming convention - the UI (user control in this case) is <somename>View, and the Controller is <somename>Controller. The interface that forms the contract between the two is I<somename>View. Looking at the class model above, we can also see that the View implements IPropertyPluginView - which means that it's the thing that actually plugs into the application.

So in this case we have:

  • EditorPluginView
  • EditorPluginController
  • IEditorPluginView

Since the Controller must manage the UI, one way to setup a contract between the two is using an Interface. The interface is implemented by the View, and consumed by the Controller. The interface simply specifies the methods that the Controller can call on the View (UpdateText and GetText in this case) and the events that the View raises and that the Controller can handle (ContentChanged and Store).


Run Time
When the form is initialized (IPropertyPluginView.Initialize()), it creates it's controller class and passes in a reference to itself as an IEditorPlugin. The Controller then sinks the Events on the interface, reads the file from disk (if it exists) and updates the text on the form. When the user edits the text the IEditorPluginView.ContentChanged event is raised in the form, and the Controller is notified. When the user clicks the store button, the IEditorPluginView.Store event is raised and the Controller gets the content via IEditorPluginView.GetText, and it stores it away.

Even this simple example could be extended, but this is enough to show how it all works.

Conclusion

While this was a whirlwind of terms and concepts, I encourage you to download the code, and play with it. It's actually quite easy to implement once you get the hang of it, and it can help make your applications much more testable. I find it much easier to look at real code than to just read the generic explanations of patterns, so I hope this helps if you are thinking about implemeting a plugin framework or Supervising Controller in .NET

 
Download the code


Other Reading:

The Polymorphic Podcast has a series of great screen casts and audio clips that go over the details of the Model View patterns. I highly recommend these specifically, and the podcast in general.

MSDN Article: Creating a Plugin Framework. This is from 2003, and deals with ASP.NET but the concepts have not changed.

Tuesday, June 05, 2007 11:19:19 AM (Mountain Daylight Time, UTC-06:00)
Dave - This is a very interesting post. I've been reading a great series by Jeremy Miller in a similar vein, except he's attempting to show how good use and understanding of Supervising Controller and Passive View can replace the Microsoft CAB. Either way, it's nice to see a TDD approach being using within an ArcGIS context.

As a matter of interest, what do you think of the various Inversion of Control options available vs. your plugin framework. Obviously slightly different intent, but I've been looking at the a@http://www.castleproject.org@Castle Project's Windsor IoC for some upcoming work.

Anyway, thanks for a timely article.

Dylan Thomas
Woolpert
Dylan Thomas
Tuesday, June 05, 2007 11:21:53 AM (Mountain Daylight Time, UTC-06:00)
Oops. Forgot to put a link to the blog posts I mentioned: Article series
Dylan Thomas
Comments are closed.