인터페이스의 개념
Coddy C 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 61개 중 40번째.
지금까지 우리의 구조체들은 데이터와 동작을 함께 혼합해 왔습니다. 예를 들어, 이름과 함수 포인터를 함께 두는 식입니다. 하지만 만약 구조체가 함수 포인터만 포함한다면 어떨까요? 이것은 강력한 무언가를 만들어냅니다. 바로 인터페이스입니다.
인터페이스는 작업이 어떻게 작동하는지 명시하지 않고 어떤 작업이 가능한지를 정의합니다. 이는 "이 인터페이스를 구현하는 모든 객체는 반드시 이러한 함수들을 제공해야 한다"는 계약과 같습니다.
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;ILogger 구조체는 함수 포인터 외에는 아무것도 포함하지 않습니다. 자체적인 데이터는 없으며, 순수하게 동작만을 설명합니다. "I" 접두사는 이것이 인터페이스임을 나타내는 일반적인 관례입니다.
ILogger를 받아들이는 모든 코드는 로그 메시지가 어디로 가는지 신경 쓰지 않습니다. 콘솔에 출력하거나, 파일에 쓰거나, 네트워크를 통해 데이터를 보낼 수도 있습니다. 호출자는 단지 log를 호출할 수 있다는 것만 알면 됩니다:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // 어떻게 로그를 기록하는지는 알 수 없습니다
}이러한 분리는 다형성의 본질입니다. process 함수는 어떠한 로거 구현체와도 작동합니다 — 함수의 코드를 변경하지 않고도 동작을 바꿀 수 있습니다. 다음 레슨에서는 이 인터페이스 계약을 충족하는 구체적인 구현체들을 만들어 볼 것입니다.
챌린지
쉬움함수 포인터만 포함하는 구조체인 ICalculator 인터페이스를 만들어 보겠습니다. 이 인터페이스는 계산기가 어떻게 작동하는지 구체적으로 명시하지 않고, 어떤 연산을 지원해야 하는지만 정의합니다.
코드를 정리하기 위해 두 개의 파일을 생성합니다:
calculator.h: 여기에 인터페이스를 정의합니다. 두 개의int매개변수를 받고int를 반환하는BinaryOp라는 함수 포인터 타입을 만듭니다. 그런 다음 두 개의 함수 포인터만 포함하는ICalculator구조체를 정의합니다:operate(BinaryOp타입)와describe(매개변수가 없고 반환값이 없는 함수 포인터). 이 구조체는 순수하게 인터페이스 역할을 하며, 데이터는 가지지 않고 동작만 정의합니다.main.c: 헤더 파일을 포함하고 인터페이스를 충족할 수 있는 구체적인 함수들을 구현합니다:add— 두 정수의 합을 반환합니다.multiply— 두 정수의 곱을 반환합니다.describe_add—Addition을 출력합니다.describe_multiply—Multiplication을 출력합니다.
ICalculator인스턴스를 생성하고, 입력에 따라 적절한 함수들을 연결한 뒤, 이를 사용하여 계산을 수행합니다.
프로그램은 세 개의 입력을 받습니다: 두 개의 정수와 연산 코드 (1은 덧셈, 2는 곱셈).
연산 코드에 따라 ICalculator에 적절한 함수 쌍을 연결하세요. 그런 다음 인터페이스를 통해 describe를 호출하여 연산 이름을 출력하고, 이어서 operate를 호출하여 결과를 계산하고 출력합니다.
입력이 5, 3, 1일 때의 출력 예시:
Addition
8입력이 4, 7, 2일 때의 출력 예시:
Multiplication
28핵심은 ICalculator 구조체가 일종의 계약을 정의한다는 것입니다. 이 구조체를 사용하는 모든 코드는 어떤 구체적인 함수가 연결되어 있는지 몰라도 describe와 operate를 호출할 수 있다는 것을 알고 있습니다. 헤더 파일에 include guard를 사용하는 것을 잊지 마세요.
치트 시트
인터페이스(interface)는 함수 포인터만을 포함하는 구조체로, 작업이 어떻게 작동하는지 명시하지 않고 어떤 작업이 가능한지를 정의합니다.
인터페이스 정의하기:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;"I" 접두사는 이것이 인터페이스임을 나타내는 일반적인 관례입니다. 이 구조체는 데이터를 포함하지 않으며, 순수하게 동작만을 설명합니다.
인터페이스 사용하기:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // 어떻게 로그를 남기는지는 알 수 없습니다
}이러한 분리는 다형성(polymorphism)을 가능하게 합니다. 함수는 인터페이스의 모든 구현체와 함께 작동하므로, 함수의 코드를 변경하지 않고도 동작을 교체할 수 있습니다.
직접 해보기
#include <stdio.h>
#include "calculator.h"
// TODO: add 함수 구현
// 두 정수의 합을 반환함
// TODO: multiply 함수 구현
// 두 정수의 곱을 반환함
// TODO: describe_add 함수 구현
// "Addition"을 출력함
// TODO: describe_multiply 함수 구현
// "Multiplication"을 출력함
int main() {
int num1, num2, op_code;
scanf("%d", &num1);
scanf("%d", &num2);
scanf("%d", &op_code);
// TODO: ICalculator 인스턴스 생성
// TODO: op_code에 따라 (1은 덧셈, 2는 곱셈),
// 계산기에 적절한 함수를 연결
// TODO: 인터페이스를 통해 describe 호출
// TODO: 인터페이스를 통해 operate를 호출하고 결과 출력
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.