C++ 데이터 추상화 마스터하기: 인터페이스와 구현 분리로 설계 단순화
데이터 추상화(data abstraction)란 외부 세계에 필수 정보만 제공하고 배경 세부 사항을 숨기는 것, 즉 프로그램에서 필요한 정보를 표현하되 세부 사항은 제시하지 않는 것을 말합니다.
데이터 추상화는 인터페이스와 구현의 분리에 의존하는 프로그래밍(및 설계) 기법입니다.
현실 세계의 TV 예를 들어보세요. TV를 켜고 끄고, 채널을 바꾸고, 볼륨을 조절하고, 스피커, VCR, DVD 플레이어 같은 외부 부품을 추가할 수 있습니다. 하지만 내부 세부 사항, 즉 어떻게 공중파나 케이블로 신호를 수신하고, 어떻게 변환하며, 최종적으로 화면에 표시하는지는 알 필요가 없습니다.
따라서 TV는 내부 구현을 외부 인터페이스와 명확히 분리하며, 전원 버튼, 채널 변경기, 볼륨 조절 같은 인터페이스로 내부 작동 원리를 몰라도 조작할 수 있습니다.
C++에서 클래스는 훌륭한 수준의 데이터 추상화를 제공합니다. 객체 기능을 조작하고 객체 데이터(상태)를 조작하기 위한 충분한 public 메서드를 외부 세계에 제공하며, 클래스가 내부적으로 어떻게 구현되었는지 알 필요 없게 합니다.
예를 들어, 프로그램이 sort() 함수를 호출할 때 해당 함수가 주어진 값들을 정렬하는 데 실제로 어떤 알고리즘을 사용하는지 알 필요가 없습니다. 사실 정렬 기능의 내부 구현은 라이브러리 릴리스 사이에 변경될 수 있지만, 인터페이스가 동일하게 유지되는 한 함수 호출은 여전히 작동합니다.
C++에서는 클래스를 사용해 자체 추상 데이터 타입(ADT)을 정의합니다. ostream 클래스의 cout 객체를 사용해 표준 출력으로 데이터를 스트리밍할 수 있습니다.
Live Demo
#include <iostream>
using namespace std;
int main() {
cout << "Hello C++" << endl;
return 0;
}
여기서 cout이 사용자 화면에 텍스트를 어떻게 표시하는지 이해할 필요 없습니다. public 인터페이스만 알면 되고, 'cout'의 내부 구현은 자유롭게 변경될 수 있습니다.
접근 레이블이 추상화를 강제함
C++에서는 접근 레이블(access labels)을 사용해 클래스의 추상 인터페이스를 정의합니다. 클래스는 0개 이상의 접근 레이블을 포함할 수 있습니다.
접근 레이블이 나타날 수 있는 횟수에 제한은 없습니다. 각 접근 레이블은 뒤따르는 멤버 정의들의 접근 수준을 지정합니다. 지정된 접근 수준은 다음 접근 레이블이 나타나거나 클래스 본문의 닫는 중괄호가 나올 때까지 유효합니다.
데이터 추상화의 이점
데이터 추상화는 두 가지 중요한 이점을 제공합니다.
클래스 내부가 의도치 않은 사용자 수준 오류로부터 보호되어 객체 상태가 손상되는 것을 방지합니다.
클래스 구현은 변경되는 요구사항이나 버그 리포트에 대응해 시간이 지남에 따라 진화할 수 있으며, 사용자 수준 코드 변경 없이도 가능합니다.
데이터 멤버를 클래스의 private 섹션에만 정의함으로써, 클래스 작성자는 데이터 변경을 자유롭게 할 수 있습니다. 구현이 변경되면 클래스 코드만 검토해 변경의 영향을 확인하면 됩니다. 데이터가 public이면 이전 표현의 데이터 멤버에 직접 접근하는 모든 함수가 깨질 수 있습니다.
데이터 추상화 예제
public과 private 멤버를 가진 클래스를 구현하는 모든 C++ 프로그램은 데이터 추상화의 예입니다. 다음 예제를 고려해 보세요.
Live Demo
#include <iostream>
using namespace std;
class Adder {
public:
// 생성자
Adder(int i = 0) { total = i; }
// 외부 세계와의 인터페이스
void addNum(int number) { total += number; }
// 외부 세계와의 인터페이스
int getTotal() { return total; }
private:
// 외부 세계로부터 숨겨진 데이터
int total;
};
int main() {
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() << endl;
return 0;
}
위 코드를 컴파일하고 실행하면:
Total 60
위 클래스는 숫자들을 더해 합계를 반환합니다. public 멤버인 addNum과 getTotal은 외부 세계와의 인터페이스이며, 사용자는 이들을 알아야 클래스를 사용할 수 있습니다. private 멤버 total은 사용자가 알 필요 없지만 클래스가 제대로 동작하는 데 필요합니다.
설계 전략
추상화는 코드를 인터페이스와 구현으로 분리합니다. 따라서 컴포넌트 설계 시 인터페이스를 구현으로부터 독립적으로 유지해야 하며, 그러면 내부 구현을 변경해도 인터페이스는 그대로 유지됩니다.
이 경우 이러한 인터페이스를 사용하는 모든 프로그램은 영향을 받지 않으며, 최신 구현으로 재컴파일만 하면 됩니다.