Design Question

Design Question

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


selim51 posted on Thursday, June 26, 2008

Hello!

I need some help choosing the right pattern for my business object.

There is a general parent which is divided into three specialized parents, and there is a general child which is divided into three specialized children, too. The ChildA should be associated only with its ParentA, and ChildB with ParentB, and so on.

For now I have this:

public abstract class Parent : BusinessBase<Parent>
{
   
// general properties
    public Childs Childs;
}

public class ParentA : Parent
{
   
// special properties for ParentA
}

public class ParentB : Parent
{
   
// special properties for ParentB
}

public abstract class Child : BusinessBase<Child>
{
    // general properties
}

public class ChildA : Child
{
   
// special properties for ChildA
   
internal static ChildA NewChildA()
    {
       
return new ChildA();
   
}
}

public class ChildB : Child
{
   
// special properties for ChildB
    internal static ChildB NewChildB()
    {
        return new ChildB();
    }
}

public abstract class Childs : BusinessListBase<Childs, Child>
{
    
abstract public Child AddNewChild();
}

public class ChildsA : Childs
{
    public override Child AddNewChild()
    {
        var childA = ChildA.NewChildA();
        Add(childA);
        return childA;
    }
}

public class ChildsB : Childs
{
    public override Child AddNewChild()
    {
        var childB = ChildB.NewChildB();
        Add(childB);
        return childB;
    }
}

I don't like the fact to have an abstract child collection and a concrete child collection for each concrete child object. Is that the "right" way? Or is there another nicer solution? Am I right, that I would have to do some if or switch checks, when I use one child collection only (without abstract concrete collections)?

Yavuz

vdhant replied on Friday, June 27, 2008

Hi Yavuz
I can see the problem that you are talking about and it is a common issue.

the best way that i have found to solve this issue is by using IoC (Inversion of Control pattern). Basically this says that in your case you would create the instance objects in the abstract class. The problem is though and the reason why you haven't done it this way in the first place is that the abstract class doesn't know what instance type to create.

IoC deals with this very problem and defers this decision via a level of abstraction. If you have a look at this article http://msdn.microsoft.com/en-us/magazine/cc337885.aspx it is a really really really good article on this very issue. The IoC part is towards the end but make sure your read through as it builds up to this point.

In your case to achieve what you are after i would say that you would use generics on the base class.

public abstract class Childs[T] : BusinessListBase
where T : Child
{
public Child AddNewChild()
{
var child = new T();
Add(child);
return child;
}
}

public class ChildsA : Childs[ChildB]
{
}

public class ChildsB : Childs[ChildB]
{
}

Hope this helps.
Anthony

PLEASE NOTE -- I have used square brackets because my browser is having issues with diamond brackets

selim51 replied on Sunday, June 29, 2008

Hi Anthony!

Thank you for your answer. I can see that now AddNewChild() is implemented in the base collection class. However I have to implement one empty concrete collection class per concrete child classes. I was looking for an solution where I have only one non-abstract collection class which can handle each concrete child class. I have achieved that with a different generic collection class (see code below), so I can do this:

ParentA parentA = ParentA.NewParentA();
ChildA
childA1 = parentA.Childs.AddNewChild();
Child
childA2 = parentA.Childs.AddNewChild();

ParentB parentB = ParentB
.NewParentB();
ChildB
childB1 = parentB.Childs.AddNewChild();
Child childB2 = parentB.Childs.AddNewChild();

Each concrete parent class's child collection member (Childs) is of type Childs<ConceteChild>. Adding a new child to the collection returns the right concrete child object. While implementing the child collection member in each of the concrete classes, I can't do this:

Parent parentA = ParentA.NewParentA();
ChildA childA1 = parentA.Childs.AddNewChild();
// does not work
Child childA2 = parentA.Childs.AddNewChild();  // does not work

This is my solution so far:

public abstract class Parent : BusinessBase<Parent>
{
    // general Parent properties
   
protected
Parent() {}
}

public class ParentA :
Parent
{
   
// special ParentA properties
   
private Childs<ChildA> _childs = new Childs<ChildA
>();
    public Childs<ChildA
> Childs { get { return _childs; } }

    public static ParentA
NewParentA()
    {
        return DataPortal.Create<ParentA
>();
    }

    protected
ParentA() {}
}

