Stuggling on a decision of inheritance vs. composition

Stuggling on a decision of inheritance vs. composition

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


boo posted on Sunday, April 08, 2007

I have mixed feeling and after reading other posts on the forum they're even more mixed.

In our business there is a 'customer'.  In our business model there are suttle rules that differ between a new customer and an existing customer.  But there are many similiar rules between the two.  In CSLA terms, strictly from the 'coding' side there are a lot of attributes about a customer (existing or new) that are the same,, but some that only exist for 'new' or 'existing'.  To complicate things there are several other objects, including forms that use a customer that don't really care whether it's new or not.  While doing this I have to keep in mind the architecture of the current system and minimizing the amount of change in a single release.

So at minimum, as to not break existing code I'll have to have an interface that will allow implementation and access to common functionality, and this is the only thing I'm sure of at this point.

So I struggle with the thought of inheritance knowing that the difference between new and existing are only about 2% of the customer - and being a programmer, and lazy by nature, inheritance is easier to implement; although I don't know what if any challenge are presented by using CSLA in an inheritance type of way.  As a side bar, any notes on that are appreciated.

But my gut, seems to want to go with two seperate business objects because the behavior while suttle, is slightly different in minimal ways between 'new' and 'existing', and CSLA is about behavior.  So although I think to myself this could easily be handled within the business object it self by using a flag and reduce the over-all complexity of the application by not having two nearly similiar business objects that in actuallity will live in the same DB table I don't know if inheritance is the right choice.  There would only be a small percentage of attributes that might make this somewhat challenging where an attribute may only be needed for 'existing' or 'new'...but considering our current architecture this would still be an improvement. 

My gut says to just have an interface that identifies the common attributes.  The kicker is the child objects in some cases will also have minimal differences...so do I implement an interface on those too? It seems like whatever my decision I should be uniform, at least if I go with interfaces.

Anyway, I come here to listen to the experiance and advise of others who have done this before; no programmer alone has all the answers.

Thanks!


vonheim replied on Monday, April 09, 2007

If most of your rules are similar (assuming the same) I would go with either same object or inheritance. That has been my choice so far. I usually keep a simple bool to separete the suttle differences

i.e. bool _newCustomer

if there are attribute differences I use f.ex. CanWriteProperty(_newCustomer) and etc. for rules.

If I misunderstood your question, forget this, otherwise I hope it helps!-)

Cheers,

Knut

ajj3085 replied on Monday, April 09, 2007

If the rules and other behaviors are mostly the same, I'd say inheritance is probably a good choice.  The base class would contain all the rules / behaviors that are common between both, and the subclasses would add in the additional behaviors.

Bowman74 replied on Monday, April 09, 2007

Like everything else in life, it depends.  One thing you don't want to have is a lot of switching logic.  It makes your object hard to maintain and over time tends to get more and more messy with switching logic.  On the other hand some level of switching logic is inevitable.  You can get yourself into just as much trouble sub classing in every situation you hit something that requires a little switching logic. 

So you really need to pull out your crystal ball and think about this.  How much switching logic will not only be required today but five years from now.  If the answer is negligible then it is probably just as well to go with a single class.  Your goals should be ease of use, understanding and maintainability.  All design patterns are a means to that end, not ends in and of themselves.

The issue around additional fields for one case but not the other is always a PITA.  Sub classing doesn't make it any easier other than cleaning up the interface a bit.  You may want to encapsulate all the "new" properties in single class and add it to your customer hierarchy.  There are lots of ways to handle this, some crummier than others but all ultimately somewhat crummy.

Thanks,

Kevin

boo replied on Monday, April 09, 2007

So it seems we all agree seperate objects are the way to go when it comes to handling in order to reduce complexity down the road.

Maybe I could get some of you to come back and tell me why you would prefer inheritance (a base class with two derived classes that seperate the specifics) instead of two classes that implement an interface that defines the common objects where each of the classes hold a reference to an object that would return 'common rules' or things of that nature.

I'm trying to feel out which will be easier to develop...interfaces are more work, but I'm not sure what challenges there are with inheritance in CSLA if there are any at all.

ajj3085 replied on Monday, April 09, 2007

Inheritance will be easier to develop; the implementation and interface are all in one place.

The main challenge to ineritance with Csla is generics; you'll probably have to declare your base class as such:

public abstract CustomerBase<T> : BusinessBase<T> where T : CustomerBase<T> { }

