Thread overview
Builder: Tiny Utility Library to Add a Builder API to Classes
Jan 05, 2023
Vijay Nayar
Jan 06, 2023
Vijay Nayar
Jan 06, 2023
thebluepandabear
Jan 06, 2023
Vijay Nayar
Jan 06, 2023
thebluepandabear
January 05, 2023

https://code.dlang.org/packages/builder

Interacting with many projects that are related to Java, I could not help notice that a common "Builder API" is not easily available in D.

What is the problem? When constructing classes, especially those with lots of data, there are two broad ways of building the object.

  1. Initializing each field in a different statement.
A a = new A();
a.setName("Bob");
a.setAge(20);
a.isProbation(false);
a.isActive(true);
...

This approach involves writing a lot of boiler plate, and it doesn't work well for quickly creating objects inline, such as during a function call.

  1. Using a constructor with many arguments.
A a = new A("Bob", 20, false, true);

This approach can construct arguments inline, such as during a function call, however, the arguments are not labeled, making it easy to get the order wrong or for the meaning to be unclear.

This library allows one to get the best of both worlds. Construction within a single statement, but also without losing meaning of the parameters, e.g.

class A {
  string name;
  int age;
  bool isProbation;
  bool isActive;

  mixin AddBuilder!(typeof(this));
}

A a = A.builder()
  .name("Bob")
  .age(20)
  .isProbation(false)
  .isActive(true)
  .build();
January 05, 2023

On Thursday, 5 January 2023 at 21:48:40 UTC, Vijay Nayar wrote:

>
  1. Using a constructor with many arguments.
A a = new A("Bob", 20, false, true);

This approach can construct arguments inline, such as during a function call, however, the arguments are not labeled, making it easy to get the order wrong or for the meaning to be unclear.

Hopefully we'll finally have named arguments. Considering they were accepted to language more than two years ago - maybe in the next century...

January 06, 2023

On Thursday, 5 January 2023 at 23:31:36 UTC, Vladimir Marchevsky wrote:

>

On Thursday, 5 January 2023 at 21:48:40 UTC, Vijay Nayar wrote:

>
  1. Using a constructor with many arguments.
A a = new A("Bob", 20, false, true);

This approach can construct arguments inline, such as during a function call, however, the arguments are not labeled, making it easy to get the order wrong or for the meaning to be unclear.

Hopefully we'll finally have named arguments. Considering they were accepted to language more than two years ago - maybe in the next century...

Named arguments would definitely obviate this tool, but in the meanwhile, this tool basically lets you achieve the same objectives in terms of single-expression construction and clearly labeled arguments. I guess one extra advantage of the builder library is that you also don't have to bother writing a constructor at all and can just use the default one.

January 06, 2023
>

.isActive(true)
.build();

Good 👍

how would I extend the builder methods?

January 06, 2023

On Friday, 6 January 2023 at 09:26:51 UTC, thebluepandabear wrote:

> >

.isActive(true)
.build();

Good 👍

how would I extend the builder methods?

The builder methods are automatically generated and go up the inheritance chain to capture all the fields in your class. (I assume that you are referring to inheritance when you say "extend"?)

Here is one of the unit-tests demonstrating this:

class A2 {
  int a;
  string b;
}

class B2 : A2 {
  int c;

  mixin AddBuilder!(typeof(this));
}

/// All inherited fields should be available from the builder.
unittest {
  B2 b2 = B2.builder()
      .a(3)
      .b("ham")
      .c(4)
      .build();

  assert(b2.a == 3);
  assert(b2.b == "ham");
  assert(b2.c == 4);
}
January 06, 2023

On Friday, 6 January 2023 at 12:54:07 UTC, Vijay Nayar wrote:

>

On Friday, 6 January 2023 at 09:26:51 UTC, thebluepandabear wrote:

> >

.isActive(true)
.build();

Good 👍

how would I extend the builder methods?

The builder methods are automatically generated and go up the inheritance chain to capture all the fields in your class. (I assume that you are referring to inheritance when you say "extend"?)

Here is one of the unit-tests demonstrating this:

class A2 {
  int a;
  string b;
}

class B2 : A2 {
  int c;

  mixin AddBuilder!(typeof(this));
}

/// All inherited fields should be available from the builder.
unittest {
  B2 b2 = B2.builder()
      .a(3)
      .b("ham")
      .c(4)
      .build();

  assert(b2.a == 3);
  assert(b2.b == "ham");
  assert(b2.c == 4);
}

I meant what if I want some extra behavior for the setter method other than just assigning a value to the field. E.g. if I set a password field, I might want to validate that it's valid.