6

I am constructing a car class that has an engine, gearbox, clutch etc. I don't want a bloated constructor that takes 7 parameters, so I decided to use the builder pattern. All the parts are required. However, how do I make the user of the Car class use all the parts' setters, as they are all mandatory? Throw exceptions?

public class Car {

    private Engine engine;
    private Chassis chassis;
    private GearBox gearBox;
    private Coupe coupe;
    private Exterior exterior;
    private Interior interior;
    private Clutch clutch;

    public Car(Builder builder) {
        engine = builder.engine;
        chassis = builder.chassis;
        gearBox = builder.gearBox;
        coupe = builder.coupe;
        exterior = builder.exterior;
        interior = builder.interior;
        clutch = builder.clutch;
    }

    public static class Builder {

        private Engine engine;
        private Chassis chassis;
        private GearBox gearBox;
        private Coupe coupe;
        private Exterior exterior;
        private Interior interior;
        private Clutch clutch;


        private Car build() {
            return new Car(this);
        }

        public Builder setEngine(@NonNull Engine engine) {
            this.engine = engine;
            return this;
        }

        public Builder setChassis(@NonNull Chassis chassis) {
            this.chassis = chassis;
            return this;
        }

        public Builder setGearBox(@NonNull GearBox gearBox) {
            this.gearBox = gearBox;
            return this;
        }

        public Builder setCoupe(@NonNull Coupe coupe) {
            this.coupe = coupe;
            return this;
        }

        public Builder setExterior(@NonNull Exterior exterior) {
            this.exterior = exterior;
            return this;
        }

        public Builder setInterior(@NonNull Interior interior) {
            this.interior = interior;
            return this;
        }

        public Builder setClutch(@NonNull Clutch clutchs) {
            this.clutch = clutchs;
            return this;
        }

    }


}

I want the user so call ALL of the builder setters, not an optional subset of them. How do I do that?

Is there another way to construct a car without having a huge constructor that takes so many parameters?

EDIT: I looked at The builder pattern and a large number of mandatory parameters but there is no solution there that prevents huge constructors.

Community
  • 1
  • 1
