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
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
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
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
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 {}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() 'add more rules here
End Sub
Joe
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
Joe
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