Using Factory Methods with Polymorphic Children In Collections

Using Factory Methods with Polymorphic Children In Collections

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


JZuerlein posted on Tuesday, August 08, 2006

I've read Rocky's post on using polymorphic children in collections, and started working with that idea, but I'm a bit confused about how to add items to the collection.  Using Rocky's example....

Public Interface ILineItem

  Inherits Csla.Core.IEditableBusinessObject

 

End Interface

 

<Serializable()> _

Public Class TaxableItem

  Inherits Csla.BusinessBase(Of TaxableItem)

 

  Implements ILineItem

 

  ' ...

 

End Class

 

<Serializable()> _

Public Class LineItems

  Inherits Csla.BusinessListBase(Of LineItems, ILineItem)

 

  ' ...

 

End Class

 

What should the add method look like for the "LineItems" class.  My first thought was to overload the AddNew method on the LineItems class to take in a variable with the taxableitemtype.  Or is it better to make a public factory method for TaxableItem, so they can be created first then added to the collection?

 

Thoughts?

 

Jeff

RockfordLhotka replied on Tuesday, August 08, 2006

There are (at least) two schools of thought as to how child objects should be added to collections. (note that we're not talking about data fetching here - that's a different story)

One is that you should create the child object first, then pass it to an Add() method. That works fine, but does mean that your child class needs to expose a public factory method so the UI code can create the child.

The other approach is that your collection should have AddBlah() methods that accept enough parameters that the collection can create and add the new child - possibly returning the new child as a result of the method. I favor this approach, largely because it abstracts the process from the UI. In the case of polymorphic children, the UI just calls the right AddBlah() overload, and the right kind of child object is created.