Kaloyan Roussev
  • 14,515
  • 21
  • 98
  • 180
  • Possible duplicate of [The builder pattern and a large number of mandatory parameters](http://stackoverflow.com/questions/7302891/the-builder-pattern-and-a-large-number-of-mandatory-parameters) – jaco0646 Nov 12 '15 at 14:20
  • Yeah, I read that one, but there is no solution there that prevents a huge constructor :( – Kaloyan Roussev Nov 12 '15 at 14:26
  • Yes, but how do I make sure the Builder user will call all of the setters and not a subset of them... Thats the main problem. – Kaloyan Roussev Nov 12 '15 at 14:40
  • I developed a free Intellij plugin that may be what you are looking for: https://github.com/banterly91/Java-Builder-Guided-Completion-Intellij-Plugin – dragosb Oct 16 '21 at 06:19

3 Answers3

7

Builders are for things where lots of parts are optional or you have many different configurations. The build() method then makes sure that the specific configuration works. A HTML builder makes sense, a string builder not so much.

If there are no optional parts, you have these options:

  • Get rid of the builder and use a constructor that requires all parts. Classic solution, least readable and hard to extend.

  • You can throw an exception in the build() method for each missing part. That's a lot of code to write and kind of goes against the pattern.

  • You can add methods with more than a single argument to the builder. That's a mix of the two above. You're still using the builder pattern (so you can easily add more configurations later) but your API also communicates more clearly which parts are mandatory.

    As an example, you might get an engine in the future which already contains the gearbox or which requires a specific gearbox (i.e. when you use this engine, you also select the gearbox). To implement this, you'd create a second builder method which just asks for the engine and determines the gearbox automatically.

  • Use a factory with protected methods which build the parts. The factory will make sure all parts are supplied to the car. If you want to replace a part, you can override the protected method. This works well if you have lot of defaults and only 2-3 useful configurations.

  • Use several builders. Instead of build everything from individual screws, create your car from larger building blocks: propulsion, interior, body. Create your car from those three with a constructor (three parameters is good). Now you can use three builders to create those three. Try to find a balance between required and optional elements.

  • Chain builders as explained by mlk below. This forces the user to fill in all parts (since you can only call the build() at the end of the chain). Main drawbacks here are: A lot of code to write and hard to follow from the user perspective since the code is spread over many classes. jOOQ is an example here; the project implements the SQL syntax as a chain of builders.

Remember what the builder pattern tries to solve: They make it easy to add more parts since all existing code doesn't need to change if the new part is optional. If the new part is mandatory, the builder pattern becomes a liability: With a huge (unreadable) constructor, you get compile errors in all places that needs fixing. The builder will fail at runtime in this case; you need an IDE to find all the places which need fixing.

Community
  • 1
  • 1
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • Great answer, but what i wonder about. Wouldn't bring option 3 the same problems to the readability of the code? I mean you either put a load of parameters to a constructor or to a method in the builder. The amount of parameters does not change. So option 2 stays. but that's against the idea of the builder pattern, so wouldn't it in this case be the best choice to just reconsider the whole builder pattern? – Mark Smit Nov 12 '15 at 14:14
  • *"Builders are for things where lots of parts are optional."* ... No, I disagree. I would say that "builders are for things where parts -can- be optional". The Builder Pattern is apt for just the kind of problem that the OP is trying to solve, and I would not recommend that he abandon it for the more unwieldy telescoping constructor pattern. – scottb Nov 12 '15 at 14:22
  • So there is no solution that is not ugly? – Kaloyan Roussev Nov 12 '15 at 14:23
  • 2
    @J.K.: One of the reasons to use the Builder Pattern is to forestall ugliness. In your shoes, I would not have the `build()` method throw an exception. A good immutable class will enforce its invariants in its (private) constructor before returning the new object. If required arguments are missing, the (private) constructor invoked by your Builder should throw an `IllegalStateException`. – scottb Nov 12 '15 at 14:32
  • 1
    @MarkSmit: Correct. Option #3 isn't the perfect solution; It's just the best compromise if you want to keep the builder. Maybe he would be better off with a factory that has protected methods to build the parts. He could then override the factory to replace parts. – Aaron Digulla Nov 12 '15 at 14:34
  • so the solution is for the constructor to throw that exception if any of the parts is missing? – Kaloyan Roussev Nov 12 '15 at 14:34
  • @AaronDigulla can you provide an example of the factory pattern in option 4? – Kaloyan Roussev Nov 12 '15 at 14:38
  • @J.K. ugly is in the eye of the beholder. When you write code, you can optimize it for many dimensions: Readability, maintainability, time to write it, performance, ease of use. Most of the time, those are mutually exclusive. Which dimension do you want to go? – Aaron Digulla Nov 12 '15 at 14:38
  • Readability and maintainability. – Kaloyan Roussev Nov 12 '15 at 14:39
  • 2
    @J.K. Replace the setters with methods that create a default instance of the engine, gearbox, etc. Call them in the `build()` method. The difference between factory and builder is that the factory knows how to build while the builder is configured since there are too many possible configurations. – Aaron Digulla Nov 12 '15 at 14:41
  • 1
    Long constructors are hard to read and even harder to change. Imagine all the places in the code that you would have to change if you added a component. – Aaron Digulla Nov 12 '15 at 14:41
  • J.K. Try to create bigger building blocks; see my edits. – Aaron Digulla Nov 12 '15 at 14:50
5

If the primary reason for this is to have a fluent API rather than removing a bloated constructor then you could chain builders together:

class Engine {}
class Door {}
class Car {
    Car(Engine engine, Door door) {

    }
}

class CarBuilder {
    private Engine engine;

    public CarWithEngineBuilder withEngine(Engine engine) {
        this.engine = engine;
        return new CarWithEngineBuilder();
    }

    class CarWithEngineBuilder {
        private Door door;


        public CarWithEngineAndDoor withDoor(Door door) {
            this.door = door;
            return new CarWithEngineAndDoor();
        }

        class CarWithEngineAndDoor {
            public Car build() {
                return new Car(engine, door);
            }
        }
    }
}

class TestStuff {
    {
        Car c = new CarBuilder().withEngine(new Engine()).withDoor(new Door()).build();
    }
}

Or if your main concern is the size of the constructor, maybe the constructor is telling you something, and you could look at the class and see is some parts are logically "together". I.e. are Engine, Gears & Brake are one part of larger component? Should the car be DriveSystem and Chassis (which includes an Exteriorand Interior). Then constructor for Car has a manageable number of parameters, as does DriveSystem and Chassis?

Michael Lloyd Lee mlk
  • 14,561
  • 3
  • 44
  • 81
  • *"If you are after a builder for a fluid API"* ... IMO, this phrasing is awkward. A Builder can be thought of as -part- of an API (and using one enables a fluent style when constructing objects), but I would never say that a Builder is a fluent API. – scottb Nov 12 '15 at 14:25
  • A fluent API would be a good solution here. The main drawback is that you need to write a lot of code to build one in Java :-) – Aaron Digulla Nov 12 '15 at 14:51
  • This is the right answer to exactly my question, however, it requires a LOT of code, and I don't wanna think about how this is going to look with 7 levels of depth. – Kaloyan Roussev Nov 12 '15 at 15:02
  • IMO part two of my answer is the right answer, that is your should look at what a `Car` is and subdivide it. – Michael Lloyd Lee mlk Nov 12 '15 at 15:25
0

If you look to the builder pattern, you can find out, that there is important thing, which you do not have, and it is Director. And also Builder interface, that defines all the required methods.

Director should call all methods required on interface builder (in your case "build engine, build coupe etc."). The class implementing the Builder interface must override all methods or the code does not even compile.

libik
  • 22,239
  • 9
  • 44
  • 87