Strategy

What is the Strategy Pattern?

The Strategy Pattern is a behavioral design pattern that enables selecting an algorithm’s behavior at runtime. It defines a family of interchangeable algorithms (strategies), encapsulates each one, and makes them interchangeable within the context they operate in. This promotes flexibility and decouples implementation details from the core logic.


Why Use Strategy in FRC Subsystems?

In robot programming, especially for FRC, subsystems often interact with hardware devices like motors, sensors, or vision systems. However, hardware might change or need to be simulated for testing. The Strategy Pattern lets you:

  • Swap between real hardware interfaces and simulated/mock interfaces without changing the subsystem logic.

  • Support multiple hardware implementations (e.g., different motor controllers).

  • Test your code easily in simulation or on real robots.

  • Keep subsystem code clean and independent of specific hardware APIs.


Example Use Case: Different IO Implementations for a Subsystem

Imagine an Elevator subsystem that controls a motor and reads encoder positions.

Without Strategy

You might hardcode hardware calls directly inside the subsystem. This makes it hard to swap hardware or test:

public class Elevator {
    private TalonSRX motor;
    private Encoder encoder;

    public Elevator() {
        motor = new TalonSRX(1);
        encoder = new Encoder(0, 1);
    }

    public void setPower(double power) {
        motor.set(power);
    }

    public double getPosition() {
        return encoder.getDistance();
    }
}

Problems:

  • Can’t easily replace TalonSRX with another motor type.

  • Hard to simulate encoder/motor for testing.

  • Changes in hardware API break subsystem code.


Applying Strategy Pattern

Define an interface that abstracts elevator I/O operations:

public interface ElevatorIO {
    void setPower(double power);
    double getPosition();
}

Then create different implementations:

Real hardware implementation

public class ElevatorIOReal implements ElevatorIO {
    private TalonSRX motor = new TalonSRX(1);
    private Encoder encoder = new Encoder(0, 1);

    @Override
    public void setPower(double power) {
        motor.set(power);
    }

    @Override
    public double getPosition() {
        return encoder.getDistance();
    }
}

Simulated/mock implementation

public class ElevatorIOSim implements ElevatorIO {
    private double power;
    private double simulatedPosition = 0;

    @Override
    public void setPower(double power) {
        this.power = power;
        simulatedPosition += power * 0.1; // simple simulation logic
    }

    @Override
    public double getPosition() {
        return simulatedPosition;
    }
}

Refactor Elevator subsystem to use ElevatorIO

public class Elevator extends Subsystem {
    private final ElevatorIO io;

    public Elevator(ElevatorIO io) {
        this.io = io;
    }

    public void setPower(double power) {
        io.setPower(power);
    }

    public double getPosition() {
        return io.getPosition();
    }

    // Factory method for easy switching
    public static Elevator createReal() {
        return new Elevator(new ElevatorIOReal());
    }

    public static Elevator createSim() {
        return new Elevator(new ElevatorIOSim());
    }
}

Last updated