Either way, this is a classic case where you likely want a factory method outside of ANY of the child classes. You might call it LineItemFactory, with a set of methods like NewLineItem() - with overloads to get the right data for each type of line item out there. This NewLineItem() factory method is what makes the decision about which type of child object to create - ultimately calling a Friend/internal factory method in the child class itself to do the creation (at least that's my preference).

longbridge replied on Thursday, August 10, 2006

I am a newbie to Csla.net, and I am getting some difficulties in the Polymorphic object fetching in collection. Could somebody please show how to write a neat Fetch(SafeDataReader dr) function by Rocky's LineItems example? Thanks a lot.

JZuerlein replied on Thursday, August 10, 2006

Here is an example of what I'm using.  It is a collection of questions, so for example....

IQuestion <- AbstractQuestion <- MultipleChoiceWithComment

Private Sub Fetch(ByVal dr As SafeDataReader)

RaiseListChangedEvents = False

Dim typeID As Integer

While dr.Read()

typeID = dr.GetInt32("QuestionTypeID")

If typeID = QuestionTypeEnum.MultipleChoiceWithComments Then

Me.Add(MultipleChoiceQuestionWithComment.GetMultipleChoiceQuestionWithComment(dr))

ElseIf typeID = QuestionTypeEnum.MultipleChoiceNoComments Then

Me.Add(MultipleChoiceQuestion.GetMultipleChoiceQuestion(dr))

ElseIf typeID = QuestionTypeEnum.Comment Then

Me.Add(CommentQuestion.GetCommentQuestion(dr))

End If

End While

RaiseListChangedEvents = True

End Sub

longbridge replied on Saturday, August 12, 2006

It really helps. Thanks a lot.

Xabatcha replied on Friday, February 16, 2007

Thanx for an example....it helped.

But How did you solve the update method in Data Access region of  child list. As mentioned Rocky, my childs has implement IEditableBusinessObject, which they do through my Interface IToken. Looks like this X509Token > BusinessBase, IToken where IToken > IEditableBusinessObject.
Back to the child list...in Update method Update, Insert, DeleteSelft methods of child are called. But my Childlist works only with interface IToken. There are not those methods available.

So my Qs follows>
1. Do I need change type of item into specific type in every loop before calling item methods( line 04,09,11)?
2. Am I missing some other Interface from csla?
3. Am I completly wrong?

Bye bye X.

Part of my Child list ......
01  internal void Update(IIdentity parent, DbTransaction transaction) {
02      RaiseListChangedEvents = false;
03      foreach (IToken item in DeletedList)
04          item.DeleteSelf(transaction);
05      DeletedList.Clear();
06
07      foreach (IToken item in this)
08          if (item.IsNew)
09              item.Insert(parent, transaction);
10          else
11              item.Update(transaction);
12      RaiseListChangedEvents = true;
13  }

RockfordLhotka replied on Friday, February 16, 2007

CSLA doesn’t define how a root or parent object talks to its children – that’s entirely up to you.

 

You have just a few options:

 

1.       Use a known base type that implements internal methods

2.       Use a known interface type that implements public methods (public through the interface anyway)

3.       Use reflection (late binding) to invoke the methods by name

 

Which you choose is really up to you and your needs.

 

Number 1 is difficult due to the generic base class issue. There are days I regret making the CSLA base classes generic… However, it would be ideal, because it offers high performance and doesn’t add extra public members to your interface.

 

Number 2 is unfortunate, because it opens the door for the UI developer to “cheat” and use that interface. It muddies your object’s API in a way that can only be solved by manual code review. But this is a high performance option.

 

Number 3 has a negative performance impact and prevents the compiler from finding simple typo errors in method names. However, this isn’t a bad option in many cases, because it avoids both the generic base class issue and eliminates the need to add public methods to your objects interface.

 

Rocky

 

 

From: Xabatcha [mailto:cslanet@lhotka.net]
Sent: Friday, February 16, 2007 8:27 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Using Factory Methods with Polymorphic Children In Collections

 

Thanx for an example....it helped.

But How did you solve the update method in Data Access region of  child list. As mentioned Rocky, my childs has implement IEditableBusinessObject, which they do through my Interface IToken. Looks like this X509Token > BusinessBase, IToken where IToken > IEditableBusinessObject.
Back to the child list...in Update method Update, Insert, DeleteSelft methods of child are called. But my Childlist works only with interface IToken. There are not those methods available.

So my Qs follows>
1. Do I need change type of item into specific type in every loop before calling item methods( line 04,09,11)?
2. Am I missing some other Interface from csla?
3. Am I completly wrong?

Bye bye X.

Part of my Child list ......
01  internal void Update(IIdentity parent, DbTransaction transaction) {
02      RaiseListChangedEvents = false;
03      foreach (IToken item in D eletedList)
04          item.DeleteSelf(transaction);
05      DeletedList.Clear();
06
07      foreach (IToken item in this)
08          if (item.IsNew)
09              item.Insert(parent, transaction);
10          else
11              item.Update(transaction);
12      RaiseListChangedEvents = true;
13  }


ajj3085 replied on Friday, February 16, 2007

RockfordLhotka:
Number 2 is unfortunate, because it opens the door for the UI developer to “cheat” and use that interface. It muddies your object’s API in a way that can only be solved by manual code review. But this is a high performance option.


Could the problem be solved by making the interface internal, and only implementing the interface explicitly?  I've done this where I needed an interface for internal use but didn't want the UI to see it.  I don't see why that couldn't be done here.

RockfordLhotka replied on Friday, February 16, 2007

Good point Andy – that’ll teach me to answer questions before I’ve had my morning coffee J

 

Rocky

 

From: ajj3085 [mailto:cslanet@lhotka.net]
Sent: Friday, February 16, 2007 10:39 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: Using Factory Methods with Polymorphic Children In Collections

 

RockfordLhotka:

Number 2 is unfortunate, because it opens the door for the UI developer to “cheat” and use that interface. It muddies your object’s API in a way that can only be solved by manual code review. But this is a high performance option.



Could the problem be solved by making the interface internal, and only implementing the interface explicitly?  I've done this where I needed an interface for internal use but didn't want the UI to see it.  I don't see why that couldn't be done here.


emmanuel replied on Monday, December 03, 2007

Hi Rocky and Andy,

 

I am new to CSLA.NET and actually new to C# and OOP.

By the way Rocky, I just want to say that your books and framework are making my learning of

OOP and C# so much more enriching and enjoyable.

 

Having followed the advice in this thread carefully, I still do not see how you can make the interface

for a collection of polymorphic children internal.

It seems to me that in order to achieve a collection with polymorphic child objects as described in:

http://www.lhotka.net/Article.aspx?area=4&id=b8515cd9-7f8e-43df-9efd-cd958dfdc21a

 

we must define a public interface (e.g. public interface ILineItem) which inherits from

Csla.Core.IEditableBusinessObject.

 

This seems to force us to declare void Update, void Insert and void DeleteSelf in the said public interface so that each child must implement these methods as public (as opposed to internal).

Can one of you please explain explicitly how to avoid having Update, Insert, and DeleteSelf as public methods (thus manipulatable by the UI) coming from the public interface?

In other words, can you please explain to me explicitly by example why Andy really does have a point?

 

Thank you,

Emmanuel

ajj3085 replied on Tuesday, December 04, 2007

well you're starting off wrong; you create an internal interface, like this:

internal interface IMyInterface {
   string MyProperty { get; set; }
}

Then you implement the interface explicitly:

public sealed class MyBo : BusinessBase<MyBo>, IMyInterface {
   string IMyInterface.MyProperty {
       get { return myProperty; }
       set { myProperty = value; }
   }
}

emmanuel replied on Tuesday, December 04, 2007

Hi Andy,

Thank you very much for your quick reply.

I still can't seem to make the interface IMyInterface internal!

Did you leave out the fact that IMyInterface must inherit from Csla.Core.IEditableBusinessObject on purpose?

Here's what I have following the scheme in the first post of this thread:

namespace Test.Library

{
   internal interface ILineItem : Csla.Core.IEditableBusinessObject {}
}

namespace Test.Library
{
    [Serializable()]
    public sealed class TaxableItem : BusinessBase<TaxableItem>,ILineItem
    { // ... }
}

namespace Test.Library
{
    [Serializable()]
    public sealed class DiscountableItem : BusinessBase<DiscountableItem>,ILineItem
    { // ... }
}

namespace Test.Library
{
    [Serializable()]
    public class LineItems : BusinessListBase<LineItems, ILineItem>
    { // ... }
}

When I try to compile this I get:

Error 1 Inconsistent accessibility: base class 'Csla.BusinessListBase<Test.Library.LineItems,Test.Library.ILineItem>' is less accessible than class 'Test.Library.LineItems'

This is before we even try to implement the interface explicitly.

I believe this is because the interface ILineItem must inherit from the interface

Csla.Core.IEditableBusinessObject (as explained by Rocky) which is **public**.


This, as I understand, implies that the interface ILineItem must be public in the first place

which is exactly what we are trying to avoid!


What am I not getting?


Thank you,
Emmanuel.

 

ajj3085 replied on Tuesday, December 04, 2007

Sorry, I forgot.  Yes, the interface must inherit from IEditableBusinessObject as well.  But you only need to implment your interface explicitly.

Now if you try to use that in your definition of the BusinessListBase, it will have to be public.  I hit the same problem.  I have an ILineItem interface, which is public and contains only things the UI should see.

Then any internal only members are defined in an internal interface, ILineItemInternal, which inherits ILineItem.

If you go this route, you'll need some casting.

Copyright (c) Marimer LLC