Factory

What is the Factory Pattern?

The Factory Pattern is a creational design pattern that provides a method to create objects without specifying the exact class of the object that will be created.

Instead of calling a constructor directly, you use a factory method that decides which class or object to return, often based on configuration or parameters.


Why Use Factory in FRC Robot Code?

FRC robots often support multiple hardware configurations, autonomous modes, or testing environments. Using Factory methods allows you to:

  • Decouple what you want from how it’s created.

  • Cleanly switch between real vs. simulated hardware.

  • Support different robot builds (e.g., comp bot vs. practice bot).

  • Create named commands or auto paths without hardcoding logic in multiple places.

  • Make subsystem mocking easier for tests.


Real-World Use Cases in FRC

Use Case
Description

Subsystem Factories

Return different drivetrain implementations depending on hardware or sim.

Autonomous Command Factories

Create Command objects based on selected auto mode.

Input Device Factories

Return real or simulated controllers.

Sensor Factories

Choose between real or mocked sensors (encoders, gyros, cameras).


Basic Factory Example: Subsystem

Let’s build a Drivetrain Factory that can return either a real or simulated drivetrain based on some config setting.

Step 1: Define the interface/base class

public interface Drivetrain {
    void drive(double speed, double turn);
}

Step 2: Implement two versions

public class RealDrivetrain implements Drivetrain {
    public void drive(double speed, double turn) {
        // Control Talons, Sparks, etc.
    }
}

public class SimDrivetrain implements Drivetrain {
    public void drive(double speed, double turn) {
        // Simulate motion using WPILib simulation
    }
}

Step 3: Create the factory

public class DrivetrainFactory {
    public static Drivetrain createDrivetrain() {
        if (RobotBase.isReal()) {
            return new RealDrivetrain();
        } else {
            return new SimDrivetrain();
        }
    }
}

Step 4: Use the factory in your robot code

Drivetrain drivetrain = DrivetrainFactory.createDrivetrain();

Factory for Autonomous Commands

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");
        };
    }
}

Then in RobotContainer or wherever you choose the auto:

SendableChooser<String> chooser = new SendableChooser<>();
chooser.addOption("Taxi", "Taxi");
chooser.addOption("2 Ball", "2 Ball");
...

Command auto = AutoFactory.createAuto(chooser.getSelected());

Factory vs Builder vs Strategy

Pattern
Purpose
FRC Example

Factory

Creates an object based on logic/conditions

Real vs. simulated gyro or drivetrain

Builder

Builds complex object step-by-step

Shooter PID config, path builder

Strategy

Switch between interchangeable behaviors

Different IO classes for same subsystem


Key Benefits in FRC

  • Hardware abstraction: Write code once, switch implementations automatically.

  • Easier testing: Use mock versions of subsystems in tests.

  • Auto selector clarity: Cleanly generate commands from dashboard input.

  • Avoid duplication: Avoid repeating creation logic in multiple places.


Example: Sensor Factory

public interface Gyro {
    double getHeading();
}

public class PigeonGyro implements Gyro {
    private final Pigeon2 pigeon = new Pigeon2(0);
    public double getHeading() {
        return pigeon.getYaw();
    }
}

public class SimGyro implements Gyro {
    private double heading = 0;
    public double getHeading() {
        return heading;
    }
}

public class GyroFactory {
    public static Gyro createGyro() {
        return RobotBase.isReal() ? new PigeonGyro() : new SimGyro();
    }
}

Best Practices

  • Keep factory logic simple and focused.

  • Return interfaces when possible (not concrete classes).

  • Centralize decision-making (don’t duplicate "real vs sim" logic all over).

  • You can combine with Singleton if the factory returns shared instances.

Last updated