12 분 소요

조영호님의 책 객체지향적의 사실과 오해를 읽고 OOP에 대한 많은 영감을 받아 이를 정리한 글 입니다.


객체지향이란

객체지향은 과거 프로그램을 순차적인 명령어의 집합으로 바라보던 시각에서 벗어나 독립적인 객체들의 집합과 (메세지를 통한) 객체들의 상호 협력으로 바라보는 프로그래밍 페러다임입니다. 프로토타입 기반 언어인 Javascript를 제외한 현대의 거의 대부분의 프로그래밍 언어가 객체지향을 지원하고 있고, 이들 대부분은 class라는 것을 이용해 객체를 구현해냅니다. 이 때문에 객체지향을 class기반의 프로그래밍으로 오해하는 경우들이 종종 발생하는 것 같고 저 또한 이 둘의 차이를 깨닫게 된지 얼마 안되는 것 같습니다. 하지만 class는 객체의 구현일 뿐, 정말 중요한 것은 객체 그 자체를 바라보는 것입니다. 객체와 객체간의 협력에 대해 이해하는 것이 객체지향을 이해하는 것의 핵심이라고 할 수 있습니다.


객체란

객체는 독립적으로 인식 가능한 개체로서, 식별자와 상태 그리고 행동을 가지고 있는 실체입니다. 우리는 이 객체를 실행 가능한 코드로서 구현해 냅니다.

행동
객체지향 페러다임에서 프로그램은 서로 다른 역할과 책임을 가지고 있는 객체들이 협력을 통해 자신의 역할을 다하는 형태로 만들어집니다. 이때, 객체들의 협력은 기본적으로 요청과 응답으로 이루어지며, 요청과 응답은 객체들이 주고 받는 메세지가 됩니다. 특정 객체가 다른 객체들과 협력하기 위해, 자신의 책임을 다하기 위해 반드시 필요한 것이 행동입니다. 다른 객체에게 요청을 보내고, 수신 받은 요청을 수행해 응답하는 것 모두가 객체의 행동입니다. 이때, 객체는 행동을 통해 다른 객체들과의 협력에 참여하기 때문에 이를 위해 행동은 외부에 가시적이어야 합니다.

상태
객체의 행동은 때에 따라 달라지기도 합니다. 그리고 이 ‘때’를 결정하는 것이 바로 상태입니다. 상태는 객체가 가지고 있는 특정 시점에 대한 정보의 집합으로 정의내릴 수 있습니다.
객체는 모두 독립적이며 자율적인 존재입니다. 객체가 자율적이기 위해서는 자신이 가지고 있는 상태를 오직 본인만이 컨트롤 할 수 있도록 해야합니다. 만약 다른 객체가 상태에 간섭할 수 있게 된다면, 객체는 본인의 행동을 본인이 결정할 수 없는 수동적인 존재가 되는 것입니다. 따라서 상태는 외부로 부터 감추어져 있어야 합니다. 그리고 우리는 이를 캡슐화라고 부릅니다. 객체는 자신의 상태를 감추고 행동만을 외부에 노출 시키는 것입니다.

이 때, 객체가 취하는 행동은 여러가지 side effect를 야기시킬 수 있습니다. 행동은 상태의 영향을 받지만, 반대로 상태를 변경시키기도 하기 때문입니다. 따라서 객체의 행동은 *객체 자신의 상태 변경과 행동 내에서 협력하는 다른 객체에 대한 메세지 전송*이라는 side effect를 명확하게 서술하고 있어야 합니다.

식별자
객체의 상태는 가변적입니다. 따라서 서로 다른 객체의 상태가 동일해 질 수 있는데, 아무리 상태가 같다고 하더라도 객체는 독립적으로 존재하며 다루어 져야 하므로 객체의 동일성을 식별하기 위한 식별자가 필요합니다. 즉 식별자란, 상태에 상관 없이 객체를 유일하기 식별하기 위한 프로퍼티 입니다.

행동이 상태를 결정한다
객체지향적인 설계에서 주의할 점은 상태가 행동에 앞서지 않도록 하는 것입니다. 앞서 이야기 했듯이 객체지향 프로그래밍은 객체간의 협력으로 프로그램을 만드는 페러다임입니다. 따라서 협력을 먼저 생각해야 하며, 상태는 적절한 협력을 위해 따라오는 것일 뿐입니다. 만약 상태를 먼저 생각하게 되면 캡슐화를 저해할 수 있고, 객체를 협력으로 부터 멀어지게 만들 수 있으며, 협력에 참여가 어려워 짐에 따라 재사용성이 떨어질 수 있습니다.


타입

