객체지향에서 추상화를 이용하면 복잡성을 극복할 수 있으며, 유용한 애플리케이션을 개발하기 위해서는 아래 추상화의 두 차원을 올바르게 이해해야 합니다.
개념
공통점을 기반으로 객체들을 그룹으로 묶기위한 아이디어나 관념을 개념(concept)라고 합니다. 개념을 이용하면 객체를 여러 그룹으로 분류(classification)할 수 있습니다.
결국 객체란, 특정한 개념을 표현하는 그룹의 일원이며 이때 객체를 그 개념의 인스턴스(instance)라고 부릅니다.
개념의 세 가지 관점
개념은 심볼, 내연, 외연으로 구성되어 있습니다.
중요한 것은 개념의 구성 요소보다, 개념을 이용해 공통점을 가진 객체를 분류할 수 있다는 사실입니다. 이는 복잡성을 극복하는 가장 기본적인 수단이기 때문입니다.
객체를 분류하기 위한 틀
분류란 객체에 특정한 개념을 적용할것인지를 결정하는 작업입니다. 어떤 객체를 어떤 개념으로 분류하는지가 객체지향의 품질을 결정합니다. 객체가 적절하게 분류되지 못한 애플리케이션은 유지보수가 어렵고, 변화에 대처하기가 어렵습니다.
분류는 추상화를 위한 도구다
개념을 이용해 객체를 분류하는 과정에서는, 추상화의 일반화와 단순화를 모두 사용합니다. 이를 통해 우리는 복잡한 세상을 그나마 단순화할 수 있습니다.
객체와 타입
타입(type)의 정의는 개념의 정의와 완전히 동일합니다. 우리는 프로그램을 작성할 때 객체를 타입에 따라 분류하고, 그 타입에 이름을 붙입니다. 이는 결국 프로그램에 사용할 새로운 데이터 타입을 선언하는 것과 같습니다.
이 때, 항상 아래 두 가지 조언을 명심해야 합니다.
행동이 우선이다
객체의 타입을 결정하는 것은 객체의 행동뿐이며, 객체가 어떤 데이터를 갖고 있는지는 타입을 결정하는 데 아무련 영향을 미치지 않습니다. 동일한 타입의 객체는 내부 데이터 표현 방식이 다르더라도 동일한 행동을 수행할 수 있으며, 이 때 행동을 수행하는 방식은 서로 다를 수 밖에 없습니다. 이것이 다형성에 의미를 부여합니다.
내부 데이터 표현 방식과 무관하게 행동만이 중요하다는 것은, 외부에 객체의 데이터를 감춰야 한다는 것을 의미합니다. 즉, 훌륭한 객체지향 설계는 공용 인터페이스로 행동만을 제공하고 데이터는 뒤로 감춰야 합니다. 이 원칙을 우리는 캡슐화라고 합니다.
일반화(generalization)/특수화(specialization) 관계
타입과 타입 사이에는 일반화/특수화 관계가 존재할 수 있습니다. 이 관계에 중요한 것은 역시나 데이터가 아니라 행동입니다. 한 타입은 더 일반적으로 행동해야 하며, 다른 타입은 일반적인 타입의 행동을 모두 포함하고 거기에 자신만은 특수한 행동을 가져야 합니다.
슈퍼타입(Supertype)과 서브타입(Subtype)
일반화/특수화 관계에서 좀 더 일반적인 타입을 슈퍼타입이라 하고, 좀 더 특수한 타입을 서브타입이라고 합니다. 서브타입은 슈퍼타입의 행동을 상속하므로, 슈퍼타입을 대체할 수 있어야 합니다.
일반화는 추상화를 위한 도구다
일반화/특수화 기법에는 추상화의 일반화와 단순화가 모두 사용됩니다.
타입의 목적
타입은 시간에 따라 동적으로 변하는 상태를 시간과 무관한 정적 모델로 다룰 수 있게 해줍니다. 이런 관점에서, 타입은 결국 추상화입니다. 타입을 이용하면 객체의 동적인 특성을 추상화할 수 있고, 이는 상태 변경이라는 복잡성을 단순화해줍니다.
동적 모델과 정적 모델
우리가 코드를 작성하며 클래스를 구현하는 시점에는 시스템을 정적인 관점에서 접근하는 것입니다. 하지만 실제 작동하고 있는 애플리케이션에서 상태 변경을 추적하며 디버깅하는 과정은, 동적인 모델을 접근하는 것입니다. 따라서 훌륭한 객체지향 프로그래머가 되기 위해서는 동적 모델과 정적 모델을 모두 다뤄야 합니다.
클래스
많은 사람들이 클래스와 타입을 동일시합니다. 하지만 클래스는 타입의 구현하기 위한 여러 방법 중 하나일 뿐이며, 클래스는 타입의 구현 용도 외에도 코드를 재사용하는 용도로 사용되기도 합니다. 따라서 클래스와 타입을 혼동해서는 안되며, 클래스는 타입을 구현하기 위한 방법 중 하나일 뿐이라는 것을 기억해야 합니다.