Inheritance + Generics .. time to give up?

Inheritance + Generics .. time to give up?

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


jh72i posted on Tuesday, April 22, 2008

I have been fighting this for ages now because I just cannot believe that inheritance is such a no-no in the Csla world......I use inheritance a LOT (and deeply) and it has served me very very well over the years but now, with Csla generic'd, I'm pretty much at a dead-end so please help me out with a practical exaple........

As a really simplified example take the following class:

User / Users
Every application in the company has this shared concept. They all potentially share the same behaviour and business logic but if one system wants to specialize the basic User it simply inherits and alters the behaviour - overloading/overriding/introducing.

For example, a Order Processing app might inherit User because all of its 'Users' have 0 or more orders - OrderAppUsers["joe"].Orders.Count.....These users are still subject to all the rules and constraints of the base User. The Orders part might be lazy loaded or the data access portion might be overridden but with a call the base to ensure the correct object population, etc.

OrderProcessingAppUser : User
{
  private Orders _orders;
}

OrderProcessingAppUsers : Users
{
 public new OrderProcessingAppUser this[int index]
 {
  get
  {
   return (OrderProcessingAppUser)List[index];
  }
}

Now, as wee aside: how User/Users/OrderProcessingAppUser/OrderProcessingAppUsers "behave" in my world is determined at creation time - this is a flexibilty I need to avoid having many code bases for the same entity - example, we do not have a Child/Readonly/etc.User but a User that can be "created" as a child (User.Load("joe", IsChild | IsReadonly | Is....etc.).


Now, consider a different application that has not need of further specializing the user concept - it simply accesses User/Users. Consider other apps not yet dreamed up - we know they will have, at the very least, Users.

The compromise has been either to cast all OrderProcessingAppUsers to the approriate type in, say, the UI or simply override the indexer and cast appropriately from the underlying List in the inheriting class(as above).

 

Problem now is that because BusinessBase/BusinessListBase use generics if User/Users inherit from them they cannot be inherited to implement further specialization - the User is typed and none of my overrides can cast it. This is a DISASTER in the deep object hierarchies I have.


Old Csla was great because it allowed me to inherit all the great work in the equivalents of BusinessBase/BusinessListBase yet still implement my own object management strategy (single object source with runtime behaviour,shared rules, etc.). 

With new Clsa I'm scr*wed - even at its simplest I cannot access the old List (CollectionBase) and cast to get me back on track. Interfaces don't help because I want OrderProcessingAppUsers to inherit behaviour - example, I want the UI to be able to call orderProcessingAppUsers["joe"].TelephoneNumber even though OrderProcessingAppUser only has a TelephoneNumber because its implemented on User. I want the order processing app to be able to change a user's telephone number.
I don't want every possible User specialization for years to come to "copy" all the properties of the base user and implement them again or somehow delegate to the base User - this approach WILL fail because this company is not interested in the software - only in the individual apps.

 

Surely, surely others out there use inheritance in a way similar to what I describe here - how the heck do you use Csla these days?

 

skagen00 replied on Tuesday, April 22, 2008

First and foremost, chill out.

Second, I use inheritance just fine. Apply an interface to your base classes and your child classes will inherit use of this interface and may override the behavior. There on out, you're passing around IUser objects. If you want IUser to show some of the ISavable/etc. members to make usage more natural, so be it - IUser : ISavable, ....

I use factory classes to assist in the creation/fetching/etc of instances. UserFactory.GetUser(X), etc, which would return an IUser, for example.

Bottom line, it can be done, and I know my methodology for inheritance is different than one or two others that are being used by others on this forum.

Chris

 

jh72i replied on Tuesday, April 22, 2008

skagen00:

First and foremost, chill out.

:) I've been overcoming the problem but i'm majorly worried about the future of our "business object framework" - I see doors closing and/or complexity increasing incredibly.

skagen00:

Second, I use inheritance just fine. Apply an interface to your base classes and your child classes will inherit use of this interface and may override the behavior. There on out, you're passing around IUser objects. If you want IUser to show some of the ISavable/etc. members to make usage more natural, so be it - IUser : ISavable, ....

I suppose this is where I'm majorly worried - I have done this to a degree already but I'm thinking that every single class in the system needs it's own interface to allow it be inherited later.

Also, doesn't the IUser negate the benefit of generics in terms of not having to cast.

 

Lunch, now to calm me down (maybe a pub lunch :)

skagen00 replied on Tuesday, April 22, 2008

One thing you can consider, though I opted not to, is to create a base class directly from Core.BusinessBase, which is not generic. If you look at what BusinessBase<T> implements, it's not a great deal.

Please refer to this post on that subject: http://forums.lhotka.net/forums/post/2852.aspx

I opted against doing this because I was wary about breaking away from the standard approach to using the framework. (Though in the post above Rocky says that Core.BusinessBase is there (at least in part) for exactly this reason.)

I know I had a bit of disappointment that "extra had to be done" to use inheritance, but I guess it's a balance to using the framework. If you want the implementation BusinessBase<T> provides for you in terms of additional methods, then you have to deal with some of the inheritance issues.

I've seen Rocky speak before and have heard a couple podcasts where I think he expressed some disappointment generics aren't polymorphic, himself - c'est la vie.

I'm not sure what you mean about "doesn't the IUser negate the benefits of generics in terms of not having to cast".

Good luck.

Chris

 

jh72i replied on Tuesday, April 22, 2008

skagen00:

One thing you can consider, though I opted not to, is to create a base class directly from Core.BusinessBase, which is not generic. If you look at what BusinessBase<T> implements, it's not a great deal.

This is one of my options but bypassing all Rocky's great work at that level really is not something I want to do.

skagen00:

Please refer to this post on that subject: http://forums.lhotka.net/forums/post/2852.aspx

Thanks for this link. Wish he came up with a best-of-both worlds approach :)

