28.3.1. `FlightTask` 기본 클래스(Base Class)의 템플릿 메서드 패턴(Template Method Pattern)

28.3.1. FlightTask 기본 클래스(Base Class)의 템플릿 메서드 패턴(Template Method Pattern)

수십 가지에 달하는 PX4 커스텀 비행 모드들이 수만 줄의 파편화된 스파게티 코드로 전락하지 않도록 꽉 잡아주는 강력한 소프트웨어 공학적 접착제가 바로 **템플릿 메서드 패턴(Template Method Pattern)**이다.

최상위 기저 클래스인 FlightTask는 단순히 다형성 포인터 껍데기 역할만 하는 것이 아니다. 스스로 뼈대(Skeleton) 알고리즘의 순서도를 엄격하게 통제하면서, 개별 비행 모드(자식 클래스) 개발자들이 오직 ‘세부 구현 연산식’ 퍼즐 조각만 끼워 넣도록 강제하는 템플릿 패턴의 완벽한 튜토리얼을 보여준다.

1. 템플릿 메서드 패턴의 할리우드 원칙 (Hollywood Principle)

“Don’t call us, we’ll call you(우리를 부르지 마라, 우리가 당신을 부를 것이다).” 객체 지향의 고전적인 할리우드 원칙이 PX4 FlightTask 시스템 전체를 관통한다.

개별 비행 모드를 개발하는 하위 프로그래머는 자신이 짠 FlightTaskAutoMission 객체의 함수를 언제 호출해야 할지 스케줄링할 필요가 없다.
상위 데몬인 FMM(Flight Mode Manager)은 매 루프마다 부모 클래스(FlightTask)에 정의된 updateInitialize() 라는 템플릿 메서드를 먼저 무조건 호출한다. 이 부모 클래스의 메서드 안에서 센서 데이터 주입과 안정성 검사가 모두 끝나면, 그제야 그 내부에서 자식 클래스가 오버라이딩(Overriding) 해놓은 update() 함수 콜백을 호출해 준다. 자식 클래스는 그저 불림을 당할 뿐이다.

2. 부모 클래스의 사전 정지 작업: updateInitialize()와 센서 은닉

템플릿 메서드 패턴에서 가장 빛나는 부분이 바로 이 ’공통 사전 준비 로직의 중앙집중화’다. FlightTask.cpp 본문을 뜯어보면 부모 클래스가 자식 대신 얼마나 많은 잡일을 해주는지 알 수 있다.

  • 구독 데몬 통합: 부모 클래스의 updateInitialize() 함수 내부는 _sub_estimator_states, _sub_vehicle_local_position 등 수십 개의 uORB 토픽들을 updated() 플래그로 검사하고 복사해 오는(Fetch) 노가다성 코드로 꽉 차 있다.
  • Time-delta(dt) 추출: 앞 장에서 설명한 그 정밀한 hrt_abstime 기반 타임 지터(Jitter) 보상용 dt 스텝 계산도 바로 여기서 부모 클래스가 전담하여 실수형(Float) 멤버 변수인 _deltatime에 미리 구워 놓는다.
  • 캡슐화된 상태(State) 프로텍트: 취합된 센서 데이터들은 자식 클래스가 마음대로 값을 조작하지 못하도록, 읽기 전용 상수 보호막인 const 상태로 래핑되어 자식 객체의 내부 멤버 구조체인 _position, _velocity, _yaw 치환 변수에 살포시 안착한다.

덕분에 자식 클래스인 FlightTaskManual 소스 코드 어디를 뒤져봐도 uORB 토픽을 orb_copy 하고 타임스탬프를 깎는 지저분한 인프라 코드는 단 한 줄도 보이지 않는다. 오직 부모가 차려준 _velocity 밥상을 떠먹는 제어 수식만 깔끔하게 남아있게 된다.

3. 핵심 뼈대 구성: virtual bool update() = 0; (순수 가상 함수)

부모가 모든 인프라 밥상을 차렸다면, 숟가락을 드는 것은 자식 클래스의 몫이다. FlightTask 부모 클래스는 이 숟가락질을 강제하기 위해 C++ 문법의 **순수 가상 함수(Pure Virtual Function)**라는 족쇄를 채워둔다.

// FlightTask.hpp 내의 순수 가상 함수 선언
virtual bool update() = 0;
  • 인스턴스화 원천 차단: 이 = 0 구문 하나로 인해, C++ 컴파일러는 FlightTask 부모 클래스 단독으로는 절대 램(RAM)에 new 인스턴스를 올려주지 않는다(Abstract Class).
  • 구현율(Implementation)의 강제: 만약 대학원생이 새로운 비행 모드 FlightTaskMyCustomMode를 상속(Inherit)받아 만들었는데, 깜빡하고 update() 함수를 오버라이딩하여 내부에 PID 수식을 채워 넣지 않았다면? 이 코드는 드론에 플래싱(Flashing) 되기도 전에 컴파일 에러를 강렬하게 뿜어내며 빌드 자체를 멈춰버린다.

결과적으로 템플릿 메서드 패턴 구조 하에서 상위 FMM은 “자식이 뭔 짓을 하든 무조건 update() 함수를 가지고 있을 것이다“라는 수학적 확신을 가지고 무지성 다형성 호출을 갈길 수 있으며, 하위 프로그래머는 “나는 OS 스케줄링이나 uORB를 1도 모르지만, 부모의 규칙대로 update() 안표에 PID 수식만 짜 넣으면 이 거대한 픽스호크 군단이 내 뜻대로 모터를 굴려줄 것이다“라는 절대적 안전 보장을 받게 되는 C++ 인터페이스 계층 설계의 정수를 보여준다.