본문 바로가기
IT 공부/객체지향 설계 공부

추상 데이터 타입 vs 객체지향의 객체

by exdus3156 2023. 12. 8.

( ※ 출처 - <오브젝트> by 조영호 챕터7 )

 

1. 추상 데이터 타입

추상 데이터 타입(Abstract Data Type)은 개발자가 필요로 하는 데이터 타입을 추상적으로 표현하고 이것과 관련된 연산을 구현한 새로운 데이터 타입을 말한다.

본래 타입(type)이란, 어떤 값에 대한 의미와 함께 그것을 가지고 할 수 있는 연산이 무엇인지 제공한다. 따라서 추상 데이터 타입(ADT)이라고 말했으나, 프로그래밍 언어에서 제공해주는 기본 데이터 타입도 같은 데이터 타입이다. 단지 추상 데이터 타입은 개발자가 직접 정의할 수 있다는 점에서 다를 뿐이다.

ADT는 좋은 프로그램의 밑거름이 된다. 절차적(Procedural) 프로그래밍이 프로시저(함수)를 중심으로 원하는 기능을 분해하고 통합하는 식이라면, ADT는 개념 즉, 데이터를 기반으로 소프트웨어를 개발하게 만든다. 전자가 알고리즘의 제어 흐름부터 읽게 만든다면, 후자는 개념을 중심으로 코드를 읽게 만드는 시초다.

추상 데이터 타입의 추상(abstract)은 데이터 타입에 대한 추상화를 통해 내부 구현에 신경쓰지 않도록 한다는 의미를 담고 있다. ADT의 사용자는 오퍼레이션만 알면 된다. 내부 구현 사항에 대해서는 몰라도 된다. 

절차적 프로그래밍이 기능을 분해하고 정리하는데는 유용하지만, 기능 구현을 위해 사용해야 하는 데이터를 외부로부터 공급받아야 한다(인자, 전역변수)는 점에서 모듈로서의 가치가 없다는 것을 생각해보자. ADT는 필수적인 데이터를 내부로 숨겨준다.

 

 

2. ADT vs 객체

2-1. ADT는 여전히 기능과 데이터를 분리한다.

ADT는 그 이름에서도 알 수 있듯이, 여전히 데이터다! ADT는 데이터를 중심축에 놓고 설계되기 때문에 자연스럽게 외부에 제공하는 오퍼레이션은 데이터를 단순 조작하는 연산을 제공하기 마련이다. 즉, 해당 ADT를 통해 구체적으로 어떤 기능을 구현하고 싶다면 따로 다른 모듈에서 프로세스를 구현해야 한다.

따라서 ADT는 비록 데이터와 기능을 한데 묶어주는 문법을 사용하지만 안타깝게도 여전히 진정한 능력(데이터를 사용해 어떤 기능을 구현하는 것)은 ADT를 '사용'하는 외부 객체가 떠맡는 식이다.

애초에 추상 데이터 타입의 본래 의도 자체가 프로그래밍 언어가 제공하는 타입처럼 동작하는 사용자 정의 타입을 추가하게 하는 것이었다.(리스코프) ADT는 데이터 타입일 뿐, 실질적으로 그 데이터를 가지고 기능을 구현하는 것은 ADT 내부가 아니라 외부에 있다.

 

2-2. ADT와 객체 구분이 혼란스러운 이유

객체지향에서의 객체와 ADT는 서로 다른 개념이다. 다만 설계 상에서 그 출발이 같다는 점이 ADT와 객체의 차이에 대한 혼동을 일으키는 것이다. 절차적 프로그래밍이 제어 흐름을 통해 기능을 분해한다면, ADT와 객체는 모두 개념(데이터)를 중심으로 시스템을 분할한다.

예를 들어, 직원 급여 관리 시스템을 개발해야 한다면 절차적 프로그래밍 관점에서는 main 함수에서 시작해 필요한 기능을 상상해보며 쪼개나간다. 그리고 각 기능을 구현하기 위해 필요한 데이터의 종류와 구조를 설계한다.