skagen00:

I'm not sure what you mean about "doesn't the IUser negate the benefits of generics in terms of not having to cast".

Is the approach you meant?

public interface IUser{}

public class xUser : IUser{
   
public string xUserSpecific;
}

public class xUsers : BusinessCollectionBaseEx<xUsers, IUser>{
   
public override IUser this[int index]{get{ return base[index]; } set{ base[index] = value; }}
}

public class OAPUser : xUser{
   
public string OAPUserSpecific;
}

public class OAPUsers : xUsers{}

public class dosomething{
   
public dosomething()
   
{
      
OAPUsers oap = new OAPUsers();
      oap[0].??? no good - have to cast...
      ((
OAPUser)oap[0]).xUserSpecific;
    }
}

skagen00 replied on Tuesday, April 22, 2008

If you have a polymorphic collection of Users, you're going to have to cast it whether it's using an ancestor class or an interface.

UserCollection : BusinessListBase<UserCollection, BaseUser>

UserCollection : BusinessListBase<UserCollection, IUser>

Neither BaseUser or IUser knows what "OAPUserSpecific" would be. That is, _myUserCollection[0] is either a BaseUser or IUser with no idea what the actual derived class might contain. Casting either to an OAPUser (when the cast is valid) would provide you with access to OAPUserSpecific.

If you want a collection to hold only OAPUsers, then

OAPUserCollection : BusinessListBase<UserCollection, OAPUser>

That aspect doesn't change whether you use interfaces or not.

Maybe I'm misunderstanding your question?

Chris

jh72i replied on Tuesday, April 22, 2008

skagen00:

OAPUserCollection : BusinessListBase<UserCollection, OAPUser>

this won't work for the old.... 'UserCollection' must be convertible to 'Csla.BusinessListBase<UserCollection,OAPUser>' in order to use it as parameter 'T' in the generic type or method 'Csla.BusinessListBase<T,C>' ....reason

