객체지향의 사실과 오해/1 - 객체

객체지향의 사실과 오해

객체지향의 사실과 오해를 읽고 정리한 내용입니다. 문제가 될 경우에는 삭제하겠습니다.

객체

객체란?

객체는 식별 가능한 개체 또는 사물이다. 객체는 자동차처럼 만질 수 있는 구체적인 사물일 수도 있고, 시간처럼 추상적인 개념일 수도 있다. 객체는 구별 가능한 식별자, 특징적인 행동, 변경 가능한 상태를 가진다.

여기서 설명하는 객체는 다음과 같이 정리할 수 있다.

  • 객체는 식별가능한 개체 또는 사물이다.
  • 식별자, 행동, 변경 가능한 상태를 가진다.
  • 식별자를 통해 유일하게 식별 가능하다.
상태

상태를 알면, 과거에 발생한 이력이나 행동들에 대한 기억을 할 필요가 없다. 단지 그 당시의 상태만을 가져와 비교를 하면 되는 것이다. 그리고 이런 상태는 객체의 행동결과에 영향을 미친다. 상태가 어떤지에 따라 특정 행동을 할 경우에 그 결과가 달라지기 때문이다.

행동

행동이란, 외부의 요청 또는 수신된 메시지에 응답하기 위해 동작하고 반응하는 활동이다. 행동의 결과로 객체는 자신의 상태를 변경하거나 다른 객체에게 메시지를 전달할 수 있다. 객체는 행동을 통해 다른 객체와의 협력에 참여하므로 행동을 외부에 가시적이어야 한다.

행동은 객체의 상태를 변화시킨다. 하지만 위에서 말했듯이, 객체의 상태에 의존적이다.

협력

객체는 자신에게 주어진 책임을 다하기 위해, 다른 객체와 메시지를 주고 받으며 협력한다. 어떠한 객체도 혼자서 스스로 그 책임을 다할수는 없다. 그리고 이렇게 메시지를 주고받는 과정에서 객체는 자기 자신뿐만 아니라 다른 객체의 상태도 변화시킬 수 있다.


객체지향의 본질

  • 객체지향이란 시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분할하는 방법이다.
  • 자율젹인 객체란 상태행위를 함께 지니며 스스로 자기 자신을 책임지는 객체를 의미한다.
  • 객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다. 각 객체는 협력 내에서 정해진 역할을 수행하며 역할은 관련된 책임의 집합이다.
  • 객체는 다른 객체와 협력하기 위해 메세지를 전송하고, 메세지를 수신한 객체는 메세지를 처리하는 데 적합한 메소드를 자율적으로 선택한다.

객체 지향에서 중점을 두어아하는 것은 클래스가 아니라 객체이다. 물론 클래스도 중요하기는 하지만 지나치게 클래스를 강조하는 프로그래밍 언어적인 관점은 객체의 캡슐화를 저해하고 클래스를 서로 강하게 결합시킨다. 그리고 이는 유연하고 확장 가능한 어플리케이션의 구축을 방해한다.

따라서, 훌륭한 객체지향 설계자가 되기 위해서는 클래스 중심의 설계보다는 객체들 간의 어떠한 메세지를 전달하고 협력하는지에 더 초점을 맞추어야 한다.


객체 지향 패러다임의 목적

객체 지향 패러다임의 목적은 현실세계를 모방하는 것이 아니라 현실 세계를 기반으로 새로운 세계를 창조하는 것.


객체를 상태가 아닌 행동에 초점을 맞춰라

행동이 상태를 결정한다. 상태를 먼저 결정하고 행동을 나중에 결정하는 경우의 단점은 아래와 같다.

  • 캡슐화가 저해된다. 상태가 객체 내부로 깔끔하게 캡슐화되지 못하고 공용인터페이스에 그대로 노출되버릴 확률이 크다.
  • 객체를 협력자가 아닌 고립된 섬으로 만든다. 협력에 중심을 두지않고 객체 상태에 중심을 두고 만들 경우, 협력을 고려한 설계와 멀어질 가능성이 크다.
  • 객체의 재사용성이 저해된다.