public class ParentB : Parent
{
   
// special ParentB properties
   
private Childs<ChildB> _childs = new Childs<ChildB>();
   
public Childs<ChildB> Childs { get { return _childs; } }

   
public static ParentB NewParentB()
   
{
       
return DataPortal.Create<ParentB>();
   
}

   
protected ParentB() {}
}

public abstract class Child : BusinessBase<Child>
{
   
// general Child properties
   
protected Child() {}
}

public abstract class Child<T> : Child
{
   
internal static T NewChild()
   
{
       
return DataPortal.Create<T>();
   
}

   
protected Child() {}
}

public class ChildA : Child<ChildA>
{
   
// special ChildA properties
   
protected ChildA() {}
}

public class ChildB : Child<ChildB>
{
   
// special ChildB properties
   
protected ChildB() {}
}

public class Childs<T> : BusinessListBase<Childs<T>, Child<T>> where T : Child<T>
{
   
internal Childs() {}

   
public T AddNewChild()
   
{
       
var c = Child<T>.NewChild();
       
Add(c);
       
return c;
   
}
}

I haven't figured out yet how to implement the Childs member within the abstract parent class, so it returns the right child object based on the type of the parent.

Yavuz

tmg4340 replied on Sunday, June 29, 2008

selim51:
I was looking for an solution where I have only one non-abstract collection class which can handle each concrete child class.

...

I haven't figured out yet how to implement the Childs member within the abstract parent class, so it returns the right child object based on the type of the parent.

You said in your OP that you didn't like having to create specific parent and child classes to match up your parents and children.  Why?  What about that bothers you?  Because I think it's probably the better option.

In the end, I don't think you're going to get what you want without developing some if/switch code, as you mentioned earlier.  While that would certainly give you what you want, I don't think it's a better solution than simply creating concrete parents that return the appropriate concrete children, especially if you have behaviors specific to the concrete subtypes.  The code is much simpler to follow, and it keeps you from having to deal with a lot of headaches and casting.  The single-parent, single-collection solution is essentially mixing responsibilities, and as your needs change with the parent/child combinations, trying to maintain that within one class only makes it more complex.

If you want to create an IoC container, as Anthony suggests, then by all means, go ahead.  But you're still creating a match between a concrete parent type and concrete child type.  You're just doing it in your IoC configuration, instead of writing the code yourself.

HTH

- Scott

selim51 replied on Monday, June 30, 2008

Thank You for your arguments Scott!

tmg4340:
You said in your OP that you didn't like having to create specific parent and child classes to match up your parents and children.

Maybe You haven't understood or I haven't clearly expressed myself. I didn't say I don't like having specific parent and child classes. They have to be there because they are my business object. I meant that I dont't like having specific child collection classes, for each specific child class one specific child collection class. Anthony's generic abstract child collection class has reduced implementing AddNewChild() to one time in the base class. In my original approach I had to implement it n times in very similar ways (one in each specific child collection class).

I assumed that I will not have specific behaviours for the child collections, so I get rid of the specific child collection classes by turning Anthony's generic abstract child collection class to a generic non-abstract one.

Following the CSLA pattern for creating new business objects through static methods (factories) I had to change my abstract child class to a generic one, too, because T.NewChild() does not work. This has the side effect that the concrete children don't need a factory method. As long as the process of creating children in each concrete child is similar this is good.

tmg4340:
... I don't think it's a better solution than simply creating concrete parents that return the appropriate concrete children, especially if you have behaviors specific to the concrete subtypes.

...

If you want to create an IoC container, as Anthony suggests, then by all means, go ahead.  But you're still creating a match between a concrete parent type and concrete child type.

In the original post each concrete parent returned concrete collections with concrete children in it. Now each concrete parent return the same generic colection which holds that concrete parent's concrete children. Simply by matching them in this way: Childs<ConcreteChild> _childs. I am aware of that I have to match parents with their children. I have never said that I don't want that. For now the _childs field is declared in each of the concrete parent, so I can't access it after casting a concrete parent to its base abstract parent type.

When shifting the _childs field to the abstract parent class the question is which type does it have? Childs<Child> and Childs<Child<T>> result in a compilation error.

If there is no solution until the next few days I will use my original approach because I have to finish my project Smile [:)]

Yavuz

Copyright (c) Marimer LLC