반대로, 추상 데이터 타입과 객체는 기능이 아니라 도메인의 개념을 식별하고 분해한다. "직원", "급여"가 그렇다. 두 관점 모두 "직원" 이라는 개념을 토대로 ADT나 객체를 설계한다. 그러나 ADT와 객체의 공통점은 여기까지다.

 

2-3. 타입 추상화(ADT) vs 절차 추상화(객체)

흔히 객체와 ADT의 차이점을 "상속과 다형성 기능 제공" 여부에 따라 구분한다. 틀린 설명은 아니다. 이 설명을 통해 우리는 객체의 구현 문법인 클래스(class)가 ADT 구현 문법이 아니라는 사실을 추론해낼 수 있다.

하지만 여기에는 보다 더 본질적인 원리가 숨어 있다. ADT와 객체는 추상화의 대상이 다르다. 여기서 추상화란, 무엇을 기준으로 통합하는가와 관련이 있다.

 

ADT는 타입 추상화다. 즉, 타입을 추상화한다. 따라서 타입이 하나로 통합된다.

ADT는 서로 다른 타입을 하나로 묶어 버린다. 예를 들어, 정규직 직원과 아르바이트 직원은 서로 다른 종류의 타입이다. ADT는 이 둘을 하나로 묶어 하나의 "직원" 타입으로 통합한다. 서로 다른 타입을 녹여 버렸다고 보면 된다.

 

객체는 절차 추상화다. 즉, 절차를 추상화한다. 따라서 절차가 하나로 통합된다.

객체는 ADT와 달리 타입을 추상화하지 않는다. 즉, 타입을 통합하지 않는다는 말이다. 따라서 정규직 직원과 아르바이트 직원은 개별적인 타입으로 존재한다. 클래스 구현 문법으로 설명하자면 SalariedEmployee와 HourlyEmployee 클래스가 코딩된다는 것이다.

객체는 절차 추상화다. 추상화의 대상이 절차이기 때문에 절차가 통합된다. 여기서 상속 구현 문법이 나온다. 즉, 서로 다른 타입의 두 직원을 하나의 절차로 통합한다. 여기서 같은 절차(메소드)를 공유하는 Employee 객체가 있고 다른 두 직원 객체는 Employee 객체를 상속한다.

 

2-4. 언제 무엇을 써야 하는가?

변경의 방향에 따라 다르다. 변경의 방향이 새로운 타입 추가라면 객체가 낫고, 새로운 절차 추가라면 ADT가 낫다.

ADT는 타입이 하나로 통합된다. 따라서 변경의 방향이 새로운 타입이라면 새로운 타입과 관련된 로직을 ADT 내부에 원래 있던 코드와 통합해야 한다. 이것은 매우 번거로운 작업이다.

하지만 절차는 추가하기 쉽다. ADT를 찾아서 새로운 절차를 넣기만 하면 된다. 타입이 하나로 추상화되었기 때문에 고칠 대상은 하나밖에 없다. 따라서 시스템에서 여러 개의 타입이 존재할 여지가 없고, 단순한 데이터로 취급될 대상이라면 ADT가 낫다.

객체는 절차가 하나로 통합된다. 다른 타입들은 이것을 상속한다. 따라서 변경의 방향이 새로운 절차 추가라면 객체는 변경하기 까다롭다. 왜냐하면 부모 클래스에 절차를 추가하면 그것을 상속하는 다른 모든 타입에 그대로 영향을 주기 때문이다.

반대로 타입은 추가하기 정말 쉽다. 그저 부모클래스를 상속한 자식 클래스를 만들면 된다. 기존의 부모 클래스나 다른 자식 클래스들과는 아무런 영향도 주고받지 않는다. 이것은 OCP 원칙으로 이어진다.