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
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
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
andTrajectoryGenerator
use builder-like chaining:TrajectoryConfig config = new TrajectoryConfig(maxSpeed, maxAccel) .setKinematics(driveKinematics) .addConstraint(voltageConstraint);
SendableChooser
can be constructed and configured step by step.SequentialCommandGroup
andParallelCommandGroup
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