Abstract Base class Factory methods

Abstract Base class Factory methods

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


DamReev posted on Friday, June 02, 2006

I have the following classes:

public enum ActivityType {Meeting, Call, Reminder}

public abstract class Activity: BusinessBase<Activity> {

public ActivityType ActivityType {

get {/*...*/}

set {/*...*/}

}

//....

//Factory Methods

public static Activity GetActivity(int activityId){

//Need to return activity based on ActivityType which is stored in the database

//Want to be able to do something like the following

return DataPortal.Fetch<Activity>(new Criteria(activityId));

}

//Data Access

protected class Criteria : CriteriaBase {/*...*/ }

}

public class Meeting: Activity { }

public class Call: Activity { }

public class Reminder: Activity { }

How would I do this in CSLA considering the fact that I won't know the type of the activity until I hit the database an retrieve ActivtyType? Am I to assume I can't use the DataPortal to do this for me, and will have to use a switch statement to call the specific factory methods?

xal replied on Friday, June 02, 2006

Well, you're kind of missing the point of strong typing.

First of all in order to avoid trouble you should declare activity like:

public abstract class Activity<T>: BusinessBase<T> where T: Activity

and then you declare the derived classes like:
public class Remainder : Activity<Remainder>

Now, I'll assume that the user doesn't know / interact with the id, so you need to somehow provide of a way to abstract that from him, and while doing so, you could also handle the activity type issue.

So, suppose you have a readonly list of activities. And the readonly items have the id, the activity type and whatever other field you want to display in a list. So, from this point, there are two nice ways of doing this:
a) Create a method inside the readonly activity class like
object GetActivity()
{
    switch (this._activityType)
    {
       case ActivityTypes.Remainder:
          return Remainder.GetRemainder(this._id);
       ...
    }
}


b) Create a method inside the activity class that takes the readonly item as param, which would finally call the factory methods in the actual classes.

Now, that is a way to solve it, although I'm not very fond of that, since you're not returning a strongly typed object. Another problem with that is that you're likely to use different UI logic for each type and that you'll need a lot of type checking too.

Andrés

DamReev replied on Monday, June 05, 2006

Thanks for the response.

From what you have said it seems that I'm pretty much going to be forced to create all my activities on the client, which isn't a big deal now (everything is happening locally now in our application), but it takes away from one of the reasons we chose to use CSLA (flexibility for the future).

Also declaring:

public abstract class Activity<T>: BusinessBase<T> where T: Activity

Is actually pretty hard to deal with since it propagates generics all over the object model, but I think I can get around this with an IActivity interface, my only problem is that as you guessed I will have a list of activities; however, this list is read-write since the user can create new activities. I'm now pretty much stuck to have to make IActivity inherit from IEditableBusinessObject but thats fine again.

I still feel there should be a way for me to do the object type determination on the serverside dataportal, but I understand the type issues that this causes.

xal replied on Monday, June 05, 2006

I am aware that you will be creating new activities, but that doesn't neccesarily mean that the list should be editable. This is a very common topic. You see, editable objects generally have child objects, and are much bigger than readonly objects, so you typically want to avoid loading such big complex lists of objects and instead provide a speedy flat dumb readonly list.

That said, if your requirement is that the changes to the entire list live inside one transaction and either everything gets commited or nothing, then you probably do need an editable root collection. If that is not your requirement, then consider taking the first approach. It'll save you a few headaches and the overall performance most likely will be better.


One question: Are your activities so different that they need to be different types? Is their behaviour or the data they contain very different?


Andrés

DamReev replied on Monday, June 05, 2006

xal:
I am aware that you will be creating new activities, but that doesn't neccesarily mean that the list should be editable. This is a very common topic. You see, editable objects generally have child objects, and are much bigger than readonly objects, so you typically want to avoid loading such big complex lists of objects and instead provide a speedy flat dumb readonly list.

That said, if your requirement is that the changes to the entire list live inside one transaction and either everything gets commited or nothing, then you probably do need an editable root collection. If that is not your requirement, then consider taking the first approach. It'll save you a few headaches and the overall performance most likely will be better.

You make a very compelling point, about the readonly list. So how would I go about adding an object to a list once the user has created a new activity. Would I basically set IsReadOnly = false and then add the object and then set the property back to true. A readonly list does make sense to me to a degree since the user isn't actually modifying the list directly when they add or delete items. Any guidance is appreciated (I'm sure you can tell I'm a CSLA newbie).

xal:
-
One question: Are your activities so different that they need to be different types? Is their behaviour or the data they contain very different?


Andrés

Yes it turns out that most of our different activity types actually live in different database tables and they all have different business rules. I've already flattened the structure since we have subtypes of most of the other activies i.e. Meeting has meeting subtypes such as Personal, Client Meeting, or ProspectMeeting... all these meetings require different kinds of attendees.

By the way, Generics did complicate things a bit to where I can't fetch an activity with a static:

IActivity Activity<T>.GetActivity(long activityId) {...}

since i don't know T when I invoke the method.

So I've created an ActivityFactory class that will hit the DB figure out the activity type and pass the DataReader to the concrete Meeting, Call, or Reminder's static GetInstance(IDataReader reader) method .  So its coming along. 

Thanks for the help.

xal replied on Monday, June 05, 2006

About refreshing the list you can
a) Reload from the db.
b) You can use something like the observer's channel events. (read more about observer in http://csla.kozul.info/) (And no, observer does not require that you use active objects)

Hm... ActivityFactory class sounds like a very good idea to me...

Andrés

Copyright (c) Marimer LLC