8

I am playing around with the Builder pattern and get stuck how to add a new "property" to a new-created object:

public class MsProjectTaskData {
  private boolean isAlreadyTransfered;
  private String req;    

   public static class Builder {    
    private boolean isAlreadyTransfered = false;

    public Builder withTransfered(boolean val) {
        isAlreadyTransfered = val; 
        return this;
    }    
    public MsProjectTaskData build() {
        return new MsProjectTaskData(this);
    }
   }

   private MsProjectTaskData(Builder builder) {
     isAlreadyTransfered = builder.isAlreadyTransfered;
   }

  public MsProjectTaskData(String req) {
    this.req = req;
  }
}

I can create a new object with Builder like this:

MsProjectTaskData data = new MsProjectTaskData.Builder().withTransfered(true).build();

But with this approach the req string from a new-created object is lost (of course).

Is there a possibility to create a new object with the new set isAlreadyTransfered variable and with the "old" req string from a "old" object?

Maybe I have to pass the old object reference to the Builder but I do not know how to do this. Maybe the use of Builder pattern is not really usefull for this approach?

EDIT: (After comment from Eugene)

Think, I got it:

public static class Builder {   
 private boolean isAlreadyTransfered = false;
 private MsProjectTaskData data;

 public Builder(MsProjectTaskData data) {
     this.data = data;
 }

 public Builder withTransfered(boolean val) {
     isAlreadyTransfered = val; 
     data.setAlreadyTransfered(isAlreadyTransfered);
     return this;
 }   
 public MsProjectTaskData build() {
     return data;
 }
}

Seems to work or is something wrong with the code above? Can I use this approach without consideration?

sk2212
  • 1,688
  • 4
  • 23
  • 43
  • Which `old` object reference are you talking about here? Add a parameter to the `build()` method. This ends up having the same effect as a copy constructor. – Sotirios Delimanolis Apr 25 '13 at 13:57
  • old object reference means a "old" object of MsProjectTaskData. I want to clone this object with the new value which is set within withTransfered(true) method. – sk2212 Apr 25 '13 at 14:00
  • In this special case a simple setTransfered() would do the job. If your goal is to get familiar with builder Pattern and its implementation, I guess you'll have to go and find a better example. What you are doing here looks more like Prototyping to me. – Fildor Apr 25 '13 at 14:00
  • @Fildor Yes, but I want to keep the object immutable. – sk2212 Apr 25 '13 at 14:02
  • I see. Well, you could give the Builder a "from" Method that takes a MsProjectTaskData as "Template" and then only change the Fields you want for the new instance. Oh, yeah or CTOR-arg as Eugene sais .. – Fildor Apr 25 '13 at 14:06

1 Answers1

17

Make the Builder constructor take as an argument the "old" object and set whatever you want from it to the new one.

Edit

You need to read a bit more about the builder pattern to get a better grasp at what it is and if you really need it.

The general idea is that Builder pattern is used when you have optional elements. Effective Java Item 2 is your best friend here.

For your class, if you want to build one object from another and use a Builder pattern at the same time, you

  1. Either pass the "old" object in the Builder constructor
  2. Create a method from or fromOld, etc.

So how does that looks like? I am going to provide only the first one you can figure out the second on your own.

class MsProjectTaskData {
    private final String firstname;
    private final String lastname;
    private final int age;

    private MsProjectTaskData(Builder builder){
        this.firstname = builder.firstname;
        this.lastname  = builder.lastname;
        this.age       = builder.age;
    }

    public static final class Builder{
        //fields that are REQUIRED must be private final
        private final String firstname;
        private final String lastname;

        //fields that are optional are not final
        private int age;

        public Builder(String firstname, String lastname){
            this.firstname = firstname;
            this.lastname  = lastname;
        }

        public Builder(MsProjectTaskData data){
            this.firstname = data.firstname; 
            this.lastname  = data.lastname;
        }

        public Builder age(int val){
            this.age = val; return this;
        }

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

    public String getFirstname() {
         return firstname;
    }

    public String getLastname() {
         return lastname;
    }

    public int getAge() {
         return age;
    }
}

And how you will create one object from another:

   MsProjectTaskData.Builder builder = new MsProjectTaskData.Builder("Bob", "Smith");
   MsProjectTaskData oldObj = builder.age(23).build();
   MsProjectTaskData.Builder newBuilder = new MsProjectTaskData.Builder(oldObj);
   MsProjectTaskData newObj = newBuilder.age(57).build();
   System.out.println(newObj.getFirstname() + " " + newObj.getLastname() + " " + newObj.getAge()); // Bob Smith 57
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • Can you please provide some code? I did not get it to work :(. – sk2212 Apr 25 '13 at 14:30
  • thanks, that works! Well you say that maybe the use of Builder pattern is not really useful here. Currently I try to change my mutable objects of my current project to immutable ones. This is not so easy if I need to change a property of such an object and put this one back to a container arraylist. – sk2212 Apr 26 '13 at 07:56
  • @Eugene I have exactly similar problem on cloning the builder pattern to make a new builder [here](http://stackoverflow.com/questions/41206286/how-to-clone-old-builder-to-make-a-new-builder-object). I wanted to see if you can help me out. –  Dec 19 '16 at 01:48