views:

432

answers:

4

I have a customer hierarchy like so:

abstract class Customer {
    public virtual string Name { get; set; }
}

class HighValueCustomer : Customer {
    public virtual int MaxSpending { get; set; }
} 

class SpecialCustomer : Customer {
    public virtual string Award { get; set; }
}

When I retrieve a Customer, I would like to show on the web form the properties to edit/modify. Currently, I use if statements to find the child customer type and show the specialized properties. Is there a design pattern (visitor?) or better way so I can avoid the "if" statements in presentation layer? How do you do it?

Further information: This is an asp.net website with nHibernate backend. Each customer type has its own user control on the page that I would like to load automatically given the customer type.

+2  A: 

Can you use reflection to get the list of properties specific to an subclass (instance)? (Less error-prone.)

If not, create a (virtual) method which returns the special properties. (More error prone!)

For an example of the latter:

abstract class Customer {
    public virtual string Name { get; set; }

    public virtual IDictionary<string, object> GetProperties()
    {
        var ret = new Dictionary<string, object>();
        ret["Name"] = Name;
        return ret;
    }
}

class HighValueCustomer : Customer {
    public virtual int MaxSpending { get; set; }

    public override IDictionary<string, object> GetProperties()
    {
        var ret = base.GetProperties();
        ret["Max spending"] = MaxSpending;
        return ret;
    }
} 

class SpecialCustomer : Customer {
    public virtual string Award { get; set; }

    public override IDictionary<string, object> GetProperties()
    {
        var ret = base.GetProperties();
        ret["Award"] = Award;
        return ret;
    }
}

You probably want to create sections (fieldsets?) on your Web page, anyway, so if would come into play there, making this extra coding kinda annoying and useless.

strager
I am trying to avoid reflection. How would I use virtual method to set those properties?
Mank
@Mank, Reflection would be the easy answer. ;P I think you may be able to set them by using <string, ref object> or something. Not sure. Maybe have an encapsulating object? I don't know C# enough to help you there.
strager
A: 

Have you tried something like this:

public class Customer<T>
    where T : Customer<T>
{
    private T subClass;
    public IDictionary<string, object> GetProperties()
    {
        return subClass.GetProperties();
    }
}

With a subclass of:

public class FinancialCustomer : Customer<FinancialCustomer>
{
}

This is off the top of my head so might not work. I've seen this type of code in CSLA.NET. Here's the link to the CSLA.NET class called BusinessBase.cs which has a similar definition to what I've given above.

Jonathan Parker
Am I seeing things, or is this (forced) infinite recursion? Customer<Customer<Customer<Customer<...>>>> Maybe you meant "where T : Customer" instead?
strager
No I think it should work. For example CSLA.NET has: public abstract class BusinessBase<T> : Core.BusinessBase, Core.ISavable where T : BusinessBase<T> See here: http://www.lhotka.net/cslacvs/viewvc.cgi/trunk/cslacs/Csla/BusinessBase.cs?view=markup
Jonathan Parker
For example: public class FinancialCustomer : Customer<FinancialCustomer> { } should compile.
Jonathan Parker
FYI, Jonathan, the link you provided results in an error page... the claim is the source file is no longer there...
Jason D
@Jason D: Fixed the link. Thanks
Jonathan Parker
+2  A: 

I think a cleaner organization would be to have a parallel hierarchy of display controls or formats. Maybe use something like the Abstract Factory Pattern to create both the instance of Customer and of CustomerForm at the same time. Display the returned CustomerForm instance, which wuold know about the extra properties and how to display and edit them.

John Saunders
A: 

new:

interface CustomerEdit
{
    void Display();
}

edit:

abstract class Customer {
    protected CustomerEdit customerEdit; // customers have an object which allows for edit

    public virtual string Name { get; set; }
    public void Display() { customerEdit.Display(); } // allow the CustomerEdit implementor to display the UI elements
}

// Set customerEdit in constructor, tie with "this"
class HighValueCustomer : Customer {
    public virtual int MaxSpending { get; set; }
} 

// Set customerEdit in constructor, tie with "this"
class SpecialCustomer : Customer {
    public virtual string Award { get; set; }
}

usage:

Customer whichCouldItBe = GetSomeCustomer();
whichCouldItBe.Display(); // shows UI depeneding on the concrete type
Statement