3

First, I want to acknowledge that this question is very similar to this other one, but I wanted to ask with more specifics and hopefully garner higher quality answers.

Recently I followed a tutorial where the Builder pattern was implemented with a Director. I've streamlined the classes for demonstration purposes:

public class Director
{
    private readonly Builder _builder;

    public Director(Builder builder)
    {
        _builder = builder;
    }

    public void BuildProduct()
    {
        _builder.CreateProduct();
        _builder.BuildPart1();
        _builder.BuildPart2();
    }

    public Product GetProduct() => _builder.GetProduct();
}

public abstract class Builder
{
    protected Product Product;

    internal void CreateProduct()
    {
        Product = new Product();
    }

    internal Product GetProduct() => Product;

    internal abstract void BuildPart1();

    internal abstract void BuildPart2();
}

public class Thing1Builder : Builder
{
    internal override void BuildPart1() => Product.ThingStrings.Add("Thing-1 String-1");

    internal override void BuildPart2() => Product.ThingStrings.Add("Thing-1 String-2");
}

public class Thing2Builder : Builder
{
    internal override void BuildPart1() => Product.ThingStrings.Add("Thing-2 String-1");

    internal override void BuildPart2() => Product.ThingStrings.Add("Thing-2 String-2");
}

public class Product
{
    internal readonly ICollection<string> ThingStrings = new List<string>();

    public void Display()
    {
        foreach (string thingString in ThingStrings)
        {
            Console.WriteLine($"Thing string = {thingString}");
        }
    }
}

As I was following along with the tutorial, I couldn't help but wonder why we don't just put the Director's only meaningful method (the BuildProduct method) into the abstract base class of the builders. This still ensures all concrete builders get the same build template and does away with what seems like a useless layer. What advantages does the Director bring?

Here I have coded virtually the same thing, just without the director (Product class omitted because it did not change):

public abstract class BuilderWithoutDirector
{
    protected Product Product;

    public void CreateProduct()
    {
        Product = new Product();
        BuildPart1();
        BuildPart2();
    }

    public Product GetProduct() => Product;

    protected abstract void BuildPart1();

    protected abstract void BuildPart2();
}

public class Thing1BuilderWithoutDirector : BuilderWithoutDirector
{
    protected override void BuildPart1() => Product.ThingStrings.Add("Thing-1 String-1");

    protected override void BuildPart2() => Product.ThingStrings.Add("Thing-1 String-2");
}

public class Thing2BuilderWithoutDirector : BuilderWithoutDirector
{
    protected override void BuildPart1() => Product.ThingStrings.Add("Thing-2 String-1");

    protected override void BuildPart2() => Product.ThingStrings.Add("Thing-2 String-2");
}

Usages of these two examples look like this:

    private static void UseWithDirector()
    {
        var director = new Director(new Thing1Builder());
        director.BuildProduct();
        var thing1 = director.GetProduct();

        director = new Director(new Thing2Builder());
        director.BuildProduct();
        var thing2 = director.GetProduct();

        thing1.Display();
        thing2.Display();
    }

    private static void UseWithoutDirector()
    {
        var builder1 = new Thing1BuilderWithoutDirector();
        builder1.CreateProduct();
        var thing1 = builder1.GetProduct();

        var builder2 = new Thing2BuilderWithoutDirector();
        builder2.CreateProduct();
        var thing2 = builder2.GetProduct();

        thing1.Display();
        thing2.Display();
    }

These two methods output the same thing. I see a hint to an advantage with the Director version in that you create one director and reuse it with multiple builders which has a feeling of a top-level object that knows what's going on (please excuse the fuzzy logic there), but you still have to know about and create two different builders, so why not just use them directly?

Community
  • 1
  • 1
bubbleking
  • 2,977
  • 3
  • 26
  • 45

3 Answers3

3

Putting Director's job into Builder is a violation of Single Responsibility Principle because the Builder will have two responsibilities:

  1. Responsibility of a Builder: It knows how to implement the BuildPart1 and BuildPart2 methods.
  2. Responsibility of a Director: It knows which parts should be used in which order.

Indeed, when you, for example, change the order of the calls to BuildPart1 and BuildPart2 in the base class Builder, all of your concrete Thing*Builder(s) are affected unnecessarily (they have to be recompiled and redeployed).

Nghia Bui
  • 3,554
  • 12
  • 21
  • 1
    I find the SRP explanation compelling. I sometimes struggle with such matters when they are so abstract and not presented with more real-world-like solutions in reference material. The recompile/redeploy point is a good one I did not consider since in my care they're part of the same assembly. In a different setting, I can imagine clients making builders and hooking them to my director, leaving me with the knowledge of build order and not having to force changes on clients when the build order changes. In short, this is a good, clear answer that has specific reasons for the separation. – bubbleking Jan 09 '17 at 16:20
0

You can use the Director to encapsulates code for construction and the steps that is required to contruct the object. In the following example you would have to have a RedCarBuilder and a GreenCarBuilder to do it with your base class. Best example I could find :)

The BuilderPattern tries to solve the problem where multiple constructors exists for an object with different purposes. Example, a constructor for creating a red car and another for creating a green car. But in code it is hard to see what the different constructors does.

public class Car
{
    public int Wheels { get; set; }

    public string Colour { get; set; }
}

public interface ICarBuilder
{
    void SetColour(string colour);
    void SetWheels(int count);

    Car GetResult();
}

public class CarBuilder : ICarBuilder
{
    private Car car;

    public CarBuilder()
    {
        this.car = new Car();
    }

    public void SetColour(string colour)
    {
        this.car.Colour = colour;
    }

    public void SetWheels(int count)
    {
        this.car.Wheels = count;
    }

    public Car GetResult() => car;
}

public class CarBuildDirector
{
    public Car ConstructRedCar()
    {
        CarBuilder builder = new CarBuilder();

        builder.SetColour("Red");
        builder.SetWheels(4);

        return builder.GetResult();
    }

    public Car ConstructGreenCar()
    {
        CarBuilder builder = new CarBuilder();

        builder.SetColour("Green");
        builder.SetWheels(4);

        return builder.GetResult();
    }
}
Michael
  • 2,597
  • 1
  • 15
  • 29
  • Interesting. In my example the intention is to have a builder for each version of the product to be built. There aren't getters or setters. You seem to be recycling the builder but your director implementation is interesting because it provides a method for each product type. I could see the director in my example having value if it had a method for each product, thus allowing the client to not know about all those builders. – bubbleking Jan 07 '17 at 02:06
0

There is no requirement to have director while creating and using builder pattern.

In the example you suggested, when you do not use director, the builder classes look more like example of template method pattern.

Job of director here I think is to encapsulate logic of creating the final product by calling various methods of builder class.

Example of this you can find at https://sourcemaking.com/design_patterns/builder/java/1

But there is other version of buillder pattern suggested at http://www.javaworld.com/article/2074938/core-java/too-many-parameters-in-java-methods-part-3-builder-pattern.html

Thanks and regards, Chetan Ranpariya

Chris Moutray
  • 17,529
  • 7
  • 44
  • 65
Chetan
  • 6,380
  • 3
  • 19
  • 32