Builder

What is the Builder Pattern?

The Builder Pattern is a creational design pattern that lets you construct complex objects step by step. Instead of having long, messy constructors with lots of parameters, you use a builder to gradually build up the object in a readable, flexible, and safe way.


Why Use Builder in FRC Robot Code?

Robots often involve configuring complex objects—like autonomous paths, PID settings, drivetrain settings, or subsystem options. Using the Builder pattern helps you:

  • Avoid messy constructors with too many arguments.

  • Make code more readable and self-documenting.

  • Allow optional settings without creating dozens of overloaded constructors.

  • Cleanly separate object construction from object logic.

  • Build test setups or simulation configurations easily and consistently.


Real-World Examples in FRC

Use Case
Description

Trajectory Configuration

WPILib's TrajectoryConfig uses a builder-like pattern.

PID Controller Setup

Creating custom PID gains per mode.

Command Groups

Step-by-step assembly of SequentialCommandGroup.

Dashboard Configurations

Building up tunable SmartDashboard settings.

Vision Pipeline Setup

Gradually configuring filters, thresholds, or camera parameters.

Subsystem Constructors

Creating a subsystem with optional motors/sensors for different robot builds.


The Builder Pattern: Step-by-Step

Without Builder (ugly and unreadable):

Shooter shooter = new Shooter(0.01, 0.3, 0.02, true, false, 12.0, 0.5);

What do all those numbers mean? It’s hard to tell. Mistakes are easy.


With Builder (clear and expressive):

Shooter shooter = new Shooter.Builder()
    .setKP(0.01)
    .setKI(0.3)
    .setKD(0.02)
    .enableFeedforward(true)
    .setMaxRPM(12.0)
    .setMinRPM(0.5)
    .build();

Now it's clear what each value means, and it's easy to read and modify.


Implementing a Builder (Shooter Example)

Step 1: The target class with a private constructor

public class Shooter {
    private final double kP, kI, kD;
    private final boolean feedforward;
    private final double maxRPM, minRPM;

    private Shooter(Builder builder) {
        this.kP = builder.kP;
        this.kI = builder.kI;
        this.kD = builder.kD;
        this.feedforward = builder.feedforward;
        this.maxRPM = builder.maxRPM;
        this.minRPM = builder.minRPM;
    }

    public static class Builder {
        private double kP = 0, kI = 0, kD = 0;
        private boolean feedforward = false;
        private double maxRPM = 0, minRPM = 0;

        public Builder setKP(double kP) {
            this.kP = kP;
            return this;
        }

        public Builder setKI(double kI) {
            this.kI = kI;
            return this;
        }

        public Builder setKD(double kD) {
            this.kD = kD;
            return this;
        }

        public Builder enableFeedforward(boolean ff) {
            this.feedforward = ff;
            return this;
        }

        public Builder setMaxRPM(double max) {
            this.maxRPM = max;
            return this;
        }

        public Builder setMinRPM(double min) {
            this.minRPM = min;
            return this;
        }

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

Summary: Key Points About Builder

Concept
Description

Goal

Separate construction from use, make complex object creation simple.

Fluent Interface

Each builder method returns this, so you can chain method calls.

Immutable Result

Final object is often immutable and safely configured.

Error-Proof

Avoids confusion with parameter order or skipping optional values.


WPILib Patterns That Mirror Builder

  • TrajectoryConfig and TrajectoryGenerator use builder-like chaining:

    TrajectoryConfig config = new TrajectoryConfig(maxSpeed, maxAccel)
                                  .setKinematics(driveKinematics)
                                  .addConstraint(voltageConstraint);
  • SendableChooser can be constructed and configured step by step.

  • SequentialCommandGroup and ParallelCommandGroup build up actions step-by-step.


When NOT to Use Builder

  • For simple objects with 2–3 parameters.

  • If an existing factory method is already simpler.

Last updated