객체는 다른 객체와 협력하기 위해 존재한다. 하나의 객체는 행동을 통해 다른 객체에 영향을 미친다. 그렇기 때문에 다른 객체와 협력하기 위해서는 객체의 행동에 초점을 맞추어 설계하는 것이 바람직하다.

  • 행동에 초점을 먼저 맞추고 필요한 상태를 정의한다.
  • 객체의 행동을 책임을 의미한다. 이를 통해 책임주도 설계를 수행할 수 있다. (RDD - Responsiblilty-Driven Design)
  • 책임주도 설계는 응집도 높고 재사용성이 높은 객체를 만들 수 있다.



추상화

추상화는 어떤 양상, 세부사항, 구조를 좀 더 명확하게 이해하기 위해 특정 절차나 물체를 의도적으로 생략하거나 감춤으로써 복잡도를 극복하는 방법이다.

  • 구체적인 사물들 간의 공통점은 취하고 차이점은 버리는 일반화를 통해 단순하게 만드는 것이다.
  • 중요한 부분을 강조하기 위해 불필요한 세부사항을 제거함으로써 단순하게 만드는 것이다.
해리 핵의 지하철 노선도

목적을 위해 정확성을 포기. 추상적이게 되면 목적에 의존적이게 된다.

추상화의 세 가지 관점
  • 심볼 : 개념을 가리키는 간략한 이름이나 명칭.
    • (트럼프)
  • 내연 : 개념의 완전한 정의를 나타내며 내연의 의미를 이용해 객체가 개념에 속하는지 여부를 확인할 수 있다.
    • (몸이 납작하고 두 손과 두 발은 네모 귀퉁이에 달려있는 등장인물)
  • 외연 : 개념에 속하는 모든 객체의 집합.
    • (정원사, 병사, 신하, 왕자와 공주, 하객으로 참석한 왕과 왕비들 등)

객체를 개념에 따라 적절하게 분리할 경우, 유지보수가 쉬워지고 변경에 유연하게 대처할 수 잇다.


Type

타입은 개념과 동일하다. 따라서 타입이란, 우리가 인식하고 있는 다양한 사물이나 객체에 적용할 수 있는 아이디어나 관념을 의미한다. 어떤 객체에 타입을 적용할 수 있을 때 그 객체를 타입의 인스턴스라고 한다. 타입의 인스턴스는 타입을 구성하는 외연인 객체 집합의 일원이 된다.

타입은 개념이다. 타입은 공통점을 기반으로 객체들을 묶기 위한 틀이다. 컴퓨터 안에 데이터를 목적에 맞게 분리하면서 타입시스템이 생기게 되었는데, 이 타입 시스템은 메모리에 저장된 0과 1에 대해 수행 가능한 작업과 불가능한 작업을 구분함으로써 데이터가 잘못 사용되는 것을 방지한다.

  • 데이터가 어떻게 사용되느냐에 관한 것을 말한다. 쉽게 말하면 데이터에 어떠한 연산자를 사용할 수 있느냐에 따라 데이터의 타입이 정해진다.
  • 타입에 속한 데이터를 메모리에 어떻게 표현하는지는 외부로부터 철저하게 감춰진다.
행동이 객체의 타입을 결정한다.

객체의 내부 표현 방식이 다르더라도 동일한 행동을 한다면 동일한 타입에 속한다고 할 수 있다. 여기서 내부 표현 방식이 다르다는 것은 다형성에 의미를 부여한다고 할 수 있다. 동일한 메시지를 받는다고 하더라고 내부 표현방식이 다르기 때문에 처리하는 방식 또한 다르다. 행동에 따라 객체를 분류하는 과정은 아래와 같다.

  1. 객체가 외부에 제공해야 하는 책임을 먼저 결정한다.
  2. 책임을 수행하는 데 필요한 데이터를 결정한다.
  3. 데이터를 책임을 수행하는 데 필요한 외부 인터페이스 뒤로 캡슐화한다.

훌륭한 객체지향 설계는 외부에는 행동만을 제공하고 데이터는 행동 뒤로 감춰야 한다. 이를 캡슐화라고 한다.


협력

객체의 모양을 결정하는 것은 객체의 행동이나 상태가 아니라 어떠한 식으로 다른 객체와 협력하는지다. 객체가 협력하는 방식에 따라 행동이 결정되고 이러한 행동으로 인해 상태가 결정된다.

