1

Im having difficulties to figure out how to change some field in a object created by builder pattern: for example this is the class

public class Pizza {
  private int size;
  private boolean cheese;
  private boolean pepperoni;
  private boolean bacon;

  public static class Builder {
    //required
    private final int size;

    //optional
    private boolean cheese = false;
    private boolean pepperoni = false;
    private boolean bacon = false;

    public Builder(int size) {
      this.size = size;
    }

    public Builder cheese(boolean value) {
      cheese = value;
      return this;
    }

    public Builder pepperoni(boolean value) {
      pepperoni = value;
      return this;
    }

    public Builder bacon(boolean value) {
      bacon = value;
      return this;
    }

    public Pizza build() {
      return new Pizza(this);
    }
  }

  private Pizza(Builder builder) {
    size = builder.size;
    cheese = builder.cheese;
    pepperoni = builder.pepperoni;
    bacon = builder.bacon;
  }
}

and Pizza obect is created like this:

Pizza pizza = new Pizza.Builder(12)
                       .cheese(true)
                       .pepperoni(true)
                       .bacon(true)
                       .build();

now what I'm trying to find out is how to change in this object for example cheese field to false? I don't have getters and setters, I know I can use reflection but it makes code harder to read and understand. So is builder pattern useful to not final objects ?

jan
  • 53
  • 2
  • 4
  • 3
    add getters/setters. *So is builder pattern useful to not final objects* builder is to build object, not to mutate it. – Antoniossss Aug 24 '18 at 06:50
  • 1
    just add the getter and setter methods. As far as I know there is no such thing as a restriction that you're not allowed to provide them in your POJO when using the Builder-Pattern as this pattern is only to "build"/create your object. – the hand of NOD Aug 24 '18 at 06:53

4 Answers4

4

The builder pattern is usually good to create immutable objects but it does not need to do so. If you'd like to be able to change values after build, you just need to add a setter for that.

Michael
  • 2,443
  • 1
  • 21
  • 21
  • Hmm, so when I will use Builder Pattern for a class which has for example 8 variables, I will have getter/setter for those fields and beside that "a builder" who will build an object for me... It seems that it will be a lot of code... (not using for example Lombock or any other lib). In this situation the best example for me is only getter and setter (less code and easy to read and I can modify them). – Kacu Aug 24 '18 at 07:08
  • @Kacu I agree, I'd go with builder for immutable objects and have only getters for the fields. This is Java, it is famous for a lot of boilerplate code. – Michael Aug 24 '18 at 07:15
3

how to change in this object for example cheese field to false?

You should add a setter to the Pizza as:

class Pizza {
    //...

    public void setCheese(boolean cheese) {
        this.cheese = cheese;
    }

    @Override
    public String toString() {
        return String.format("cheese: %s", this.cheese);
    }
    //...
}

And then you can use it as:

    System.out.println(pizza);
    pizza.setCheese(false);
    System.out.println(pizza);

After your initialization, the output:

cheese: true
cheese: false

So is builder pattern useful to not final objects ?

There are some interesting posts discussing Builder Pattern: Three Reasons Why I Like the Builder Pattern and When would you use the Builder Pattern?.

Personally, I would say it quite depends on your case itself.

Hearen
  • 7,420
  • 4
  • 53
  • 63
2

The best way is to use Lombok's @Builder annotation. And you need to specify (toBuilder = true) to be able to convert the object back to the builder and modify the field.

@Builder(toBuilder = true)
public class Pizza {
 ...
}

So, you've created the Pizza
Pizza pizza = Pizza.builder()
                   .cheese(true)
                   .pepperoni(true)
                   .bacon(true)
                   .build();

And now you want to modify one field - super easy!

Pizza modifiedPizza = pizza.toBuilder()
                           .cheese(false)
                           .build();
Max
  • 766
  • 9
  • 19
0

Builder class should have methods to set the optional parameters and it should return the same Builder object after setting the optional attribute. As "cheese" is your optional parameter so you need a setter for it in your Builder class

public Builder cheese(Boolean cheese) {
            this.cheese = cheese;
            return this;
        }

Then you can use :

Pizza pizza1 = new Pizza.Builder(12)
                       .cheese(false)
                       .pepperoni(true)
                       .bacon(true)
                       .build();
Adya
  • 1,084
  • 9
  • 17