Mark Needham

Thoughts on Software Development

Coding: An abstract class/ASP.NET MVC dilemma

with 9 comments

I previously described a refactoring that we have been working on to reduce the number of fields and delay calculations and the actual goal behind this refactoring was to get the code into shape so that we could add in the logic for a new business process that our application needed to handle.

The code in question defines view models being used by different partial views which are rendered depending on the business process that the user is currently executing.

We decided that the best way to add in this new code was drive our code towards a stage where we could have an abstract class and then sub classes for each of the specific business processes around this area.

Before this we had been using the same class everywhere but it was becoming a bit too complicated to understand as we attempted to add in this new functionality – there was already scattered if/else logic for the two business processes that were already being handled in the same place.

The views were defined like this, where the generic type on ‘ViewModel’ is the name of the specific business process model class:

ParentPage.aspx : ViewModel
 
BusinessProcess1Partial.aspx : ViewModel
BusinessProcess2Partial.aspx : ViewModel
BusinessProcess3Partial.aspx : ViewModel

The classes in question are roughly like this:

public abstract class ParentModel
{
	private readonly Dependency dependency;
 
	public ParentModel(Dependency depedency)
	{
		this.dependency = dependency;
	}	
 
	public string GetValueOne()
	{
		return dependency1.GetValueOne();
	}
}
 
public class BusinessProcessModel1 : ParentModel
{
	public BusinessProcessModel1(Dependency dependency) : base(dependency) { }
 
	public int SomeBusinessProcess1SpecificThing()
	{
		// do some calculation
	}
}
 
public class BusinessProcessModel2 : ParentModel
{
	public BusinessProcessModel1(Dependency dependency) : base(dependency) { }
 
	public int SomeBusinessProcess2SpecificThing()
	{
		// do some calculation
	}
}

It worked quite well for the majority of what we wanted to do but we eventually got to the stage where there was a property which we needed on the ‘ParentModel’ for the ‘ParentPage’ which was also needed by BusinessProcessModel1 and BusinessProcessModel2 but not by BusinessProcessModel3.

We actually had the data required for that property at the time of construction of all three of the business processes so we decided to add it to the ‘ParentModel’ and even though it seems quite evil to do this we couldn’t see an immediately obvious alternative.

Our initial thought was that perhaps we could make use of some role based interfaces and then define those interfaces as part of the ‘ViewModel’ generic that each page extends. As far as I understand it’s not possible to do this though because any types we use with ‘ViewModel’ need to be classes.

An approach which we considered but didn’t try out is to have a ‘ParentModel’ representing the top level page and then have each of the business proceess models as propertys on that.

By doing that we could reduce the need to have the class hierarchy although we would increase the complexity of the code for rendering the business process specific partials since we’d need to introduce if statements into the parent view to ensure that the correct property from that was selected.

Neither option seems that great. Has anyone found a better solution?

Written by Mark Needham

September 13th, 2009 at 12:19 am

Posted in .NET,Coding

Tagged with

  • Jeremy Gray

    I could be missing something in relation to your requirements but it looks like you just discovered that ParentModel itself needs a base class from which BusinessProcessModel3 will need to derive, leaving BusinessProcessModel1 and BusinessProcessModel2 deriving from what is currently called ParentModel. Alternatively, you could mentally visualize this as adding a new class between ParentModel and BusinessProcessModel1 and 2, with the new property added to the newly-created class.

    All in all, this is pretty standard object model evolution, right up until you encounter the diamond problem, at which point you can either do what you’ve done so far (put the property on a base class even though not all derived classes need it) or go for something a bit more esoteric.

  • http://www.markhneedham.com Mark Needham

    I’m probably not quite understanding something but how would that approach work for BusinessProcessModel3 on the ParentPage?

    On the ParentPage say we make use of Property1 and Property2 and Property2 is the one which only exists for BusinessProcessModel1 and BusinessProcessModel2.

    So we’d have something like this:

    NewParentModel
    Property1

    ParentModel : NewParentModel
    Property2

    BP1Model : ParentModel

    BP2Model : ParentModel

    BP3Model : NewParentModel

    I guess we would need to change the type for the ParentPage to be ‘NewParentModel’ so that we have a common class for ParentPage. If we have the type for ParentPage as ‘ParentModel’ then I think we’d get a class cast exception when we’re in BP3 since you can’t downcast BP3Model to ‘ParentModel’?

    Maybe there’s a simpler way and I’ve misunderstood…

  • Pingback: TDD: Testing sub classes at Mark Needham

  • Jeremy Gray

    If you introduce NewParentModel as the new base class there is an extra step that has to be taken, that being to update ParentPage to reference NewParentModel instead of ParentModel.

    If you instead take the other approach I mentioned and add a new derivation from ParentModel, change BusinessProcessModel1 and 2 to refine it, and then add the new property to the new class, ParentPage (and other references to ParentModel) wouldn’t have to change.

    Both approaches work but have their pros and cons. For your scenario, the latter approach may be a better fit.

    So, just to be clear on how the latter approach would look:

    ParentModel
    Property2

    SubParentModel : ParentModel
    Property1

    BP1Model : SubParentModel

    BP2Model : SubParentModel

    BP3Model : ParentModel

  • Pingback: Tweets that mention Coding: An abstract class/ASP.NET MVC dilemma at Mark Needham -- Topsy.com

  • Pingback: Daily tech links for .net and related technologies - September 13-15, 2009 - Sanjeev Agarwal

  • Pingback: DotNetBurner - ASP.net MVC

  • Pingback: ASP.NET MVC Archived Blog Posts, Page 1

  • Pingback: DotNetShoutout