Friday, October 31, 2008

Builder-style setters for Java

Setters idiom in Java is an evil and I hate it. And I don't hate it because you have to invoke setXXX methods multiple times when you have to set many fields. The most annoying thing for me is that you can only set one field in a line. This is because setter method return this stupid void. Here is the example:

public class Car {
private int maxSpeed;
// remainder omitted

public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
// remainder omitted
}
so my code using the Car class will look like this:

Car car = new Car();
car.setMaxSpeed(210);
car.setSpeedUnit("km/h");
car.setLength(5);
// remainder omitted
I have an idea how the life of the developers can be made easier using builder-style setters "pattern". Here is the new BETTER Car class:

public class Car {
private int maxSpeed;
private String speedUnit;
// remainder omitted

public Car setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
return this;
}

public Car setSpeedUnit(String speedUnit) {
this.speedUnit = speedUnit;
return this;
}
// remainder omitted
}
I could instantiate and initialize my Car object in one line (maybe wrapped but still one line of code):

Car car = new Car()
.setMaxSpeed(210)
.setSpeedUnit("km/h")
.setLength(5)
...
What do you think? Isn't it much easier? Of course if you extend Car class it becomes more tricky as you have to repeat all the setter methods like this:

public class Truck extends Car {
private int capacity;
private String capacityUnit;
// remainder omitted

@Override
public Truck setMaxSpeed(int maxSpeed) {
super.setMaxSpeed(maxSpeed);
return this;
}

@Override
public Truck setSpeedUnit(String speedUnit) {
super.setSpeedUnit(speedUnit);
return this;
}
// remainder omitted
}
But hey - who extends simple JavaBeans?

Maybe I can suggest this idea as a JSR-666 (there are two links :)? I'm curious what is your opinion about this idea. Please share your thoughts in the comments zone.

14 comments:

Romain said...

Hi,

I don't see any pb with the "fluent-setters", but I tried http://www.martinfowler.com/bliki/FluentInterface.html
for some of my classes and I was appealed to violate the SRP principle by doing stuf like
myObject.add(stuff).save();

It could give some bad habits to many developers I guess.. But only for setters, i think it's ok ..

Alex Miller said...

Actually, this has already been proposed for Java 7 as "chained invocation".

See:

Chained Invocation

Przemysław Bielicki said...

Thanks - I didn't know about it.
Anyway - I would like to avoid Factory class - just pure Thing. But I understand the Factory class is there just to show an example and cascading void methods will be available also in pure JavaBean classes.

Xavi Miró said...

Hi, Przemysław.

Maybe you would like to read about Joshua Bloch's New Builder pattern. It has this fluent style of setting attributes, but it also builds immutable objects.

You can find information from Josh Bloch himself here or in the second edition of his excellent book "Effective Java". I have blogged here about some possible implementations.

pitpat said...

I don't find this a very motivating example, because there's no great reason to not make Car immutable. It should either have a constructor that accepts all the arguments at once or have an inner Builder class, per Josh Bloch's builder pattern as mentioned by xavi.

Przemysław Bielicki said...

I perfectly know New Builder pattern but it requires a lot more work - that's why I'm not in favour of using it.
More so it is rather designed to handle different combinations and configurations of objects (it's a Builder pattern, right?) and what I need is just a stupid JavaBean and JavaBean is not immutable by definition.

groszek said...

Maybe I'm complaining too much, but the number of code lines didn't decrease using your pattern and you disliked the original setters, because "you can only set one field in a line" :). I don't see any advantage in such change, seems like you don't have real work if you improve your setters.

pitpat said...

Sorry, I meant I see no good reason to make Car mutable. It should be immutable.

Przemysław Bielicki said...

@groszek: I've always thought that line ends with semicolon (;) in C-like languages - therefore in "my way" I have only one line but I split it because blogger column is to narrow. Please start looking at it like a human not like a robot :)

@pitpat: generally I agree with you and Josh Bloch that we shoud strive to make all classes immutable but sometimes it doesn't make sense or is technically impossible (e.g. Hibernate accepts only mutable objects). Again - JavaBean is by definition mutable, but let's take a look at it from another perspective.
Car class represents the real-world car. This means that the speed, capacity and other properties of my car can change but it is still the same instance of my car. It doesn't make sense to buy a new car just to mount a trunk on the roof, right?

Thanks for comments.

groszek said...

I'm looking at it like a human, robots (compilers) only care about semicolons. Code should be readable and your pattern doesn't improve readability, it allows very strange coding for lazy programmers, for example:
car.setSpeed(90).watchForLimit().setSpeed(50)...
Your example is also not good, you can create a constructor for common setters.

Przemysław Bielicki said...

If your object has 20 or more properties and most of them are of the same type constructors will be nothing else than "coding horror".

watchForLimit() is not what I meant. I wrote about setters ONLY.

And how would you solve the problem of "lazy programmers"? You can't. You can't force people to start thinking - and there is no perfect solution.

From experience I know that the pattern I presented makes life much much easier for developers and doesn't cause problems. But if you want to hurt yourself you can do it easily.

And don't make every developer lazy and stupid - I believe in developer's common sense.

groszek said...

I don't make every developer lazy and stupid, I've just seen a lot (?: operator in C, implicit $_ in perl). Some people write ugly code, when it's just a temporary solution or when they are in hurry. I can imagine a situation, when your pattern can be misused and you would spend half an hour looking at few lines of such code.

Przemysław Bielicki said...

As I wrote there are no perfect solutions. You are right that this patter can be misused - I agree. However you can say the same about almost all patterns that usually work great.

At any rate I think that this flexible setter pattern can be useful in many case, though it has some caveats.

And thanks for your comments.

Gary Frost said...

I would like to propose using an alternative mutator method naming scheme for these chained method (fluent interface) scenarios.

Instead of setXyy() for mutating property xyy) why not just use a method named after the property itself xyy(...) thankfully Java has no problem having a method and property named the same.

So
new Car()
.setType("Ford")
.setColor(Color.BLUE);

becomes:-

new Car()
.type("Ford")
.color(Color.BLUE);

I know that this is not very 'bean' friendly, but these 'fluent mutators' could just call setXyy() internally. So one could use setXyy() or xyy()

I use this for Data Access Objects for creating/mutating Entities.