7

I have two classes (A and B) that are both complex to construct, with multiple properties that must be validated at construction time. I want to use the Builder pattern to construct these objects, but among the constraints on construction of these objects are:

  • An instance of A must contain multiple instances of B
  • Each instance of B has a child relationship to a single instance of A

In addition, I need to be able to add new instances of B to A after A has been constructed.

It seems like this must be a pretty common scenario. Is there some pattern other than Builder or to augment Builder that handles this situation?

Option 1 - With a "placeholder" class

The best solution that I have come up with so far uses an impotent version of the Builder class, with the Build() method removed (I call it a Placeholder) to specify the parameters for the child class.

class ClassA {
    get propertyC
    ...
    get list<A> children

    restricted constructor() {}

    BBuilder AddChild() {}
}

class ClassB {
    get propertyD
    ...
    get ClassA parent

    restricted constructor(ClassA parent)
}

class ABuilder {
    get / set propertyC
    ...
    BPlaceholder AddChild() {}

    ClassA Build() {
        VALIDATE properties
        BUILD ClassA
        foreach child { BUILD child }
    }
}

class BPlaceholder {
    get / set propertyD
    ...

    restricted constructor(ABuilder parent) {}
}

class BBuilder : BPlaceholder {
    restricted constructor(ClassA parent) {}

    ClassB Build() {
        VALIDATE properties
        BUILD child
    }
}

Notice that, of the 5 classes in this example, the only one that can be simply constructed is ABuilder. The other classes are all instantiated, either directly or indirectly, through this class.

Option 2 - Similar to Abstract Factory

Another possibility I have considered is to remove the Build() method from both Builder classes (in which case they are no longer Builders) and pass both classes to a third Factory class to perform the construction, similar to Abstract Factory. This doesn't seem as nice to me for two reasons, though:

  • The interface doesn't intuitively guide you to the proper use
  • Construction of B after A has already been instantiated is substantially different from construction of B before A has been instantiated

Rationale for avoidance of simple construction

One possibility is to just use simple construction, something like this:

objA = new ClassA()
objA.Add(new ClassB(objA))

This is obviously a possibility, but it is missing a key element found in the Builder pattern. Builder allows you to ensure that there is no way to construct an "incomplete" instance of a class. My constraints state that a valid instance of ClassA must have at least one child of type ClassB. In the example above, objA exists (for a short time) without any children, and is hence invalid.

6
  • 2
    I guess design patterns are ontopic here, but huge chunks of source code are definitely now. Please use a small example (in pseudo code) to make your point. Also, make sure that your are not asking a code review question; for this site, you'd have to ask about a concept or for a reference.
    – Raphael
    Commented Feb 8, 2016 at 21:19
  • 1
    @Raphael - Apologies. I'm not looking for a code review. I figured that actual code might make the question clearer. Thanks for your comment. I'm really looking for a pattern recommendation here. I can come up with several solutions, but one of the benefits of patterns is that they facilitate communication and understanding. With the solution I have come up with, it's difficult to clearly and concisely explain what I did and why. If I use a documented pattern, then the what and why already exist, and I just need to reference them. I haven't found a pattern that fits my requirements, though.
    – danBhentschel
    Commented Feb 9, 2016 at 15:34
  • @D.W. - I have added a section to address your question. Hopefully this makes what I'm looking for a bit clearer. Thanks for your comment.
    – danBhentschel
    Commented Feb 9, 2016 at 15:35
  • The answer here most likely depends a lot on how you are deciding what As and Bs to create. If you could give more context around what data and/or events lead to the creation of these object, it would help.
    – JimmyJames
    Commented Sep 8, 2016 at 18:17
  • Also, are these immutable objects or is there simply a minimum of 2 Bs and more can be added to A later?
    – JimmyJames
    Commented Sep 8, 2016 at 18:18

1 Answer 1

1

I would do something along the lines of...

public class A
{
    public List<B> childern {get;private set;}
    public A(DataReader dataForA, DataReader dataForBs)
    {
        //validate and set A properties from first datareader,
        ///validate we have at least one B
        while(dataForBs.Read())
        {
            var parent = this;
            var b = new B(dataForBs, parent) //validate b data in constructor
            this.childern.Add(b);
        }
    }
}

Using datareaders here for simplicity, but obviously you could have any 'raw' data construct or just a list of parameters.

Since all the validation, child construction and linking is done in the constructor for A or B it should be impossible to have an instance of either A with no children or B with no parent.

You will need to create your own version of List which you cant remove everything from but I guess this is outside the scope of your problem

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.