Static Factories
Why Not Just new?
A static factory method is a public static method that returns an object, used instead of calling a constructor directly. At first glance this looks like extra ceremony for no reason — but it buys a few concrete things a plain constructor can't:
- A descriptive name.
Command.printMessage("Ready")says exactly what you're getting;new PrintCommand("Ready")(or worse, a constructor overload chosen by argument types alone) doesn't communicate intent nearly as clearly. - The freedom to not construct a new object at all. A factory method can return a cached, already-existing instance instead of a fresh one — a constructor, by contrast, always creates something new.
- The freedom to return a completely different concrete type, chosen by logic inside the method — the caller only ever sees the return type (often an interface), never which concrete class was actually picked.
public static Command printMessage(String msg)
{
return new InstantCommand(() -> System.out.println(msg));
}
Calling printMessage("Ready") reads clearly as "give me a command that prints this message" — the caller never needs to know or care that it's actually an InstantCommand wrapping a lambda underneath.
The Real Payoff: Choosing an Implementation by Condition
The pattern's biggest use in FRC code is picking between real and simulated hardware — deciding which concrete class to construct, in exactly one place, based on a condition:
public interface Drivetrain
{
void drive(double speed, double turn);
}
public class RealDrivetrain implements Drivetrain
{
public void drive(double speed, double turn) { /* control real Talons/Sparks */ }
}
public class SimDrivetrain implements Drivetrain
{
public void drive(double speed, double turn) { /* simulate motion via WPILib sim */ }
}
public class DrivetrainFactory
{
public static Drivetrain createDrivetrain()
{
if (RobotBase.isReal())
{
return new RealDrivetrain();
}
else
{
return new SimDrivetrain();
}
}
}
Drivetrain drivetrain = DrivetrainFactory.createDrivetrain(); // caller never picks the concrete class itself
This is a genuine alternative to the constructor-injection style from the IO-Layer Pattern (Ch.20) — instead of the caller deciding which IntakeIO to pass in, the factory itself makes that decision, centralizing the real-vs-sim logic in one place instead of duplicating an if (isReal()) check everywhere a subsystem gets built.
Factories for More Than Hardware
The same idea works for building the right object out of a whole family of choices — not just two (real/sim), but any number, selected by some other input:
public class AutoFactory
{
public static Command createAuto(String mode)
{
return switch (mode)
{
case "Taxi" -> new DriveForwardCommand(3.0);
case "2 Ball" -> new TwoBallAutoCommand();
case "Nothing" -> new WaitCommand(0.1);
default -> new PrintCommand("Unknown Auto Mode");
};
}
}
Command auto = AutoFactory.createAuto(chooser.getSelected()); // built from whatever the driver station selected
Common Pitfalls
- Returning a concrete class instead of an interface.
public static RealDrivetrain createDrivetrain()locks callers into one specific implementation — return the interface type (Drivetrain) so the real choice stays hidden inside the factory. - Duplicating the same real-vs-sim
ifcheck in multiple places. The whole point of a factory is centralizing that decision in one method — scatteringRobotBase.isReal()checks throughout the codebase defeats the purpose. - Confusing a factory with a constructor. A factory method is just a regular
staticmethod — it can validate arguments, return a cached instance, or pick any subtype, none of which a constructor is free to do (a constructor always returns a new instance of its own exact class).
Key Takeaways
- A static factory method returns an object in place of a direct constructor call, with a descriptive name, the option to return a cached instance, and the freedom to choose the actual concrete type internally.
- Its most common FRC use: centralizing real-vs-simulated (or comp-bot-vs-practice-bot) selection logic in one place, so callers only ever depend on an interface.
- Factories generalize beyond hardware — anything selected by a condition (an autonomous mode, a config option) is a natural fit.
- Always return the interface type from a factory method, not a specific concrete class, so the actual implementation choice stays hidden from callers.
Derived from other-reference-repo: team-5817-training/design-patterns/factory.md (T5817 30.2)
Deck context: mechacoder-test/src/lessons/java-2.js, slide 7 ("Static Factories")