CSLA, Generics and Inheritance

CSLA, Generics and Inheritance

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


MTmace posted on Wednesday, April 22, 2009

All of my business objects have a hand full of properties in common. So I have an abstract class (DataObject) that inherits from BusinessBase.

public abstract class DataObject<T> : Csla.BusinessBase<DataObject<T>>

I would like to define a GetObject method that accepts a SafeDataReader as an arguement and return the instanciated object.

internal static T GetObject(Csla.Data.SafeDataReader Reader)
{
T obj =
new T();
obj.Fetch(Reader);
return obj;
}

The problem is in order to instanciate a generic class the class must have a public empty constructor.
Can anyone help me with this or design a better solution?

public abstract class DataObject<T> : Csla.BusinessBase<DataObject<T>> where T : new()

Thanks,

MTmace

RockfordLhotka replied on Thursday, April 23, 2009

Your "leaf node" object type (the actual business object type) can't be generic. The UI technologies like WPF, Windows Forms, etc don't work with generic types (not well anyway), so your actual object instance must be non-generic.

This means you don't ever want to create an instance of your generic base class - only the actual non-generic business class.

The data portal will help you here, as long as you use criteria objects that inherit from CriteriaBase (or implement ICriteria) because then you can specify the actual business object type that you want created, and the data portal will create it for you.

However, if you search way back in the forum, you'll find threads where I (and others) point out that this is a bad idea. Inheritance is for inheriting behavior, not fields or properties. By trying to inherit to get fields/properties you will run into code complexity, maintenance and performance issues, because the persistence model gets very ugly very fast.

Inheritance is one of the most tightly coupled relationships in existence. Coupling is one of the most dangerous and bad things you can introduce into an application. Thus, inheritance usually causes a radical increase in coupling, and thus a radical decrease in the quality of your code.

One of the best practices for OO programming is to "favor composition over inheritance". This is because inheritance is such a double-edged sword, and the bad edge is dipped in poison.

Along this line, reuse also always brings coupling. We all want reuse - but you should only accept reuse if you can get it without much coupling. Reuse through inheritance almost always increases coupling - especially when the goal is to reuse field/property declarations...

pamo replied on Wednesday, June 08, 2011

Talk about dumbing down OO inheritance! I agree, people need to think a lot before coupling things through inheritance, but if inheritance fits for the relationship you are modelling (i.e. and IS-A relationship), then you should be able to model it that way!

In the version of Csla I'm looking at, the majority of Csla classes:
   should inherit  from Csla.BusinessBase(of T)
   which inherits Csla.Core.BusinessBase
   which inherits Csla.Core.UndoableBase
   which inherits Csla.Core.BindableBase
   which inherits System.Object

So if coupling through inheritance causes a "a radical decrease in the quality of your code", does that mean Csla is low quality ;-)  I challenge you to think about how you'd have to re-design your entire Csla framework without using inheritance...

I'd actually argue that "inheriting for behaviour" is just as wrong as for "inheriting for properties". For example, just because a Grid class needs a SortYourData method and BookCollection needs the same SortYourData method, doesn't mean they should both inherit from a Sortable class. Quite the opposite, but maybe they should maybe implement the not tightly coupled ISortable Interface! 

 Just my two cents, but modelling inheritance is not always a bad thing and needs to be supported 100% by the Csla framework.

RockfordLhotka replied on Wednesday, June 08, 2011

What a passionate way to ressurect an ancient thread :)

In some ways having things like UndoableBase be in the inheritance chain is a total PITA, and in retrospect it would have been better implemented through composition. Other things, like a lot of the data binding support, has to be done through inheritance, but would also be better done through composition if that were possible.

In any case, nothing stops you from using inheritance if you would like to do so. I was simply recommending an alternative that I think is better.

To use inheritance in your classes, you must understand how CSLA manages properties, and you must adjust your coding appropriately. To avoid high levels of complexity, you really want to avoid having anything but your "leaf type" be non-generic. Although the alternative is possible, it means giving up all the simpler generic-based RegisterProperty overloads, and that is a common source of copy-paste errors in people's code.

At the same time, if the benefit of inheritance is high enough to offset the increased complexity, then clearly inheritance should be used. These things are never (really) technical issues - they are cost/benefit business decisions.

RaulLozano replied on Wednesday, June 08, 2011

OK I'll bite


RockfordLhotka

In some ways having things like UndoableBase be in the inheritance chain is a total PITA, and in retrospect it would have been better implemented through composition.


Can you please educate me on how is having UndoableBase be in the inheritance chain is a total PITA?


RockfordLhotka

At the same time, if the benefit of inheritance is high enough to offset the increased complexity, then clearly inheritance should be used.


I have read a couple of your post where you stress this out, but for the life of me, I cant think of a good example on why inheritance makes things more complex. So what exactly is so bad about inheriting for properties? And what’s so bad about incresing coupling when you inherit?


I know I am an idiot, I admit it. But I will seriously thank you if you please help me understand this, perhaps a link where I can find some good explanation will be really appreciated.


Thanks.

 

RockfordLhotka replied on Thursday, June 09, 2011

I'd often enjoy rehashing some of these ideas. At the moment though, I'm on an extended vacation (a perk of working at Magenic for 10+ years), so I'm not up for it this summer...

The complexity with inheriting to add properties generally comes from two sources:

  1. Business rules are "cumulative" and base class rules may become awkward or non-applicable once more properties have been added, yet you are stuck with them
  2. Data access and data mapping is complex - though the object factory concept can help with this - but it is still awkward

The business rule thing should be pretty obvious with just a little thought. Rules defined and designed for a class with certain properties might not apply to a class with more extensive properties. But the rules are associated with properties in each class, and unless you start adding conditional code to avoid it, those rules will apply to subclasses too. If you have to add conditional code of that nature, you are clearly violating OOP best practices. There are entire books about that sort of thing.

The data access issue is usually the first thing people encounter though, because it is hard to solve efficiently.

Create a class A that has properties X and Y. It will use a DAL that retrieves X and Y from the database, and all is well.

Now create a subclass B that adds property Z. How do you load X, Y, and Z now? Do you replicate the DAL to add Z to this query? And even if you do that, B can't load A's properties - so you need some base class method you can invoke to allow A to load itself with data - at which point there's no abstraction or "black box" concept - all subclasses must know everything about the data required by the base class.

Or do you call the A DAL, and then also call a B DAL (two database queries)? At least this preserves encapsulation and avoids having data queries scattered all over the place.

The ultimate question, is when you add a new property to A, how many places do you need to fix/test to make that work across all the subclasses?

Copyright (c) Marimer LLC