And really what I'd want is that OAPUsersCollection to inherit from Users yet somehow manage the item types into OAPUser objects..... OAPUser this[int index]

skagen00:

That aspect doesn't change whether you use interfaces or not.

Maybe I'm misunderstanding your question?

I don't think you misunderstand - my point was that passing interfaces about it fine but they do have to be cast to something specific at some point whereas the point of generics is to do away with the need to cast. Or something like that :)

skagen00 replied on Tuesday, April 22, 2008

Sorry I mistyped

OAPUserCollection : BusinessListBase<UserCollection, OAPUser>

I meant

OAPUserCollection : BusinessListBase<OAPUserCollection , OAPUser>

Anyways, good luck. If I have time later on I'll try to figure out a solution to what you're talking about.

jh72i replied on Tuesday, April 22, 2008

Very much appreciate your input already. Great to have someone to bounce this off.  Thank you.

tmg4340 replied on Tuesday, April 22, 2008

jh72i:
I don't think you misunderstand - my point was that passing interfaces about it fine but they do have to be cast to something specific at some point whereas the point of generics is to do away with the need to cast. Or something like that :)

In the end, if you're not willing to create your own BusinessBase class, then I don't think you're going to get what you want.

It seems that your problems are with your collection classes.  Going with an interface won't help, because you can't use it as the "C" type in BusinessListBase, unless you make your custom interface get all the CSLA interfaces on it.  But I don't see where that helps you a whole lot, as every subclass would have to re-implement most of the methods to get the interface type case appropriately.  The bad part is that much of the re-implementation is not of the "new" variety - it would be additional methods that sit beside the base ones.  Depending on how you want your collection subclasses to work, you probably would also have to "new" all the base methods to throw an exception, to make sure you get the right types into your collections.  I'm assuming it's all this that you're objecting to.

So you'll probably end up having to create a collection class for each subtype, but you should be able to inherit from your "User" class just fine.  The only other option is to create a non-generic BusinessListBase of your own.  Nothing intrinsically wrong with that, though it sounds like you're not liking that option.

In the end, I'm not exactly sure what having a base collection class gets you.  Obviously, I haven't seen your collection classes, so maybe there's a lot of common business logic you'd have to re-implement.  But you could factor that out into its own interface and create an internal helper class for that.  Yes, it's a bit of a hack, but it gets the job done.  But if your collection classes don't do very much, then I don't see a big gain in an inheritance chain there.  Sure, you're writing the same little bits of code a bunch of times - but you'd probably be mucking about with a good portion of that in a collection subclass anyway, in order to get the types cast correctly.

Just MHO...

- Scott

jh72i replied on Wednesday, April 23, 2008

Thanks for that input Scott.

tmg4340:

In the end, I'm not exactly sure what having a base collection class gets you.

I'm not sure my problems are limited to collections but consider a scenario from the simple example above.  That OAP application wants to be able to make calls like:

OAPUsers users = OAPUsers.GetCollection(someRole)

which is actually implemented in the Users class and not the OAPUsers class(collection). My world does a lot more stuff that I wouldn't want to have to reimplement - and its not just about inconvenience its also about the level of knowledge required for a developer to utlilize work already completed.

I could, of course, have a Generic BaseUsers collection but I suppose my major major disappointment is that the extensibility of my object models is lost - I need to think not just about the current application but the next and the one after that - my goal has been to have one complete "library" of business objects/collections that follow a consistent pattern so that when a new system is required we can look to extend and enchance much of what we have already - kind of like saying the 40% of the next app is already built. I also have a huge desire to ensure that the next developer passing through can "inherit" the complexity and concentrate purely on what his/her goal is. And while I use "User/s" as an example really the objects/classes/concepts are far more complex.

I will plug away for a while longer but all comments from folks solving the problems is always, always welcome.

 

 

Copyright (c) Marimer LLC