Design Question -- Inheritance

Design Question -- Inheritance

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


ballistic posted on Thursday, November 08, 2007

Hello,

I currently have a class called Framing, which Inherits BusinessBase(Of Frame).  This class has a simple Get method:

Public Shared Function GetFrame(ByVal Id As Guid) As Frame
     Return DataPortal.Fetch(Of Frame)(New
Id_Criteria(Id))
End
Function

I have the Fetch method along with the private Id_Criteria class.

Now I would like to create classes that inherit Frame since there are multiple types of frames.  WallFrames, MultiOpeningFrames, CustomFrames, etc.

I would like to be able to say:
Dim objWallFrame as WallFrame = WallFrame.GetWallFrame ( Id)

Since the information in the database is the same for all types of frames I would like to reuse the Get method of the parent instead of defining it in all of the children.  Something like:

Public Shared Function GetWallFrame(ByVal Id As Guid) As WallFrame
     Return CType
(Frame.GetFrame(Id), WallFrame)
End Function

The code above does not work, since I'm not able to convert a parant type into a child type.

I believe Inheritance is the solution for what I want, but without being able to cast the parent to child type, it seems I have to duplicate the code in the parent.

Thank you,

 - Andy

skagen00 replied on Thursday, November 08, 2007

When I first started down this road I think I tried to approach it the way you were.

The approach I'm taking now and one I'm very happy with involves the use of a factory class - factory methods on that class return an instance of IFrame (in your circumstance) where IFrame is an interface defined that largely matches what your base class can implement (or minimally define abstractly).

A couple of threads to perhaps look at are this:

http://forums.lhotka.net/forums/post/13759.aspx
http://forums.lhotka.net/forums/post/10942.aspx

In the end, one can say: FrameFactory.GetFrame(Guid id), which will return an IFrame - and the job of the GetFrame in FrameFactory is determine the necessary type (by perhaps a lightweight fetch using the Id to simply return the type) and then a delegation to the appropriate child class, such as WallFrame.GetWallFrame if the type was WallFrame.

My two cents,

Chris

 

DavidDilworth replied on Friday, November 09, 2007

I would agree that the object factory route is the way to go.

You could, if you prefer, return a reference to an abstract base (ABC) class instead of an interface.  That's a pattern we've used in part of our application.  The overall object factory pattern is the same though.

Our ABC inherits from a CSLA derived base class and you get all the standard CSLA methods (i.e. Save()) on the resulting instance object.

To maintain the "standard" scope rules associated with our other CSLA objects, the factory has all the public static methods and each individual concrete inherited BO has internal factory methods that are used by the factory.

HTH

skagen00 replied on Friday, November 09, 2007

Hey David,

How do you return a reference to an abstract base class? I would agree that the interface wouldn't be needed if you could return a reference to the abstract base but with generics you can't just return a type of Frame<T>? Are you just returning type object?

And even if you get an instance to abstract base under an object instance, how can you do anything with it if it's cast as object? That's where I find the interface useful for me.

Just curious!

Chris

 

DavidDilworth replied on Friday, November 09, 2007

The following is based on code in our app.  I've snipped out the implementation details, but it illustrates the key points.

The factory looks like this:

public class AbstractFactory
{
   /// <summary>
   /// Create a new <c>Dynamic</c> instance object.
   /// </summary>
   ///
<param name="name">
   /// The identifying name of the <c>Dynamic</c>
type.
   ///
</param>
   ///
<returns>
   /// A reference to an instance derived from the <see cref="AbstractBase"/>
class.
   ///
</returns>
   public static AbstractBase NewConcrete(string
name)
   {
      //implementation in here creates an instance of a concrete
   }
}

And the class definitions look like this:

public abstract class AbstractBase : BusinessBase<AbstractBase> {}

public class ConcreteClass : AbstractBase {}

JoeFallon1 replied on Friday, November 09, 2007

Try this in the WallFrame class:

Public Shared Function GetWallFrame(ByVal Id As Guid) As WallFrame
  Return DataPortal.Fetch(Of WallFrame)(New Id_Criteria(Id))
End Function

Because WallFrame Inherits from Frame the DataPortal_Fetch method will be called in the lowest level class that it is implemented in.So if you don't override it in WallFrame it will walk up the chain and use the version in Frame thus giving you the re-use you were looking for.

Your WallFrame class should override GetIdValue.

You can also add any other properties you need that are specific to WallFrame plus any business rules.

Protected Overrides Sub AddBusinessRules()
 
MyBase.AddBusinessRules()

  'add more rules here
End Sub

Joe

stefan replied on Friday, November 09, 2007

Joe,

are you sure what you suggets is really usefull?
DataPortal_Fetch might be called in the base class,
but this way, you would never be able to retrieve any property values specific to the derived class (WallFrame). Even worse, these properties would always show up with their default values,
and overwrite what was stored in the db on the next save...

Not quite useful, I think. Correct me if I am wrong.

EDIT:
Sorry Joe,
I just saw that ballistic explicitly said that there was no difference in the persisted data.
So there is just different behaviour (->differing validation rules),
and so your suggestion was absolutely useful :-)

Stefan



JoeFallon1 replied on Friday, November 09, 2007

Stefan,

No problem.

It is also useful if you simply Override DataPortal_Fetch and then call MyBase.Fetch to grab the "common prorpeties" and then grab the additional properties in the rest of the code block.

I use this pattern a lot. In fact I split out DP_Fetch into a bunch of overridable sub-methods so that I only have to override a small piece to get this effect.

In the example below I could override FetchData and then choose to either grab the base properties or ignore them and build a new SQL statement that fetches all properties. Alternatively, I could override PostFetchData and then just grab the extra properties there too.

Protected Overrides Sub DataPortal_Fetch(ByVal criteria As Object)
  'other code removed for clarity

 
DoFetch(criteria)
End Sub

Protected Overridable Sub DoFetch(ByVal criteria As Object)
  SetDefaults()
 
Dim cn As IDbConnection = Nothing
 
Try
   
cn = DAL.CreateConnection
    cn.Open()
    FetchData(criteria, cn)
    PostFetchData(criteria, cn)
    FetchChildren(criteria, cn)
 
Finally
   
cn.Close()
 
End Try
 
MarkOld()
  ValidationRules.CheckRules()
End Sub

Protected Overridable Sub FetchData(ByVal criteria As Object, ByVal cn As IDbConnection)
 
Dim dr As SafeDataReader
 
Dim crit As CriteriaCode = DirectCast(criteria, CriteriaCode)
  dr =
New SafeDataReader(DAL.ExecuteReader(cn, CommandType.Text, DAO.Select(crit.Code)))

  Try
   
With dr
     
If .Read() Then
       
mCode = Trim(.GetString("code"))
        mDesc = Trim(.GetString(
"desc"))
     
End If
   
End With
 
Finally
   
dr.Close()
 
End Try
End Sub

Protected Overridable Sub PostFetchData(ByVal criteria As Object, ByVal cn As IDbConnection)
  'marker method that can be overridden in child class
End Sub

Protected Overridable Sub FetchChildren(ByVal criteria As Object, ByVal cn As IDbConnection)
 
'marker method that can be overridden in child class
End Sub

Joe

DavidDilworth replied on Monday, November 12, 2007

Joe, a great example of how to extend the base CSLA framework using the open-closed principle

This code is open for extension by many different derived classes, but closed for modification so that the "standard" behaviour of CSLA is kept under your control.

Copyright (c) Marimer LLC