객체지향에 대한 설명을 찾아보면 ‘객체지향은 현실 세계를 소프트웨어 적으로 구현한 것’이라고 표현하는 것을 종종 볼 수 있습니다. 하지만 우리는 현실세계의 모든것을 구현해 내지 못합니다. 현실세계는 너무나 복잡하고 변수가 많기에 이를 모두 구현하는 것을 불가능에 가깝습니다. 따라서 우리는 이를 해결하기 위해 불필요한 세부사항들을 제거하는데 이를 추상화라고 합니다. 추상화는 어떤 양상, 세부 사항, 구조를 더욱 명확하게 이해하기 위해 특정 부분을 의도적으로 생략하거나 감춤으로써 복잡도를 극복하는 방법이라고 정의할 수 있습니다. 추상화는 두 차원에서 이루어 지는데, 1. 구체적인 사물들 간의 공통점은 취하고 차이점은 버리는 일반화를 통해 단순하게 만드는 것 2. 중요한 부분을 강조하기 위해 불필요한 세부 사항을 제거함으로써 단순하게 만드는 것 입니다.

공통점을 기반으로 객체들을 묶기 위한 그릇을 개념이라고 하며, 개념을 이용하면 객체를 여러 그룹으로 분류할 수 있습니다. 객체에는 이런 개념을 적용하는 것이 가능해서 객체가 개념 그룹의 일원이 될때, 이 객채를 개념의 인스턴스라고 합니다. 객체를 분류한다는 것은 객체를 특정한 개념 집합에 포함시키거나 포함시키지 않는 작업을 의미하게 되는데, 이 분류가 객체지향 설계의 품질을 결정하는 중요한 요소가 됩니다. 객체를 적절하게 분류해내지 못하면 유지보수가 어려워지고 변화에 대해 닫힌 구조가 되기 쉽습니다. 정리하자면, 객체를 특정 개념으로 분류하는 것은 추상화의 두 차원을 적용하는 것과 일맥상통한 이야기가 되므로, 개념은 추상화를 위한 도구라고 볼 수 있게 됩니다.

이미 눈치 채셨을지 모르겠지만 개념이라는 것에 대한 설명은 우리가 사용하는 타입과 무척이나 닮아있습니다. 실제로 타입은 개념이라는 것을 프로그래밍 영역으로 그대로 가져온 것일 뿐이기 때문에 정의 자체는 동일합니다. 다만, 타입은 데이터에 대한 의미부여라는 점에서 차이가 있을 뿐입니다. 그렇다면 우리는 객체의 타입을 어떻게 분류해야할까요? 객체가 어떤 타입에 속하는지를 결정하는 것은 객체의 행동입니다. 동일한 행동을 하는 객체가 있다면 동일한 타입으로 분류 될 수 있다는 것 입니다. 결국 가장 먼저 생각해야 하는 것은 객체의 행동입니다.

여기서 눈여겨 봐야할 점중 하나는 객체의 타입을 결정하는 요소에 상태는 빠져있다는 것입니다. 동일한 데이터를 가지고 있는 객체라도 행동이 다르면 서로 다른 타입으로 분류해야 하며, 데이터가 다르더라도 행동이 같으면 같은 타입으로 분류해야 합니다. 하지만 행동은 상태에 영향을 받습니다. 따라서 상태가 달라지면 동일한 메세지를 처리하는 방식이 달라질 수 있다는 것이고 우리는 이를 다형성이라고 부릅니다. 다형성은 동일한 요청에 다른 방식으로 응답할 수 있는 능력을 의미합니다.

타입에 대해 마지막으로 살펴볼 부분은 바로 ‘타입에는 계층구조가 있다’는 것입니다. 타입은 행동으로 분류됩니다. 동일한 행동을 하나의 타입으로 묶어 놨는데, 그들 중 일부가 또다시 공통된 행동을 보인다면 해당 타입을 특수화 하여 조금 더 일반적인 타입과 계층적인 관계를 만들 수 있을 것입니다. 여기서 일반적인 타입을 슈퍼타입 혹은 부모타입 이라고 하며, 특수화된 타입을 서브타입 혹은 자녀타입이라고 합니다. 그리고 이것이 바로 객체지향의 상속성입니다.


다시한번 객체지향이란

객체지향의 특성을 이야기 해보라 한다면 보통 다음의 네가지를 꼽습니다.

  1. 캡슐화
  2. 추상화
  3. 다형성
  4. 상속성

객체지향이 이런 특성을 가진다는 것을 알고는 있었지만, 두리뭉술 하게 이해하고 있었던 것을 객체에 대한 깊은 이해를 통해 각각의 특성이 어떤 의미를 가지고 있고 왜 이런 특성을 가지게 됐는지 조금 더 명확하게 이해할 수 있게 되는 것 같습니다. 요약하자면, 캡슐화는 협력에 필요한 행동만을 들어내고 상태를 감춤으로써 객체의 독립성과 자율성을 높이는 것, 추상화는 행동의 공통점을 취하고 차이점을 버림으로서 복잡도를 낮추고 공통된 행동을 보이는 객체를 동일한 타입으로 분류하는 것, 다형성은 행동이 같더라도 다른 방식으로 응답할 수 있는 능력, 상속성은 공통된 타입을 특수화 하여 계층된 분류를 상세화 하는 것 이라고 할 수 있을 것 같습니다.

다음 글에서는 어떻게 하면 객체지향적인 구조를 만들 수 있을지에 간단하게 이야기 해보도록 하겠습니다.



ref.

객체지향적의 사실과 오해