Object Composition Vs Object InheritanceObject Composition Vs Object Inheritance
Old forum URL: forums.lhotka.net/forums/t/66.aspx
RanceDowner1234 posted on Wednesday, May 10, 2006
Hey all:
I'm doing an OOP project for the first time. And trying to learn CLSA.net at the same time. So I'm trying to keep it as simple as possible to prevent brain shock. But I'm also trying to do it right.
I've been reading some article lately on Object Composition vs Object Inheritance. Does anybody have any thoughts either way on the subject, especially using the CSLA 2.x framework.
Some articles I've read are:
http://brighton.ncsa.uiuc.edu/~prajlich/T/node14.html (seems to favor composition)
http://cpptips.hyperformix.com/cpptips/inh_vs_comp (seems to favor composition)
http://www.javapractices.com/Topic72.cjp (seems to favor compositon)
Of course none of these brilliant fellows give any real-life code examples on the differences between the two!
After a few hours of searching I finally found sort of an example for vb.net
http://discuss.develop.com/archives/wa.exe?A2=ind0502b&L=vbdotnet&T=0&F=&S=&P=1552
The above article focuses on "containing" a child object. This seems like it would be hard to "bind" to the object using vb.net's datasource and bindingsource utilities. Is there any other way to do Object Compositon?
Long story short... I would consider using Object Composition if I could find a good practical example or tutorial that explains how to implement it in the real world. Does anybody know of any sites that show some good vb.net or c#.net examples of a class that uses Object Composition vs Inheritance?
Or if anyone is currently using Object Compositon with CSLA 2.x I would love it if you could post a couple of your classes and "base"classes (if "base" is what you call them in Composite terms). Especially showing how and when you write to the database using these little module classes.
Thx
Rance Downer
rancedowner1234@hotmail.com
RanceDowner1234 replied on Thursday, May 11, 2006
I think I found my own answer, so I might as well post it incase any other lost soul is looking for an answer too. They key came in finding out that Object "Composition" goes by another name... Object "Delegation". It also goes by another name "Object Aggregation". In short... It's basically just the old VB6 way of doing OOP.
I'm surprised many persons so strongly advocate it over inheritance. But what do I know! I'm a newbie to OOP. Personally, though, I think this is one reason so many developers get turned off to OOP, is that sometimes OOP advocates get a little bit on the religiously ferverous side on ya! That's a different subject, but for an interesing read on that topic you might read Rocky's Guest OP in the latest Visual Studio Magazine... http://www.ftponline.com/vsm/2006_04/magazine/departments/guestop/
But back on topic... I figured out how atleast how I think I would assemble an "Agregated" (I prefer that moniker) Object. Some questions do arise though.
If I did use Agregation in CSLA.net I wondered primarily about ValidationRules:
* Where's the best place to put the ValidationRules... inside the module Objects or inside the Aggregate? If I put them in the "Module" Objects how do I "stitch" them all together in the Agreggate to provide a common "broken rules" collection for users without resorting to all kinds of withevents and event trapping on my base modules... which could lead to my objects not being serializable (or more difficult to make serializable)? If I put them in the "Agreggate" Object do I have to keep copying and pasting similiar logic every time I implement one of the Modules. And if I'm doing so much copying and pasting have I lost some value to the whole Object Oriented approach.
* Also I don't think there's really any good reason to inherit BusinessBase(Of Payment) and BusinessBase(Of LineItem) in the respective module objects. But I did wonder?
Long story short... I think I'm going to decide on Inheritance chains over aggregation, being that my program isn't really that difficult, and my inheritance hierarchies will be rather small. I don't think I'll be doing alot of overriding in my SubClasses. Also my gut tells me that inheritance will work better with the CSLA.net framework than Agreggation. For instance I think Business Rules will be easier to implement in an inheritance environment. I don't want to do that many workarounds. Thirdly I pretty much so just cringe at the idea of writing and implementing all those interfaces.
But it will be fun to post how I think I would do aggregation. If anyone has an actual better way of doing it though.... please.... post it by all means. Or if anyone has any comments on my implementation that would help too. Good or bad.
Note: Some examples of aggregation I saw on the internet did not Implement the Interfaces in the aggregate object, they merely provided pointers to module objects. But I chose to Implement the Interfaces because I will be databinding these objects to grids and controls in my winforms using datasources and the bindingSource control. The only way I could figure out to accomplish this was to do a full blown delegation, which would emulate the base module properties.
Note2: I didn't implement everything... it's just a sample... so I only did the DataPortal_Create and Fetch methods. I haven't tried actually running this code it's just my thoughts on how I think I might do it. I suppose it could get tricky when it came to the updating logic. I found an article on Rocky's website that shows how to do updating using inheritance. I imagine the same principles would apply in an Agreggate scenario. So I used a similiar technique in the Fetch in this example. In updating, I suppose I would just create an Update subroutine and pass in a command object, like he does in his article on inheritance. Here's Rocky's advice... http://www.lhotka.net/Articles.aspx?id=e130b265-15cb-453d-9719-d5a944385fd3
The following example basically shows a ResidentialCustomerPayment which is an aggregate of Payment and LineItem. For definition sake... a Line Item is in the context of an accountbook registry "line item". I don't show it in the example but I will also want to use LineItem in deriving other objects like an ElectricBillStatement Object or a GasBillStatement Object. I would also want to use the Payment class in agreggating other objects such as a CommercialCustomerPayment.
Anyway... without further musing... here's the code:
Imports Csla
Imports Csla.Data
Public Interface ILineItem
Property LineItemID() As Integer
Property EntryDate() As SmartDate
Property EntryAmount() As Double
End Interface
Public Class LineItem
Inherits BusinessBase(Of LineItem) 'Haven't implemented Business rule logic so I don't know if I really need to inherit from BusinessBase or not???
Implements ILineItem
Protected Overrides Function GetIdValue() As Object
Return _LineItemID
End Function
Private _EntryAmount As Double
Public Property EntryAmount() As Double Implements ILineItem.EntryAmount
Get
Return _EntryAmount
End Get
Set(ByVal value As Double)
_EntryAmount = value
PropertyHasChanged()
End Set
End Property
Private _EntryDate As New SmartDate()
Public Property EntryDate() As Csla.SmartDate Implements ILineItem.EntryDate
Get
Return _EntryDate
End Get
Set(ByVal value As Csla.SmartDate)
_EntryDate = value
PropertyHasChanged()
End Set
End Property
Private _LineItemID As Integer
Public Property LineItemID() As Integer Implements ILineItem.LineItemID
Get
Return _LineItemID
End Get
Set(ByVal value As Integer)
_LineItemID = value
PropertyHasChanged()
End Set
End Property
Private Sub New()
'Force Factory methods
End Sub
Public Shared Function NewInstance() As LineItem
Return DataPortal.Create(Of LineItem)()
End Function
Public Sub Fetch(ByVal dr As SafeDataReader)
With dr
_LineItemID = .GetInt32("LineItemID")
_EntryAmount = .GetDecimal("EntryAmount")
_EntryDate = .GetSmartDate("EntryDate")
End With
End Sub
End Class
Public Interface IPayment
Property CheckAmount() As Double
Property CheckNumber() As String
Property AppliesToPrincipal() As Double
Property AppliesToInterest() As Double
End Interface
Public Class Payment
Inherits BusinessBase(Of Payment) 'Haven't implemented Business rule logic so I don't know if I really need to inherit from BusinessBase or not???
Implements IPayment
Protected Overrides Function GetIdValue() As Object
'Cant think of anything to return so I'm returning Nothing
Return Nothing
End Function
Private _AppliesToInterest As Double
Public Property AppliesToInterest() As Double Implements IPayment.AppliesToInterest
Get
Return _AppliesToInterest
End Get
Set(ByVal value As Double)
_AppliesToInterest = value
PropertyHasChanged()
End Set
End Property
Private _AppliesToPrincipal As Double
Public Property AppliesToPrincipal() As Double Implements IPayment.AppliesToPrincipal
Get
Return _AppliesToPrincipal
End Get
Set(ByVal value As Double)
_AppliesToPrincipal = value
PropertyHasChanged()
End Set
End Property
Private _CheckAmount As String
Public Property CheckAmount() As Double Implements IPayment.CheckAmount
Get
Return _CheckAmount
End Get
Set(ByVal value As Double)
_CheckAmount = value
PropertyHasChanged()
End Set
End Property
Private _CheckNumber As Double
Public Property CheckNumber() As String Implements IPayment.CheckNumber
Get
Return _CheckNumber
End Get
Set(ByVal value As String)
_CheckNumber = value
PropertyHasChanged()
End Set
End Property
Private Sub New()
'Force Factory methods
End Sub
Public Shared Function NewInstance() As Payment
Return DataPortal.Create(Of Payment)()
End Function
Public Sub Fetch(ByVal dr As SafeDataReader)
With dr
_AppliesToInterest = .GetDecimal("AppliesToInterest")
_AppliesToPrincipal = .GetDecimal("AppliesToPrincipal")
_CheckAmount = .GetDecimal("CheckAmount")
_CheckNumber = .GetString("CheckNumber")
End With
End Sub
End Class
Public Interface IResidentialCustomerPayment
Property KilowattHoursUsed() As Double
Property ResidentialRate() As Double
End Interface
Public Class ResidentialCustomerPayment
Inherits BusinessBase(Of ResidentialCustomerPayment)
Implements IResidentialCustomerPayment
Implements ILineItem
Implements IPayment
Private mLineItem As LineItem
Private mPayment As Payment
Protected Overrides Function GetIdValue() As Object
Return mLineItem.LineItemID
End Function
Private Sub New()
'Forces Factory Methods
End Sub
Public Shared Function NewInstance() As ResidentialCustomerPayment
Return DataPortal.Create(Of ResidentialCustomerPayment)()
End Function
Public Shared Function GetInstance(ByVal id As Integer) As ResidentialCustomerPayment
Return DataPortal.Fetch(Of ResidentialCustomerPayment)(id)
End Function
Protected Overrides Sub DataPortal_Create(ByVal criteria As Object)
mLineItem = LineItem.NewInstance()
mPayment = Payment.NewInstance()
End Sub
Public Sub Fetch(ByVal dr As SafeDataReader)
With dr
_ResidentialRate = .GetDecimal("ResidentialRate")
_KilowattHoursUsed = .GetDecimal("KilowattHoursUsed")
End With
'Note: I suppose I could have just called the properties directly
'But I chose to have an easy function called Fetch to be able to
'pass a SafeDateReader
Call mLineItem.Fetch(dr)
Call mPayment.Fetch(dr)
MarkOld() 'For Good measure
End Sub
Protected Overrides Sub DataPortal_Fetch(ByVal criteria As Object)
MyBase.DataPortal_Fetch(criteria)
Dim dr As SafeDataReader
'Put logic here to call from Database and populate dr
Call Fetch(dr)
End Sub
#Region " IResidentialCustomerPayment"
Private _KilowattHoursUsed As Double
Public Property KilowattHoursUsed() As Double Implements IResidentialCustomerPayment.KilowattHoursUsed
Get
Return _KilowattHoursUsed
End Get
Set(ByVal value As Double)
_KilowattHoursUsed = value
PropertyHasChanged()
End Set
End Property
Private _ResidentialRate As Double
Public Property ResidentialRate() As Double Implements IResidentialCustomerPayment.ResidentialRate
Get
Return _ResidentialRate
End Get
Set(ByVal value As Double)
_ResidentialRate = value
PropertyHasChanged()
End Set
End Property
#End Region
#Region " ILineItem "
Public Property EntryAmount() As Double Implements ILineItem.EntryAmount
Get
Return mLineItem.EntryAmount
End Get
Set(ByVal value As Double)
mLineItem.EntryAmount = value
PropertyHasChanged() 'treat delegated propertyChange same as local propertyChange
End Set
End Property
Public Property EntryDate() As Csla.SmartDate Implements ILineItem.EntryDate
Get
Return mLineItem.EntryDate
End Get
Set(ByVal value As Csla.SmartDate)
mLineItem.EntryDate = value
PropertyHasChanged() 'treat delegated propertyChange same as local propertyChange
End Set
End Property
Public Property LineItemID() As Integer Implements ILineItem.LineItemID
Get
Return mLineItem.LineItemID
End Get
Set(ByVal value As Integer)
mLineItem.LineItemID = value
PropertyHasChanged() 'treat delegated propertyChange same as local propertyChange
End Set
End Property
#End Region
#Region " IPayment "
Public Property AppliesToInterest() As Double Implements IPayment.AppliesToInterest
Get
Return mPayment.AppliesToInterest
End Get
Set(ByVal value As Double)
mPayment.AppliesToInterest = value
PropertyHasChanged() 'treat delegated propertyChange same as local propertyChange
End Set
End Property
Public Property AppliesToPrincipal() As Double Implements IPayment.AppliesToPrincipal
Get
Return mPayment.AppliesToPrincipal
End Get
Set(ByVal value As Double)
mPayment.AppliesToPrincipal = value
PropertyHasChanged() 'treat delegated propertyChange same as local propertyChange
End Set
End Property
Public Property CheckAmount() As Double Implements IPayment.CheckAmount
Get
Return mPayment.CheckAmount
End Get
Set(ByVal value As Double)
mPayment.CheckAmount = value
PropertyHasChanged() 'treat delegated propertyChange same as local propertyChange
End Set
End Property
Public Property CheckNumber() As String Implements IPayment.CheckNumber
Get
Return mPayment.CheckNumber
End Get
Set(ByVal value As String)
mPayment.CheckNumber = value
PropertyHasChanged() 'treat delegated propertyChange same as local propertyChange
End Set
End Property
#End Region
End Class
malloc1024 replied on Monday, May 15, 2006
One of the
fundamental principles of OOP is favor composition over inheritance. Composition will give you more maintainable
and flexible code. Inheritance is
overused and abused way too often. BTW,
the articles you listed did give code examples.
They said to look at design patterns.
Design patterns will show the power that composition has over
inheritance. I would strongly suggest
that you get a good OOP/Design Patterns book.
It will help you much more than Rocky's book in the long run. Knowing the fundamentals will give you more appreciation
on what Rocky did and also help you become a much better programmer.
MichaelBosch replied on Tuesday, May 16, 2006
Malloc:
I like you're test replacing "is-a" with "behaves as a". Any suggestions on the best OOP/Design
Patterns book? I bought the Head First design patterns book, but
it seems like a very basic, simplistic presentation of design
patterns. Maybe thats a good thing? Any other
suggestions...?
Bayu replied on Wednesday, May 17, 2006
Hey,
Don't forget to mention the classic:
Design Patterns: Elements of Reusable OO Software, by the 'Gang of Four'.
http://www.amazon.com/gp/product/0201633612/sr=8-1/qid=1147851537/ref=pd_bbs_1/002-5875380-8679251?%5Fencoding=UTF8Actually, this book is all I ever read 'on paper' about design patterns. It is more like a catalog then a tutorial. Really great reference to have on your workbench to look up the 20+ most commonly used design patterns of all. With sample code (not dotnet, but very comprehensible), UML.diagrams and detailed explanation of the pros and cons of each pattern. IMO: a must-have!
(Sometimes, when discussing these matters, I tend to get a little carried away
).
Check it out!
Bayu
malloc1024 replied on Wednesday, May 17, 2006
I mentioned the GoF book. I just forgot to provide the link to it. Thanks for posting the link.
DancesWithBamboo replied on Sunday, May 14, 2006
Hi Rance-
I see you answered your own post but I thought I would add a couple of thoughts. My first consideration when deciding to use inheritance is wheter the realtionship I am modeling is a "is-a" or a "has-a" relationship. The is-a is more likeley to be modeled as an inheritance structure like "address is a contact"". The has-a is modeled as a composition such as "a customer has an order".
After that, inheritance is almost always more complicated to design correctly in the real-world environment. It seems that all of the requirements for a project are never known at design time and it is much easier to add functionality by composition than inheritance later on in the product's lifecycle.
In your answer you only use interface inheritance so I don't think what you did can really be compared to composition since you didn't inherit functionality. Interface inheritance allows you to act on all the objects in the same way (polymorphism) but doesn't give you any any less work to do in the derived class since you have to implement the methods anyway. Not to say that it is right or wrong, but I haven't seen anyone do what you did. Basically you said that a ResidentialCustomerPayment is-a LineItem and has-a LineItem. I don't see why you would do this? I would probably make a full LineItem class and give it to the customer as a property; then access it as ResidentialCustomerPayment.Charge.EntryAmount. But, it is always hard to give answers without knowing all of the requirements! It just doesn't seem right for it to be in both types of relationships at the same time.
Another advantage to composition in CSLA is the use of switchable objects. With LineItem as its own full featured object, it could be a child when needed and a parent when needed. This allows you to pluck an object out of its hierarchy and use it on an add/edit form without the need for the whole tree.
There are always many ways to design objects and it is always fun to discuss the options.
xal replied on Monday, May 15, 2006
DancesWithBamboo:
Grady Booch?
Andrés
malloc1024 replied on Monday, May 15, 2006
Bamboo,
The "is-a" test is misleading. Replace the "is-a" test with
a "behaves as a" test. Inheritace should be based on behavoir not
data. The "behaves as a" test will help enforce this.
RanceDowner1234 replied on Thursday, May 18, 2006
Thanks everybody (especially Dances With Bamboo) for taking a look and offering some thoughts. Thanks especially for the links to the books I will check them out. This was definitely my first attempt at composition (aggregation), so I needed some input.
In my secenario a ResidentialCustomerPayment "is a" (or "behaves as a") Payment which in turn "is a" LineItem. Infact the "Primary Key" in the database for a ResidentialCustomerPayment will be "LineItemID". The primary key for any other derived objects such as BusinessCustomerPayment would also be "LineItemID". So, first of all, am I correct in concluding that inheritance might be the way to go here?
But for theory's sake... if I was to go the Composition (aka Aggregation) route...
I agree with Dances With Bamboo that the interface implementation route in my code sample didn't seem like any less work than just copying and pasting the properties into each "aggregate" object. As I was coding the example it did seem like it would be just easier to create a full fledged LineItem Object and then pass it as a property.
I suppose the "pass the reference" model would look something like...
Public Class LineItem
Inherits BusinessBase(Of LineItem)
'Add properties such as: LineItemID, EntryDate, EntryAmount
'Add some factory instantion: NewInstance( )
End Class
Public Class Payment
Inherits BusinessBase(Of Payment)
'Add properties such as: CheckNumber, CheckAmount, AppliesToPrincipal, AppliesToInterest
'Add some factory instantion: NewInstance( )
End Class
Public Class ResidentialCustomerPayment
Inherits BusinessBase(Of ResidentialCustomerPayment)
Public Shared Function NewInstance() As ResidentialCustomerPayment
Return DataPortal.Create(Of ResidentialCustomerPayment)()
End Function
Protected Overrides Sub DataPortal_Create(ByVal criteria As Object)
_LineItem = LineItem.NewInstance( ) 'Instantiate a private LineItem Object
_Payment = Payment.NewInstance( ) 'Instantiate a Private Payment Object
End Sub
Private _LineItem As LineItem
Public ReadOnly Property LineItem( ) As LineItem
Get
Return _LineItem
End Get
End Property
Private _Payment As Payment
Public ReadOnly Property Payment( ) As Payment
Get
Return _LineItem
End Get
End Property
End Class
Then in GUI code if I wanted to refer to the "derived" properties in ResidentialCustomerPayment I could just say something like
Dim rcp as ResidentialCustomerPayment = ResidentialCustomerPayment.GetInstance( )
MsgBox rcp.LineItem.EntryAmount.ToString.Trim
MsgBox rcp.LineItem.EntryDate.ToString.Trim
MsgBox rcp.Payment.CheckNumber.Trim
MsgBox rcp.Payment.AppliesToPrincipal.ToString.Trim
etc. etc.
But the reason in specific I was going that interface route was because I wanted to take advantage of visual studio.net windows data-binding... especially on a datagridview control. To explain...
In Visual Studio I have been creating a "Data Source" out of my objects. Then on the forms I create a BindingSource Control and set it's datasource property to My object. In this case I would set the datasource to the ResidentialCustomerPayment object.
Then I would set the datagridview control datasource to the BindingSource control. But when you goto add fields to the datagridview you are only able to add "base" properties. You don't get the option of seeing properties in child (aggregated) objects. And in my case I want to be able to show LineItemID, EntryDate, EntryAmount, CheckNumber, CheckAmount, AppliesToInterest on the same datagridview control.
To work around this I figured I could just implement the interfaces on the child objects and then delegate the "base" properties to the child properties.
Also when I'm dealing with other control types, and databinding.... If I use the "passing the reference" model above it's not quite as difficult, but it's a little messy. In order to use Microsoft's BindingSource controls I have to create separate BindingSource controls for both my Payment and LineItem Objects using the respective DataMember properties on each control. Then I can add databindings to say textbox, maskedTextbox, DateTimePicker and other controls. It works... but I was hoping for a simpler, cleaner solution.
Should I just forget about Microsoft's BindingSource controls and just populate any grids or controls the old-fashioned unbound way?
I kind of like the new BindingSource controls. They're pretty cool. I've always avoided DataBinding in the past because Visual Studios previous implementations of it kind of stunk up the joint. So if anyone can think of another way to use it... along with aggregation... your thoughts would be appreciated.
Rance
malloc1024 replied on Thursday, May 18, 2006
A payment does not behave as a lineitem. Therefore
inheritance is not appropriate in this situation. Shouldn’t there be an invoice or order object
that contains multiple lineitems? Are
payments actually applied to a lineitem or are they applied to an account? Your classes should not be designed with
binding in mind. You will not get a very
good design this way. If you want to
bind multiple objects you could try a façade that wraps multiple objects to
provide a simpler interface.
RanceDowner1234 replied on Thursday, May 18, 2006
Thanks for your input malloc1024. In this case the definition of a "LineItem" is an entry in an Accountbook, not an InvoiceItem. A LineItem can be either a debit or a credit.... similiar to a checkbook "line item". In my application there is a AccountBookQuarter Parent Object. Then there are Invoice "LineItems", Payment LineItems, Interest LineItems. So to answer your question directly, yes Line Items are applied to an Account, not an Invoice.
There are different types of Invoice LineItems. In this case my company sells wholesale energies to smaller Telephone, Gas, and Electric companies. But a Telephone company receives a different kind of Bill, with different fields on it, than a Gas or Electric Company Does.
Also there are different kinds of Payment methods. For instance a ResidentialOnlyServiceProvider is only allowed to pay by Check or Money Order. A CommercialServiceProvider is allowed to pay by check or Automatic Fund Transfer. There's some other logic too. For instance Telephone "ResidentialOnly" service providers are able to correct their own bill, namely "kilowatt hours used", sending that corrected information in with the payment. A Telephone CommercialServiceProvider cannot correct their bill, but must call in first, we correct the invoice, and then re-bill them. So on a Telephone ResidentialServiceProvider's payment we want to track what they stated on their returned bill, and before voiding out and correcting the invoice line item itself.
I'm sure all that was clear as mud. It's kind of muddy business rules :0)
Anyway... I tried setting up the inheritance chain as follows:
LineItemBase: With EntryID, EntryDate, EntryAmount (Debit or Credit)
InvoiceBase (Derives from "LineItemBase"): With Fields that all Invoices hold in common. Note: This object does indeed have a collection of InvoiceItems that applies to the total billable amount
TelephoneCompanyInvoice (Derives from InvoiceBase): With fields particular to an Telephone company invoice
GasCompanyInvoice: With fields particular to an Gas company invoice
ElectricCompanyInvoice: With fields particular to an Electric company invoice
PaymentBase (Derives from "LineItemBase"): With fields that all Payments have in common (for instance Check Number, and check amount, what part of payment applies to Principal, and what part applies to interest)
TelephonePayment (Derives from PaymentBase): Corrected Kilowatt hours is an extra field
TelephoneCommercialPayment (Derives from PaymentBase): But adds the Automatic Fund Transfer routing number
GasPayment (Derives from PaymentBase): Nothing extra right now, but perhaps in future???
ElecPayment (Derives from PaymentBase): Nothing extra right now, but perhaps in future???
Anyway... long story short... I kinda thought PaymentBase looked like a "behaves as a" scenario.... but even if it is... what's your take... inheritance or aggregation?
Thx again. I'm trying to get this OOP down. But boy, what a deep subject. Most people say your first OOP project fails. I'm trying not to fall into that category. I think Lhotka's set me in the right direction with his framework. I'm starting to grasp concepts like singleton instantiation, and factories. But like I said, I suppose it could be real easy to shoot yourself in the foot on this aggregation vs inheritance thing. Especially if some of the business rules change down the road.
Also thanks for the idea on the facade. I imagine that could take care of my databinding issues if I went with aggregation.
Rance
Copyright (c) Marimer LLC