6

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.

migrated from cs.stackexchange.com Feb 11 '16 at 10:26

This question came from our site for students, researchers and practitioners of computer science.

  • 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 Feb 8 '16 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 Feb 9 '16 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 Feb 9 '16 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 Sep 8 '16 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 Sep 8 '16 at 18:18
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, privacy policy and cookie policy