10. Remove SubController Class#

Date: 2025-10-01

Related: PR #222

Status#

Accepted

Context#

FastCS provides two separate classes for building controller hierarchies: Controller for top-level controllers and SubController for nested components. This has become a purely philosophical distinction and now just adds limitations for no benefit:

  • Design-Time Commitment: Developers have to choose class at definition time, before knowing all contexts where components might be used

  • Reduced Reusability: A component designed as SubController can’t be reused as a top-level controller without changing its base class

Decision#

Unify Controller and SubController into a single Controller class that can be used in both top-level and nested contexts. Whether a Controller is “top-level” or “nested” is now determined by how it is used, not by its class.

Key architectural changes:

  • Remove SubController class entirely

  • Move root_attribute property to Controller

  • Any Controller instance can now be nested in any other Controller

Consequences#

Benefits#

  • Composition over Inheritance: Hierarchy determined by usage, not class definition

  • Increased Reusability: Controllers work in any context without refactoring

  • Simpler Mental Model: One class for all controller use cases

  • Reduced Coupling: No design-time commitment to hierarchy level

  • Easier Evolution: Controllers can start standalone and be nested later

Migration Pattern#

Before (Two classes):

from fastcs import Controller, SubController

class RampController(SubController):  # Forced to use SubController
    start = AttrRW(Int())
    end = AttrRW(Int())

class TempController(Controller):  # Forced to use Controller
    def __init__(self):
        super().__init__()
        self.register_sub_controller("Ramp1", RampController())

After (One class):

from fastcs import Controller

class RampController(Controller):  # Just use Controller
    start = AttrRW(Int())
    end = AttrRW(Int())

class TempController(Controller):  # Just use Controller
    def __init__(self):
        super().__init__()
        self.add_sub_controller("Ramp1", RampController())

# RampController can now be used as a top-level controller or nested