public sealed class NewCustomer : CustomerBase<NewCustomer> { }

public sealed class ExistingCustomer : CustomerBase<ExistingCustomer> { }

That way Save will return the correct type. 

boo replied on Monday, April 09, 2007

What do you mean by 'that way Save will return the correct type'?

Is there a known problem that occurs if you have something like

abstract class Customer : BusinessBase<Customer>

Are there any other considerations I need to know about?

I'm also curious about just using a regular class for the main type of customer since the derived class will have all the same information plus some extra attributes, this would cut out the need for an extra derived class that wouldn't add anything new.

Thanks for your help.

ajj3085 replied on Monday, April 09, 2007

Its not a problem per say, but if you declare your class like that, then the Save method on NewCustomer and ExistingCustomer will simply return a CustomerBase instance.  That might be be a big deal, because I imagine that once saved, you can't do anything to a NewCustomer instance, would that be correct?

You can use Customer as your base class if the ExistingCustomer class would add nothing to the base class, and simply subclass NewCustomer if NewCustomer needs the same behaviors as Customer and simply adds. 

HTH
Andy

boo replied on Monday, April 09, 2007

Yes that does help, thanks.  So let me ask you this, say I want NewCustomer to be the base class (not abstract) and ExistingCustomer to derive from NewCustomer and it does add extra properties.  Since NewCustomer is no longer an abstract class I can't have the 'where' can I?  Meaning a derived class would return NewCustomer on the Save method but I would want the Save method to return an ExistingCustomer...is this type of architecutre possible with generics and CSLA?

ajj3085 replied on Monday, April 09, 2007

You can still include the where clause for the generic type.

You couldn't have NewCustomer's save return ExistingCustomer though.  That's a bad idea anyway if NewCustomer is your base class.  OO design principals say that base classes should not depend on subclasses.

boo replied on Monday, April 09, 2007

I think it would be more of a command object that would be something like 'ChangeToExistingCustomer' where you'd pass in one type and return the other.  Not sure yet, but I agree, intial remark was bad choice.  Also I can't seem to get the where clause to work when I specifying:

class NewCustomer<NewCustomer> : BusinessBase<NewCustomer> where T: NewCustomer

class ExistingCustomer : NewCustomer where T: Existing customer

ajj3085 replied on Monday, April 09, 2007

boo:
class NewCustomer<NewCustomer> : BusinessBase<NewCustomer> where T: NewCustomer

class ExistingCustomer : NewCustomer where T: Existing customer


Try this instead:

class NewCustomer<T> : BusinessBase<T> where T : NewCustomer<T>
class ExistingCustomer : NewCustomer<ExistingCustomer>

boo replied on Tuesday, April 10, 2007

This gives a compile error on the Factory create method for NewCustomer because it's a generic type.

Chuck-MX replied on Wednesday, July 18, 2007

Hi everyone.

I know this topic it's been posted a while back, but anyway I'd like to know your opinion on this idea.

For what I understand the problem is that your customer object has to behave in accordance with the status of "new" or "not new". If this is the case I do believe that if you implement a state pattern it would help you with this issue, and therefore you would implement a composition of objects.

However, I'm starting using this interesting framework and I'm not really sure of the implications of this, specially in time and effort, so any thought would be really appreciated.

Thanks.

Carlos Ortiz.

P.D. Sorry if my english ain't so good.


JoeFallon1 replied on Monday, April 09, 2007

So it seems we all agree seperate objects are the way to go when it comes to handling in order to reduce complexity down the road."

I disagree with your conclusion above.

There were 2 posts that quite clearly recommend a single BO for both and the use of some switching logic to differentiate the behaviors. The caveat was how much swtiching logic would be introduced in the future? If there are only 2 cases then a single switch is fine and this may be the way to go. If you could end up with more cases then sublcassing may be a better decision up front.

I would probably start with the switching logic and a single class.

Joe

 

 

malloc1024 replied on Monday, April 09, 2007

When does a new customer become an existing customer?  Is it when it is saved to the database?  If so, the new customer object will be in the wrong state after it is saved.  This will make it difficult to work with new customers after they have been saved.  If you want to work on a customer objects after a state change, it might be better to have one customer class that can change its behavior dynamically using composition.

boo replied on Monday, April 09, 2007

Malloc, it does not change on the DataPortal.Save method.  Thanks though.

Copyright (c) Marimer LLC