Friday, February 27, 2009

SpecificationManager improvements

So yeah, I can't leave well enough alone.

I liked where I was going with my last post, but thought I could make it a bit better.  Why should a view have a "FailedSpecifications" property? It's nice, it's convenient, but really, what does that mean? You'll ALLWAYS be setting some Error message property, right?

So I did this...

 

    7  /// <summary>

    8     /// An interface for Views that have <see cref="Specification{T}"/>

    9     /// </summary>

   10     /// <typeparam name="T"></typeparam>

   11     public interface IValidatedView<T>

   12     {

   13         /// <summary>

   14         /// The Error Messages

   15         /// </summary>

   16         String ErrorMessage { set; }

   17 

   18         /// <summary>

   19         /// Should probably just return GetLocalResourceObject(key) here

   20         /// </summary>

   21         /// <param name="key"></param>

   22         /// <returns></returns>

   23         String GetErrorMessage(string key);

   24     }

 

I WISH that Page.GetLocalResourceObject was public (yeah, I know why it's not.. but it would be cool for this)... That's the idea, you just return GetLocalResourceObject(key) for the method. Maybe I'll play with this later and do a little reflection magic and not require the GetErrorMessage property, but this works for now...

Why the Type param? It'll get used by the SpecificationManager to find the right specs... I might be able to remove it, hadn't thought about it till just now! I'll look into that too.

Anyway, I digress... So now I want Presenter<T> to do all the work of setting ErrorMessage instead of making the implementer of the view do all that silly looping through the FailedSpecs...

Stub stuff for unit test consumption....

    4 namespace MyNamespace

    5 {

    6     public class ITestView { }

    7     public interface ITestValidatedView:IValidatedView<ITestValidatedView>{}

    8     public class FailedTestSpec:Specification<ITestValidatedView>{

    9         public override bool IsSatisfiedBy(ITestValidatedView obj)

   10         {

   11             return false;

   12         }

   13     }

 

Unit Tests....

 

    9  [TestFixture]

   10     public class PresenterTestFixture

   11     {

   12         private MockRepository mocker;

   13         private ITestValidatedView view;

   14         private Presenter<ITestValidatedView> presenter;

   15         private const string failedSpecName = "FailedTestSpec";

   16         private const string errorMessage = "ErrorMessage";

   17 

   18         public void Arrange()

   19         {

   20             mocker = new MockRepository();

   21             view = mocker.DynamicMock<ITestValidatedView>();

   22             SetupResult.For(view.GetErrorMessage(failedSpecName)).Return(errorMessage);

   23         }

   24         public void Act()

   25         {

   26             presenter = mocker.PartialMock<Presenter<ITestValidatedView>>(view);

   27             mocker.ReplayAll();

   28         }

   29         [Test]

   30         public void ViewIsValidSetsErrorMessage()

   31         {

   32             Arrange();

   33             view.ErrorMessage = errorMessage;

   34             Act();

   35             presenter.ViewIsValid();

   36             mocker.VerifyAll();

   37         }

   38         [Test]

   39         public void ViewIsValid_returns_false()

   40         {

   41             Arrange();

   42             Act();

   43             Assert.That(presenter.ViewIsValid(),Is.False);

   44         }

   45     }

 

So all that's happening is that I'm making the concrete implementation if ITestValidatedView's GetErrorMessage always return a string that I can do assertions against. The Presenters ViewIsValid() method should set the ErrorMessage to whatever the view returns for GetErrorMessage for that failed spec. Of course, I've got the unit test that makes sure that ViewIsValid() returns the right value.

 

I really need to right one for a valid view too... I'll get to it, I promise.

 

Here's the code of interest in Presenter

 

   33 /// <summary>

   34         /// Validates the View, also Sets Failed Messages if the View is <see cref="IValidatedView{T}"/>

   35         /// </summary>

   36         /// <returns></returns>

   37         protected internal bool ViewIsValid()

   38         {

   39             return !SetFailedSpecifications().HasContent();

   40         }

   41         /// <summary>

   42         /// Returns the <see cref="Specification{T}"/> that the <see cref="TView"/> does not satisfy.

   43         /// </summary>

   44         /// <returns></returns>

   45         protected Collection<Specification<TView>> SetFailedSpecifications()

   46         {

   47             var failedSpecs = ViewSpecificationManager.GetFailedSpecifications(view);

   48             SetValidatedViewErrorMessage(failedSpecs);

   49             return failedSpecs;

   50         }

   51 

   52         private void SetValidatedViewErrorMessage(IEnumerable<Specification<TView>> failedSpecs)

   53         {

   54             var validatedView = view as IValidatedView<TView>;

   55             if (validatedView == null) return;

   56             var builder = new StringBuilder();

   57             foreach (var spec in failedSpecs)

   58             {

   59                 builder.Append(validatedView.GetErrorMessage(spec.GetType().Name));

   60             }

   61             validatedView.ErrorMessage = builder.ToString();

   62         }

   63     }

 

So, yep, now, from any presenter, you can just call ViewIsValid() and it'll take care of setting error messages for ANY Specification<View> in your assembly.

 

Here's an example of it in use in an actual presenter in some of my code.

 

   45 private void SaveContentItem(object sender, EventArgs e)

   46         {

   47             if (ViewIsValid())

   48             {

   49                 var contentItem = CreateContentItem();

   50                 repository.Save(contentItem);

   51             }

   52         }

 

nice and clean... Reads really nice (at least I think)...

 

Here's the code behind for the page for the view. It's just a really silly simple CMS, this peticular implementation doesn't ever "Edit" it just creates.

 

    5 public partial class Pages_Default :MvpPage,ICreateOrEditContentItemView

    6 {

    7 

    8     public int? ContentItemId

    9     {

   10         get { return null; }

   11 

   12     }

   13 

   14     public string PageTitle

   15     {

   16         get { return TitleControl.Text; }

   17     }

   18 

   19     public string Text

   20     {

   21         get { return BodyControl.Text; }

   22     }

   23 

   24     public string Url

   25     {

   26         get { return UrlControl.Text.Trim(); }

   27     }

   28 

   29     public string ErrorMessages

   30     {

   31         set { ErrorMessagesControl.Text = value; }

   32     }

   33     public event EventHandler SaveContentItemClicked = delegate { };

   34     protected override void OnInit(EventArgs e)

   35     {

   36         base.OnInit(e);

   37         SaveButton.Click += delegate { SaveContentItemClicked(this, e); };

   38     }

   39     public string ErrorMessage

   40     {

   41         set { ErrorMessagesControl.Text = value; }

   42     }

   43 

   44     public string GetErrorMessage(string key)

   45     {

   46         return (string)GetLocalResourceObject(key);

   47     }

   48 }

 

Hope you like it!

Thursday, February 26, 2009

Specification Manager

So, I'm very fond of the Specification Pattern. I typically find myself writing one Specification for each MVP view that may or may not create new Specifications. Seems like I'm repeating my self a bit, and there HAD to be a better way to do it.

So I got to thinking, we just need a Manager that you can send an instance of a view, and get all the specifications in the assembly that the view does not satisfy.

Simple enough, so I wrote this.

    8  [TestFixture]

    9     public class ViewSpecificationManagerTestFixture

   10     {

   11         [Test]

   12         public void manager_returns_failed_views()

   13         {

   14             var mocker = new MockRepository();

   15             var view = mocker.DynamicMock<ITestView>();

   16             var manager = new ViewSpecificationManager();

   17             var failedSpecs = manager.GetFailedSpecifications(view);

   18             Assert.That(failedSpecs.Count == 1);

   19             Assert.That(failedSpecs[0].GetType() == typeof(TestSpecificationThatAlwaysFails));

   20         }

   21     }

 

Yeah, that's the unit test... Here's the working class...

 

 

    7  /// <summary>

    8     /// Manager to handle Views and thier Specs

    9     /// </summary>

   10     public class ViewSpecificationManager

   11     {

   12         private readonly SpecificationFactory factory;

   13         public ViewSpecificationManager():this(new SpecificationFactory())

   14         {

   15         }

   16 

   17         internal ViewSpecificationManager(SpecificationFactory factory)

   18         {

   19             this.factory = factory;

   20         }

   21         /// <summary>

   22         /// Returns all Specifications not satisfied by the view

   23         /// </summary>

   24         /// <typeparam name="T"></typeparam>

   25         /// <param name="view"></param>

   26         /// <returns></returns>

   27         public virtual Collection<Specification<T>> GetFailedSpecifications<T>(T view)

   28         {

   29             var failedSpecs = new List<Specification<T>>();

   30             foreach (var specification in factory.GetSpecificationsFor(view))

   31             {

   32                 if(!specification.IsSatisfiedBy(view))

   33                 {

   34                     failedSpecs.Add(specification);

   35                 }

   36             }

   37             return new Collection<Specification<T>>(failedSpecs);

   38         }

   39     }

So basically, all this class does is get all the specs from the SpecificationFactory and check thier IsSatisfiedBy method and returns the ones that fail. Pretty simple, right?

I didn't want the Manager to understand ANYTHING except how to check all the specifications for the view it's given, NOT how to actually figure out what specifications are there, that would violate SRP.

The SpecificationFactorys single responsibility is to get all the Specifications that match up to the view.  Here are it's unit tests...

Actually, here are some dummy classes I created for the unit test consumption.

    5 public class ITestView { }

    6     public class TestSpecificationThatAlwaysPasses : Specification<ITestView>

    7     {

    8         public override bool IsSatisfiedBy(ITestView obj)

    9         {

   10             return true;

   11         }

   12     }

   13     public class TestSpecificationThatAlwaysFails : Specification<ITestView>

   14     {

   15         public override bool IsSatisfiedBy(ITestView obj)

   16         {

   17             return false;

   18         }

   19     }

Now, here's the unit tests...

   12 [Test]

   13         public void factory_returns_newed_up_specs()

   14         {

   15             var mocker = new MockRepository();

   16             var view = mocker.DynamicMock<ITestView>();

   17             var factory = new SpecificationFactory();

   18             var specs = factory.GetSpecificationsFor(view);

   19             Assert.That(specs.Count == 2);

   20             Assert.That(specs[0],Is.Not.Null);

   21             Assert.That(specs[1],Is.Not.Null);

   22         }

 

Simple enough, right... implementation just news them up....

    8 public class SpecificationFactory

    9     {

   10         private readonly ViewSpecificationResolver resolver;

   11 

   12         public SpecificationFactory():this(new ViewSpecificationResolver())

   13         {

   14         }

   15 

   16         internal SpecificationFactory(ViewSpecificationResolver resolver )

   17         {

   18             this.resolver = resolver;

   19         }

   20         /// <summary>

   21         /// Returns all <see cref="Specification{T}"/> for the object sent

   22         /// </summary>

   23         /// <typeparam name="T"></typeparam>

   24         /// <param name="view"></param>

   25         /// <returns></returns>

   26         public virtual Collection<Specification<T>> GetSpecificationsFor<T>(T view)

   27         {

   28             var specifications = new List<Specification<T>>();

   29             foreach(var type in resolver.GetSpecificationsFor<T>())

   30             {

   31                 specifications.Add((Specification<T>)Activator.CreateInstance(type));

   32             }

   33             return new Collection<Specification<T>>(specifications);

   34         }

   35 

   36 

   37     }

 

So yeah, the SpecificationFactory DOES NOT know what types to get, just to new up the types that the ViewSpecificationResolver object tells it to.

 

Here is it's unit test.

 

   13 [Test]

   14         public void resolver_finds_specifications_for_view()

   15         {

   16             var resolver = new ViewSpecificationResolver();

   17             var results = resolver.GetSpecificationsFor<ITestView>();

   18             Assert.That(results.Count,Is.EqualTo(2));

   19             Assert.That(results[0] == typeof(TestSpecificationThatAlwaysPasses) || results[0] == typeof(TestSpecificationThatAlwaysFails));

   20             Assert.That(results[1] == typeof(TestSpecificationThatAlwaysPasses) || results[1] == typeof(TestSpecificationThatAlwaysFails));

   21 

   22         }

 

Here's the actual class...

 

    8  public class ViewSpecificationResolver

    9     {

   10         public virtual Collection<Type> GetSpecificationsFor<T>()

   11         {

   12             var types = typeof (T).Assembly.GetTypes();

   13             var specifications = new List<Type>();

   14             foreach (var type in types)

   15             {

   16                 if(type.BaseType == typeof(Specification<T>))

   17                 {

   18                     specifications.Add(type);

   19                 }

   20             }

   21             return new Collection<Type>(specifications);

   22         }

   23     }

 

 

There's all the guts, now, from any Presenter, you can new up the ViewSpecificationManager and Call GetFailedSpecificationsFor(view).. Like SO...

 

   26           specificationManager = new ViewSpecificationManager();

   27                     var failedSpecifications = specificationManager.GetFailedSpecifications(view);

 

So that's pretty good... I like it, since the Presenter no longer cares about specifications for the view. I just thought it would be nicer to go ahead and create a Presenter base class that does this for you...

 

Here's what I did.

 

    6  /// <summary>

    7     /// Class that Presenters should inherit

    8     /// </summary>

    9     /// <typeparam name="TView"></typeparam>

   10     public abstract class Presenter<TView>

   11     {

   12         protected readonly TView view;

   13         private ViewSpecificationManager specificationManager;

   14 

   15         protected Presenter(TView view)

   16         {

   17             this.view = view;

   18         }

   19 

   20         internal virtual ViewSpecificationManager ViewSpecificationManager

   21         {

   22             get

   23             {

   24                 if (specificationManager == null)

   25                 {

   26                     specificationManager = new ViewSpecificationManager();

   27                 }

   28                 return specificationManager;

   29             }

   30         }

   31         /// <summary>

   32         /// Returns the <see cref="Specification{T}"/> that the <see cref="TView"/> does not satisfy.

   33         /// </summary>

   34         /// <returns></returns>

   35         protected Collection<Specification<TView>> GetFailedSpecifications()

   36         {

   37             return ViewSpecificationManager.GetFailedSpecifications(view);

   38         }

   39 

   40     }

 

There ya go.. now from any Presenter you can simple call GetFailedSpecifications() and get the list of all failed specifications.  THen just call SetFailedSpecsMessage from the MvpPage base class...

 

Here it is...

 

 

 

    9 public abstract class MvpPage:Page

   10     {

   11         /// <summary>

   12         /// The Presenter for this page

   13         /// </summary>

   14         private object presenter;

   15         protected override void OnLoad(System.EventArgs e)

   16         {

   17             base.OnLoad(e);

   18             presenter = PresenterFactory.GetPresenterFor(this);

   19         }

   20         /// <summary>

   21         /// Sets the controls text to the Resources for the failed Specs

   22         /// </summary>

   23         /// <typeparam name="TView"></typeparam>

   24         /// <param name="specifications"></param>

   25         /// <param name="control"></param>

   26         protected void SetFailedSpecsMessage<TView>(Collection<Specification<TView>> specifications,ITextControl control)

   27         {

   28             foreach (var specification in specifications)

   29             {

   30                 var message = GetLocalResourceObject(specification.GetType().Name);

   31                 control.Text += String.Format("<span>{0}</span>", message);

   32             }

   33         }

   34     }

 

 

Have fun with it, and feel free to tell me what sucks about it (or what doesn't!) :)

Wednesday, February 18, 2009

PresenterFactory and MVP base

Been awhile! Sorry!

Well, MVP / Observer pattern rocks, you all know I feel that way. Well, there are those who don't. I heard rumblings that basically it's an abuse of the observer pattern to make an academic point. I think that's a silly argument because using the observer pattern makes your view contract stronger.

With the observer pattern, you can not only define what the view HAS, but also what it CAN DO. It also adheres to Tell, Don't Ask.

Well, there's more too. I'm a firm believer in DRY. MVP pages all have a presenter on their page, and I always new them up in onload. I figured I could move that into a base class.

So I did.


    5 public abstract class MvpPage:Page

    6     {

    7         ///

    8         /// The Presenter for this page

    9         ///

   10         private object Presenter

   11         {

   12             get; set;

   13         }

   14         protected override void OnLoad(System.EventArgs e)

   15         {

   16             base.OnLoad(e);

   17             Presenter = PresenterFactory.GetPresenterFor(this);

   18         }

   19     }


I didn't want to use Type parameters since that would defeat the whole purpose of making the page unaware of it's presenter. 

The work is all really done in PresenterFactory, so let's get to that.


Here is the unit test for GetPresenterFor.


    1 using System;

    2 using MyDumbView.Presentation;

    3 using MyDumbView.UnitTests.Presentation.TestAssembly.Presentation.Presenters;

    4 using MyDumbView.UnitTests.Presentation.TestAssembly.Presentation.Views;

    5 using NUnit.Framework;

    6 using NUnit.Framework.SyntaxHelpers;

    7 

    8 namespace MyDumbView.UnitTests.Presentation

    9 {

   10     [TestFixture]

   11     public class PresenterFactoryTestFixture:AssertionHelper

   12     {

   13         [Test]

   14         public void GetPresenterFor_returns_the_right_type_of_presenter()

   15         {

   16             //Arrange

   17             var view = new TestView();

   18             //Act

   19             var presenter = PresenterFactory.GetPresenterFor(view);

   20             //Assert

   21             var expectedType = typeof (TestPresenter);

   22             Expect(presenter.GetType(),Is.EqualTo(expectedType));

   23         }

   24 

   25     }

   26 

   27     namespace TestAssembly.Presentation.Views

   28     {

   29         ///

   30         /// My Test Interface

   31         ///

   32         public interface ITestView{}

   33         ///

   34         /// Concrete class of

   35         ///

   36         public class TestView:ITestView{}

   37     }

   38     namespace TestAssembly.Presentation.Presenters

   39     {

   40         public class TestPresenter

   41         {

   42             public TestPresenter(ITestView view)

   43             {

   44             }

   45         }

   46     }

   47 }


Makes sense right?


Well, here's the factory.


    1 using System;

    2 

    3 namespace MyDumbView.Presentation

    4 {

    5     ///

    6     /// A factory that that returns presenters for views

    7     ///

    8     public class PresenterFactory

    9     {

   10         ///

   11         /// Returns a presenter for the view, very opinated code based on naming conventions.

   12         ///       

   13         ///

   14         ///

   15         ///

   16         public static object GetPresenterFor(object view)

   17         {  

   18             var type = view.GetType();

   19             var viewTypeName = GetViewTypeName(type);

   20             var presenterType = GetPresenterType(viewTypeName);

   21             return GetPresenter(presenterType, view);

   22         }

   23         private static string GetViewTypeName(Type type)

   24         {

   25             return PresenterFactoryNameParsingStrategy.GetTypeNameForView(type);

   26         }

   27         private static Type GetPresenterType(string viewTypeName)

   28         {

   29             var presenterName = GetPresenterTypeName(viewTypeName);

   30             var presenterType = Type.GetType(presenterName);

   31             if (presenterType == null)

   32             {

   33                 throw new Exception(String.Format("Can not find class {0}.", presenterName));

   34             }

   35             return presenterType;

   36         }

   37         private static string GetPresenterTypeName(string viewTypeName)

   38         {

   39             return PresenterFactoryNameParsingStrategy.GetTypeNameForPresenter(viewTypeName);

   40         }

   41         private static object GetPresenter(Type presenterType, object view)

   42         {

   43             var presenter = Activator.CreateInstance(presenterType, new[] { view });

   44             if (presenter == null)

   45             {

   46                 throw new Exception(String.Format("Can not instanciate class {0}. Does the class have a public constructor taking a single argument?", presenterType));

   47             }

   48             return presenter;

   49         }

   50 

   51 

   52     }

   53 }



So this code uses whatever naming conventions are in place to figure out the correct presenter.  This could easily be replaced with something more robust or and IOC container. I just know that my code will always adhere to naming conventions so I personally like this. It's all beside the point anyway, since I've moved all the code that actually determines thy types to PresenterFactoryNameParsingStrategy.

Oh, yeah, if you're interested, here it is.

    1 using System;

    2 

    3 namespace MyDumbView.Presentation

    4 {

    5     ///

    6     /// Encapsulates our opinionated naming stategy for Views and Presenters

    7     ///

    8     internal static class PresenterFactoryNameParsingStrategy

    9     {

   10         ///

   11         /// Returns the Type name for a MVP view

   12         ///

   13         ///

   14         ///

   15         public static string GetTypeNameForView(Type type)

   16         {

   17             foreach (var iface in type.GetInterfaces())

   18             {

   19                 if (iface.Name.EndsWith("View"))

   20                 {

   21                     return iface.AssemblyQualifiedName;

   22                 }

   23             }

   24             throw new Exception("View does not adhere to Naming conventions.");

   25         }

   26         ///

   27         /// Returns the name for a MVP presenter given it's view

   28         ///

   29         ///

   30         ///

   31         public static string GetTypeNameForPresenter(string viewTypeName)

   32         {

   33             var name = viewTypeName.Replace(".I",".")

   34                 .Replace("Views", "Presenters");

   35             var nameParts = name.Split(',');

   36             var typeName = name.Split(',')[0];

   37             var assemblyName = nameParts.Length > 1

   38                                    ?

   39                                        nameParts[1]

   40                                    :

   41                                        String.Empty;

   42             typeName = typeName.Replace(".I", ".").Replace(".Views.", ".Presenters.");

   43             var endIndex = typeName.Length - "View".Length;

   44             typeName = typeName.Substring(0, endIndex) + "Presenter";

   45             return String.Format("{0}, {1}", typeName, assemblyName);

   46         }

   47     }

   48 }


Lot's of hard coded strings in here, and this is definitly worth refactoring. But you get the drift....

So, running the unit tests... Everything passes.
Lets go make a page use it.

    1 using System;

    2 using MyDumbView.DomainModel;

    3 using MyDumbView.Presentation;

    4 using MyDumbView.Presentation.Views;

    5 

    6 

    7 public partial class _Default :MvpPage,IPersonEditorView 

    8 {

    9 

   10     ///

   11     /// Wires up events

   12     ///

   13     ///

   14     protected override void OnInit(EventArgs e)

   15     {

   16         base.OnInit(e);

   17         LoadButton.Click += delegate { PersonLoaded(this, e); };

   18         SaveButton.Click += delegate { PersonSaved(this, e); };

   19     }

   20     #region IPersonEditorView Methods

   21 

   22     ///

   23     /// The , null if we're adding a new one.

   24     ///

   25     public int? PersonId

   26     {

   27         get

   28         {

   29             int x;

   30             if (int.TryParse(PersonIdControl.Text,out x))

   31             {

   32                 return x;   

   33             }

   34             return null;

   35         }

   36         set { PersonIdControl.Text = value.ToString(); }

   37     }

   38     ///

   39     /// The to display

   40     ///

   41     public string FirstName

   42     {

   43         get { return PersonFirstNameControl.Text; }

   44         set { PersonFirstNameControl.Text = value; }

   45     }

   46     ///

   47     /// The to display

   48     ///

   49     public string LastName

   50     {

   51         get { return PersonLastNameControl.Text; }

   52         set { PersonLastNameControl.Text = value; }

   53     }

   54     ///

   55     /// Event raised when Saved is Clicked

   56     ///

   57     public event EventHandler PersonSaved = delegate { };

   58     ///

   59     /// Event raised when Load is clicked

   60     ///

   61     public event EventHandler PersonLoaded = delegate { };

   62 

   63     #endregion

   64 }


Beautiful!

Note that the page has absolulty no idea about its presenter anymore. That all sits in the base class.

And the page works the same as it originally did.

I'll post the entire solution soon...