협력은 한 사람이 다른 사람에게 도움을 요청할 때 시작된다. 요청을 받은 사람은 요청한 사람에게 필요한 지식이나 서비스를 제공하는 방식으로 응답한다. 이렇게 응답하는 사람도 또 다른 사람의 도움이 필요할 수 있다. 그렇기 때문에 협력은 다수의 연쇄적인 요청과 응답의 흐름으로 구성된다.

이렇게 요청을 보내는 이유는 요청을 받는 사람이 해당 요청을 처리할 수 있는 지식과 행동 방식을 가지고있기 때문이다. 이는 또한, 요청을 받는 사람이 해당요청을 처리해야 할 책임이 있다고 생각할 수 있다. 여기서의 요청은 객체지향의 세계에서는 메세지라고 한다.

객체지향 개발에서 가장 중요한 능력은 책임을 능숙하게 소프트웨어 객체에 할당하는 것. - Craig Larman

책임의 분류

책임은 객체가 무엇을 알고있는가와 무엇을 할 수 있는가로 구성된다.

  • 무엇을 할 수 있는가
    • 객체를 생성하거나 계산을 하는 등의 스스로 하는 것
    • 다른 객체의 행동을 시작시키는 것
    • 다른 객체의 활동을 제어하고 조절하는 것
  • 무엇을 알고 있는가
    • 개인적인 정보에 관해 아는 것
    • 관련된 객체에 관해 아는 것
    • 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것

책임을 수행하도록 요청을 하는 사람은 송신자, 이런 요청을 받아 책임을 수행하는 사람을 수신자라고 한다. 이런 송신자와 수신자 사이에 오가는 요청은 메시지를 통해 이루어지는데, 송신자가 수신자에게 메시지를 보낼 때에는 수신자가 이를 수신할 수 있다는 믿음을 가지고 보낸다.

역할

위에서 언급한 책임은 어떻게 결정되는가? 책임을 그 객체가 어떠한 역할을 하고있는지에 따라 결정된다. 상담원을 수신자라고 하고, 그 상담원에게 민원이나 문의를 하는 고객을 송신자라고 하자. 이 경우에 상담원은 고객이 요청하는 문의나 민원을 처리해야 할 책임이 있다. 그리고 이러한 책임은 상담원이 상담원이라는 역할을 하고 있기 때문에 수행해야하는 행동이다.

그렇다면, 상담원이라는 역할은 지금 상담원을 하고있는 사람 한명만 할 수 있는 일인가? 그렇지는 않다. 김씨가 상담원을 하던 박씨가 상담원을 하던 고객의 입장에서는 상관이없다. 단지 상담원이라는 역할을 가지는 사람이 해주는 책임만이 중요한 것이다.

여기서 한 가지 중요한 점이 있다. 이러한 역할을 하나의 틀로 잡고 추상화시키면 어떨까? 그렇게 되면 어떠한 객체가 오더라도 해당 역할을 하고 역할이 해야하는 책임을 수행할 수 있다. 물론 여기에서 언급한 어떠한 객체는 메시지의 이해, 처리방식이 동일한 객체여야 한다. 그래야 객체가 달라지더라도 동일한 방식으로 메시지를 주고 받을 수 있기 때문이다.

협력의 추상화

역할의 가장 큰 가치는 하나의 협력 안에 여러 종류의 객체가 참여할 수 있게 함으로써 협력을 추상화할 수 있다는 것이다. 이는 설계자가 다루어야 하는 협력의 개체수를 줄이는 동시에 구체적인 객체를 추상적인 역할로 대체함으로써 협력의 양상을 단순화한다. 결과적으로 애플리케이션의 설계를 이해하고 기억하기가 쉽도록 한다.


객체의 설계과정

올바른 객체를 설계하기 위해서는 아래의 순서를 따라서 설계를 진행해야 한다.

  1. 견고하고 깔끔한 협력을 설계한다. 이는 설계에 참여하는 객체들이 주고받을 요청과 응답의 흐름을 결정한다는 것을 의미한다.
  2. 결정한 요청과 응답의 흐름을 통해 객체의 책임을 결정한다.
  3. 책임을 수행하는데 필요한 데이터를 결정한다.
  4. 클래스의 구현방법을 결정한다.