Vegard Stikbakke
Vegard Stikbakke

Reputation: 849

Java: Why use with methods instead of constructor?

Say you have a class called Person, and a Person has attributes such as name, id, age, etc. Instead of setting these values in the constructor, one does

new Person().withName("Lorem").withId("1234").withAge(29)

Where a with method is a call to a set method, and returns the object, e.g.,

public Person withAge(int age) {
    this.setAge(age);
    return this;
}

On a current project, I see a lot of code like this, often with 5-10 chained calls to different with methods. What are the benefits of doing this instead of setting these values in the constructor?

Upvotes: 4

Views: 381

Answers (4)

Dominik Reinert
Dominik Reinert

Reputation: 895

Its all about readability and fluent interfaces.

While your example is not a very good one for fluent interfaces, you can find on in the Java part of the wikipedia article::

Author author = AUTHOR.as("author");
create.selectFrom(author)
      .where(exists(selectOne()
                   .from(BOOK)
                   .where(BOOK.STATUS.eq(BOOK_STATUS.SOLD_OUT))
                   .and(BOOK.AUTHOR_ID.eq(author.ID))));

Fluent interfacing is a very advanced topic in the API design, that often comes with some kind of Builder pattern (to avoid too big and too many constructors) or Facade pattern (to avoid hard to understand, mostly internal APIs).

A pretty fluent API normally require a deep understanding of your requirements and a very good planning/preparation phase.


Note that using fluent APIs is closely connected to writing a DSL. Here you have an example by Martin Fowler and an extended, really good explanation of fluent interfacing, its advantages, disadvantages, theories, etc.

Upvotes: 2

Salih Erikci
Salih Erikci

Reputation: 5087

One advantage i see is readability.

If we extend the example

new Person()
.withName("Lorem")
.withId("1234")
.withAge(29)
.withHeight(170)
.withWeight(75)
.withTaxId("1234");

If we didn't use this pattern and used the constructor pattern we would end up lots of parameter without any description about them when using them.

new Person("Lorem","1234",29,170,75,"1234");

Upvotes: 2

AxelH
AxelH

Reputation: 14572

What are the benefits of doing this instead of setting these values in the constructor?

1) Overloading

You can manage the number of value you want to set easily, if you have a lot of parameter to set, but some are optionnal, you don't have to create specific constructor or passing null value.

 new Person("name", 19);
 new Person("name", 19, address);

 new Person("name", 19, phone);

(those are bad example ;) )

In you case, you just have to call the method you need (same with setters).

2) Identitication

Also, having a lot of parameters in a method/constructor tend to be difficult to read, to identify each parameter context

 new Person("frank", "John", "Emma");
 person.withName("frank").withFather("john").withMother("Emma");

Passing parameter to a method/constructor is nameless, you have to check the signature to understand what you are passing. With that notation, you have a more verbose and readable code. (again, same with setters).

3) Chainable setter The same would be done with setters but without the chainable feature you have here.

person.setName("name");
person.setAge(19);

person.withName("name").withAge(19);

Other than the readability, I don't think there is really some improvement, the chain need the method to return the instance itself, that give a redondant code in the class itself (return this;).

Upvotes: 3

greperror
greperror

Reputation: 5676

There seem to be 2 major advantages :

1.) Flexibility : With this pattern, you can basically choose fields to populate and not populate. While with constructors, you need to have multiple constructors to achieve the same. As in Person can be

new Person().withName("Loren")

Or it can be

new Person().withName("Loren").withAge(30)

with everything else null/default.

In case of constructor initialization, you had to have 2 constructors for both of these.

public Person(String name){
  //code
}

public Person(String name, String age){
//code
}

2.) As mentioned by other answers, readability.

new Person().withName("Loren").withAge(30).withId(567)

is more readable than

new Person("Loren", 30, 567)

Upvotes: 1

Related Questions