본문 바로가기
SW core/객체지향

객체지향의 사실과 오해 - 타입과 추상화

by ocwokocw 2021. 8. 12.

- 이 글은 조영호의 객체지향의 사실과 오해를 기반으로 작성되었습니다. (가능하면 꼭 읽어보는것을 추천드립니다.)

- Javascript 프로토타입 상속: https://developer.mozilla.org/ko/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

 

- 추상화와 복잡성

초기의 지하철 노선도는 실제 지형을 참조한 곡선의 경로와 역간의 위치 거리 등의 정보를 충실하게 반영했었다. 하지만 현재 노선도는 직선 형태이며 역간의 거리도 실제 거리와는 무관하다. 그럼에도 목적지까지 이동하는데 불편함이 없다.

추상화의 본질은 사용자의 목적을 달성할 수만 있다면 복잡한 정보를 제거함으로써 본질을 드러내게 하는것이다. 복잡성을 다루기 위해 추상화는 두 가지 원칙을 지닌다.

  1. 구체적 사물들의 공통점을 취하고 차이점을 버리는 일반화를 통한 단순화.
  2. 중요한 부분을 강조하기 위해 불필요한 세부사항 제거

- 객체지향과 추상화

친구들끼리 카드놀이나 화투를 해본적이 있을것이다. 트럼프 카드에는 1~10, J, Q, K, A 숫자와 4 가지 무늬가 있으며 화투도 많은 그림이 존재한다. 만약 우리가 대화속에서 이 카드나 화투를 언급할 때 마다 '트럼프 카드', '화투' 라고 부르지 않고, 모든 개별적인 카드나 화투패들을 일일히 나열한다면 대화는 상당히 지루하고 복잡해질것이다.

 

카드 나 화투패 한 장을 객체로 본다면 우리는 공통점을 기반으로 '트럼프 카드' 와 '화투' 로 객체들을 묶을 수 있다. 이렇게 묶는 것을 개념(concept) 이라고 한다. 개념을 이용하면 카드나 화투패 한 장(객체)을 트럼프 카드나 화투 그룹으로 나눌 수 있다. 이를 분류(classification) 라고 한다. 스페이드 A 카드는 트럼프카드 라는 개념의 일원이다. 이때 스페이드 A 카드는 트럼프카드 개념의 인스턴스(instance) 이다.

 

개념에는 3 가지 관점이 있다. 심볼(symbol)은 개념을 가리키는 이름이나 명칭이며 위의 예제에서는 '트럼프 카드' 이다. 내연(intension)은 객체가 개념에 속하는지 여부를 판단할 수 있는 정의인데, '4 가지 무늬와 숫자로 혼합된 카드'라고 할 수 있다. 외연(extension)은 개념에 속한 집합인데 '클로버1, 클로버2, ..... 스페이드A' 처럼 원소들의 나열이다. 중요한것은 이런 용어를 정확하게 외우는것이 아니라 원소들을 개념에 따라 분류할 수 있다는것을 아는 것이 중요하다.

 


- 타입

타입은 개념과 완전히 동일하다. 어떤 객체가 특정 타입에 속한 다면 해당 객체는 그 타입의 인스턴스이다. 타입은 데이터가 어떻게 되느냐에 관한것이다. 숫자형 데이터에는 사칙연산을 수행할 수 있으며 문자형 데이터는 두 문자열을 연결하며 길이를 알 수 있다. 여기서 중요한것은 타입마다 어떤 연산자를 사용할 수 있느냐를 외우는것이 아니라 적용되는 연산자에 따라 데이터 타입이 결정된다는 사실이다.

 

객체도 데이터타입과 유사한 점이 많지만 데이터는 아니다. 앞의 글에서 객체의 상태보다는 행동을 우선시해야 한다고 언급했었다. 상태는 객체가 행동했던 이력에 대한 일종의 캐시일뿐이다. 따라서 객체들이 동일한 타입에 속하는지를 결정하는것은 상태가 아니라 동일한 행동을 하고 있는지의 여부이다. 또한 객체의 상태는 행동만 잘 수행할 수 있다면 어떠한 형태를 취하던 상관없으며 외부에 드러낼 필요도 없다.

 

