Encapsulation Lesson

Encapsulation: Protecting Your Robot's Brain

Encapsulation is a core principle of Object-Oriented Programming that involves bundling data and methods into a single unit (a class) and restricting access to its internal state. It's like putting a protective case around your robot's delicate electronics.

The Problem: Uncontrolled Access

Imagine if any part of your robot's code could directly command a motor. A bug in your vision code could accidentally cause your arm to move, leading to chaos. Encapsulation prevents this by controlling how data is accessed and modified.

The Core Idea: Data Hiding

We use the `private` access modifier to "hide" a class's important fields (like motor controllers and sensors). This means they can only be accessed by the methods inside that same class, preventing outside interference.

Controlled Access: Getters and Setters

Since `private` fields are hidden, how do we interact with them? We provide `public` methods that act as controlled gateways to the data.

  • Getter Methods: Public methods that allow other classes to safely read the value of a private field (e.g., `getArmAngle()`).
  • Setter Methods: Public methods that allow other classes to safely change the value of a private field, often including crucial validation logic (e.g., `setArmAngle(double angle)`).

An FRC Example: A Shooter Subsystem

Let's look at a `Shooter` subsystem. We want to protect its motors and ensure we can't set an unsafe target speed.

public class Shooter {
    // 1. Data Hiding: These fields are private and protected.
    private Spark m_flywheelMotor;
    private Encoder m_flywheelEncoder;
    private double m_targetRPM;

    // A constant for the safe operating limit of the motor.
    private static final double MAX_RPM = 5000.0;

    public Shooter(int motorPort, int encoderPortA, int encoderPortB) {
        m_flywheelMotor = new Spark(motorPort);
        m_flywheelEncoder = new Encoder(encoderPortA, encoderPortB);
        m_targetRPM = 0.0;
    }

    // 2. Controlled Access: Public "setter" with validation.
    public void setTargetRPM(double rpm) {
        // Validation logic: only accept safe values.
        if (rpm >= 0 && rpm <= MAX_RPM) {
            this.m_targetRPM = rpm;
        } else {
            System.out.println("Error: Unsafe RPM value requested: " + rpm);
        }
    }

    // Public "getter" to read the current speed.
    public double getActualRPM() {
        return m_flywheelEncoder.getRate(); // Assuming encoder is configured for RPM
    }
    
    // Another getter
    public double getTargetRPM() {
        return m_targetRPM;
    }
}
    

Using the Encapsulated Class

Now, other parts of the code can't directly touch the motor. They must go through our safe, public methods.

// In a command or another part of the code:
Shooter robotShooter = new Shooter(10, 0, 1);

// This is safe and works as intended.
robotShooter.setTargetRPM(4500.0);

// This is also safe. The validation logic inside the setter will reject it.
robotShooter.setTargetRPM(9000.0); // Prints an error, m_targetRPM remains unchanged.

// This causes a COMPILE ERROR because m_flywheelMotor is private.
// robotShooter.m_flywheelMotor.set(1.0); // This is not allowed!
    

Test Your Knowledge

Question: What is the primary benefit of making a class's fields (like motor controllers) `private`?