Abstract Base Class with Custom Fields and Implementation in Derived Class

Abstract Base Class with Custom Fields and Implementation in Derived Class

Old forum URL: forums.lhotka.net/forums/t/4209.aspx


jspringer posted on Tuesday, January 22, 2008

I have a question about Best Practices with CSLA and how to implement a derived class. Here is some background.

I have a base class (Person) that has a small set of data (FirstName, LastName, DateOfBirth, PersonType -- which is an enumeration) and some basic functionality ( public string GetFormattedName(); public void ChangeDateOfBirth(DateTime NewDOB); public abstract void SendCampaignEmails()). The PersonType enumeration tells me what kind of person the object actually is such as President, Vice President, Senator, etc. Now depending on the PersonType the logic in SendCampaignEmails() will obviously change due to the text that is involved, as well as WHO actually gets the email (which is out of the scope of this discussion) and the custom details which are properties of the derived objects depending on their type. Now, in a normal object model I would have a corresponding object for each PersonType and then inherit from the base Person class. To throw a monkey wrench into this, I also have no idea what the PersonType is until I actually read the database. I also really don't want to round trip to figure out the actual type of object before I GET the object and then turn around and re-read the DB since that will duplicate DB calls. What is the best way to implement this type of object model. I did find this example http://forums.lhotka.net/forums/thread/1286.aspx that talks about a similar situation, but I didn't quite understand how the final result was configured. Am I stuck with doing multiple reads?? And how does one go about the UPDATE() or DELETE() methods, I'm guessing the derived objects would simply append their custom information to the DataReader. Any help would be greatly appreciated.

Code Example:
public abstract class Person
{
   string FirstName { get; set; }
   string LastName { get; set; }
   DateTime DateOfBirth { get; set; }
   PersonType PersonType { get; set; }

   public string GetFormattedName()
  {
      return string.Format("{1}, {0}", FirstName, LastName);
  }
  public void ChangeDateOfBirth(DateTime NewDOB)
  {
     //add a history record
    this.DateOfBirth = NewDOB;
  }
  public abstract void SendCampaignEmails();
}

public class President : Person
{
  public President() : base()
  {
      base.PersonType = PersonType.President;
  }
  public abstract void SendCampaignEmails()
  {
      string emailBody = "President.....";
      //Do some custom logic
  }
}

public class VicePresident : Person
{
  decimal TotalCampaignDollars = 0;

  public VicePresident() : base()
  {
      base.PersonType = PersonType.VicePresident;
  }
  public abstract void SendCampaignEmails()
  {
      string emailBody = "Vice President.....";
      //Do some other custom logic
  }
}

skagen00 replied on Tuesday, January 22, 2008

Hello,

While I don't have an exact match with your situation, we have a situation where Individuals and Organizations are both "Profiles" and both inherit Profile, a base abstract class.

I like, very much, having a ProfileFactory class which takes an ID and first does a check on what type it is before delegating to the appropriate class's factory method.

You obviously wanted to get around this extra "read", but I'm not sure there's a nice way of doing so while keeping things especially clean. I think this offers a very loose coupling -- the only thing your factory class (like ProfileFactory) needs to do is take and ID and determine what type it is, so that it can hand over the actual population behavior to the class that needs to get an instance of itself from the database.

I think this topic has come up before and others have offered a different approach, but this is the one we're using.

Chris

 

JonStonecash replied on Tuesday, January 22, 2008

I think that what you need is an instance of the "Strategy" pattern.  I would abstract the behavior of the different types into a separate interface (call it IHandleIndicator), with methods to return the email body, campaign total dollars, etc  I would then implement this interface for each of the different types.  In the data read logic for each instance of the class, I would look at the indicator and "plug in" the appropriate instance of this interface.  Within the Person class, I would then invoke the methods on the IHandleIndicator interface as needed.

This approach will eliminate any need for a second read and give you differentiated behavior based upon the indicator value.  There are some that think that this is a better approach than inheritance.

jspringer replied on Tuesday, January 22, 2008

So if I get what you are saying Cash, would I have a child object of type IHandleIndicator?? One of my colleagues actually mentioned something like this (if I am interpretting you correctly)....I think I still might run into issues with the custom properties because not all the properties of the derived types will be in all the derived types (ie: in my example I mention CampaignDollars for VP -- only a VP has this value, no one else).

public class Person
{
    IHandleIndicator _helperToActualObject;

   public void SendCampaignEmails()
  {
      this._helperToActualObject.SendCampaignEmails();
  }
}

jthorndy replied on Tuesday, January 22, 2008

The problem you mention (only a VP has CampaignDollars) is a problem regardless of whether you use inheritance or a child object which encapsulates the behavior. If you use inheritance for the root object if you want to access the specialized properties you have to cast the root object as the specialized type. If you use a child object which is an interface or an abstract type then you have to cast the child object into the specialized type.

JonStonecash replied on Tuesday, January 22, 2008

Yes, that is exactly what I meant. 

The issue with differing data values can be handled in three different ways: One, you can hide these values in the general case and only expose them for specialized editing environments.  Two, you can expose them in all cases but force them to zero (or the appropriate default value) for anyone other than a VP.  Three, you could implement ITypedList (look it up on MSDN); that would allow you to have a dynamic set of properties (but this is a whole lot of work and would be a *** to maintain and only someone who is nuts would attempt it unless he or she was trying to show off how clever he or she was when he or she was not really all that smart -- ouch!).

Jon

Copyright (c) Marimer LLC