동일한 행동을 한다는것은 객체가 동일한 메시지를 수신한다는 말이 되고 이를 코드로 구현하면 2 개 이상의 클래스가 하나의 인터페이스를 구현하는 형태가 된다. 인터페이스를 참조하면 상황에 따라 이를 구현하는 클래스 참조를 변경할 수 있다. 이것을 세글자로 다형성이라고 한다. 행동이 상태보다 중요하므로 상태를 특별한 이유없이 굳이 외부에 노출시킬 필요는 없는데 이렇게 잘 감추는것을 캡슐화라고 부른다.


- 타입의 계층

포유류는 많은 특징이 있지만 젖먹이 동물이라는것이 가장 큰 특징이다. 그 하위 개념인 개는 포유류처럼 젖을 먹이는 행동에 더불어 물거나 짖기도 한다. 많은 책들이 일반화와 특수화를 설명할 때 포유류나 개와 같은 예시에서 집합의 크기로 설명한다. 하지만 이 책에서는 행동이라는 측면에서 일반화와 특수화를 설명한다.

 

개는 젖을 먹이는 행동을 하는 포유류가 하는 행동도 하면서 짖거나 무는 등 특수한 행동을 추가적으로 수행한다. 이를 특수화라고 하며 일반화는 특수화의 역이다. 해당 타입이 가지는 행동의 수가 많으면 많을 수록 외연의 크기(집합의 크기)는 줄어든다. 따라서 행동의 가짓수와 외연의 크기는 반비례 관계에 있다. 행동을 많이 하는 타입일수록 해당 타입에 속하는 객체들이 점점 줄어들어 특수해진다.

 

행동의 수가 별로 없어서 좀 더 일반적인 타입을 슈퍼타입이라고 하고 특수한 타입을 서브타입이라고 한다. "어떤 타입의 서브 타입이다"라고 말할 수 있으려면 치환을 해도 동작(행위)에 문제가 없어야 한다. SOLID 원칙을 본적이 있다면 치환이라는 단어를 본 순간 스쳐가는 원칙이 있을것이다. 이것이 바로 유명한 리스코프 치환 원칙(LSP)이다. 리스코프 치환 원칙의 유명한 위배 문제인 정사각형/직사각형 문제도 서브타입인 정사각형이 한 변의 길이를 변경했을 때 슈퍼타입인 직사각형의 "넓이를 구하는 행위"를 만족시키지 못했기 때문이다. 자세하게 알아보고 싶다https://ocwokocw.tistory.com/32?category=844458 를 참조하거나 클린 아키텍처의 LSP 장을 참조하면 된다.


- 정적 모델과 동적 모델

만약 프로그래밍을 하면서 클래스가 생성한 인스턴스들이 실제로 동작하는 모습을 모두 생각하면서 프로그래밍을 해야한다면 때려칠사람이 많을 것이다. 인간의 뇌는 한계가 있기 때문이다. 물론 객체들이 어떻게 행동할지를 생각해야 하는 상황이 있지만 구현 초기에는 타입이 어떻게 행동할지를 생각한다. 타입의 목적은 추상화에 있으며 특정 요소를 배제하여 한계가 있는 인간이 더 쉽게 S/W 를 제작할 수 있도록 돕는다.

 

객체를 바라보는 시각에는 동적인 모델과 정적인 모델이 존재한다.

동적모델의 예로는 객체들의 특정 상태를 표현하는 다이어그램인 객체 다이어그램이 있는데 이것이 바로 동적모델의 예라고 할 수 있다. (https://ocwokocw.tistory.com/21?category=844459) 

 

동적 모델의 반대인 정적 모델은 시간에 따라 영향을 받는 상태와 행위를 시간이라는 요소를 배제하고 표현한것이다.


- 클래스

Java 에서는 타입을 구현할 때 보통 클래스를 이용한다. 일반적으로 타입을 클래스라고 하지만 엄밀히 말하면 클래스는 타입을 구현하는 매커니즘일뿐이다. 자바스크립트는 클래스를 지원하지만 이는 문법적인 양념일뿐이며 자바스크립트에서 상속의 기반은 프로토타입이다. (https://developer.mozilla.org/ko/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)

또한 클래스는 타입과 상관없이 재사용을 위한 용도로도 사용될 수 있기 때문에 클래스 = 타입 이라는 오해에서 벗어나도록 하자.

댓글