본문 바로가기

study/CS

[CS/책] 「오브젝트」2장 - 객체지향 프로그래밍

책 「오브젝트」 2장 객체지향 프로그래밍을 읽고 작성한 내용입니다.

 


객체지향은 객체를 지향하는 것이다.

객체지향은 말 그대로 객체를 지향하는 것이다. 진정한 객체지향 패러다임으로의 전환은 클래스가 아닌 객체에 초점을 맞출 때에만 얻을 수 있다.

첫째, 어떤 클래스가 필요한지를 고민하기 전에 어떤 객체들이 필요한지 고민하라.

둘째, 객체를 독립적인 존재가 아니라 기능을 구현하기 위해 협력하는 공동체의 일원으로 봐야 한다.

훌륭한 협력이 훌륭한 객체를 낳고 훌륭한 객체가 훌륭한 클래스를 낳는다.

 

객체지향 프로그램을 작성할 때 가장 먼저 떠오르는 것은 아무래도 클래스이다. 그러나 이러한 접근은 객체지향의 본질과는 거리가 멀다고 책에서 언급하고 있다.

 

현실세계의 요구사항에 기반하여 기능을 구현하려고 할 때, 객체가 아닌 클래스를 먼저 고려했었던 경험이 있다. 이러한 접근 방식에서는 클래스 내에 멤버 변수를 정의하고, 그 변수들을 활용하여 특정 기능을 수행하는 메서드를 구현하는 방법을 고민하는 방향으로 흘러갔다. 

그러나 객체지향에서 가장 중요한 것은 행동이다. 특정 기능을 구현하기 위해 객체에게 책임을 부여하고, 여러 객체가 협력하여 기능을 완성한다.

 

클래스가 아닌 객체에 초점을 맞추면 설계를 단순하고 깔끔하게 만들고, 유연하고 확장 가능하게 만든다고 책에서 언급한다.

이제는 클래스가 아닌 객체에 초점을 맞추며 설계하는 연습을 해야겠다.

 

이전에 읽었던 '객체지향의 사실과 오해'라는 책은 이러한 개념을 보다 쉽게 설명해 주어서, 오브젝트를 읽으면서 이해하는 데 도움이 되는 것 같다.

 

클래스의 경계를 구분 짓기

클래스를 구현하거나 다른 개발자에 의해 개발된 클래스를 사용할 때 가장 중요한 것은 클래스의 경계를 구분 짓는 것이다.
클래스는 내부와 외부로 구분되며 훌륭한 클래스를 설계하기 위한 핵심은 어떤 부분은 외부에 공개하고 어떤 부분을 감출지를 결정하는 것이다.

그 이유는 경계의 명확성객체의 자율성을 보장하기 때문이다. 그리고 더 중요한 이유로 프로그래머에게 구현의 자유를 제공하기 때문이다.

 

객체지향은 객체라는 단위 안에서 데이터(상태)와 기능(행동)을 한 덩어리로 묶는다. 이를 캡슐화라고 부른다.

더 나아가 외부에서 접근을 통제할 수 있는 접근 제어(access control) 메커니즘도 함께  제공한다.

 

하나는 외부에서 접근 가능한 부분으로 이를 퍼블릭 인터페이스(public interface)라고 부른다.
다른 하나는 외부에서는 접근 불가능하고 오직 내부에서만 접근 가능한 부분으로 이를 구현(implementation)이라고 부른다.

 

public, protected, private과 같은 접근 수정자를 사용해 본 적이 있어 어느 정도 이해가 되었다.

C++ 코드를 작성할 때 hpp 파일에서 클래스의 메서드 중 외부에서 접근 가능한 것들을 public으로 선언하고, 이 외의 속성은 private로 선언해서 구현 세부 사항을 감추었다.

 

구현 은닉에 대한 이유로써 private 변수를 사용한다는 이야기를 어디선가 본 적이 있었다. 이러한 이유를 포함하여 객체의 자율성을 보장하고 1장에서 언급한 의존성을 낮출 수 있는 방법 중 하나가 될 수 있을 것 같다.

 

협력하는 객체들

이처럼 시스템의 어떤 기능을 구현하기 위해 객체들 사이에 이뤄지는 상호작용을 협력(Collaboration)이라고 부른다.

객체는 다른 객체의 인터페이스에 공개된 행동을 수행하도록 요청(request)할 수 있다. 요청을 받은 객체는 자율적인 방법에 따라 요청을 처리한 후 응답(response)한다.

객체가 다른 객체와 상호작용할 수 있는 유일한 방법은 메시지를 전송하는 것뿐이다. 다른 객체에서 요청이 도착할 때 해당 객체가 메시지를 수신했다고 이야기한다. 메시지를 수신한 객체는 스스로의 결정에 따라 자율적으로 메시지를 처리할 방법을 결정한다. 이처럼 수신된 메시지를 처리하기 위한 자신만의 방법을 메서드(method)라고 부른다.

 

객체들은 협력한다. 서로 상호작용하기 위해 메시지를 전송하고 수신한다. 수신된 메시지를 처리하는 메서드는 메시지와는 구분되어야 한다.

코드의 의존성과 실행 시점의 의존성이 서로 다를 수 있다는 것이다. 다시 말해 클래스 사이의 의존성과 객체 사이의 의존성은 동일하지 않을 수 있다.

 

이후에 책에서 컴파일 시간 의존성과 실행 시간 의존성에 대해서 설명하는데 이 부분에서 메서드는 메시지와 구분될 수 있다는 예시가 될 수 있다고 생각했다. 부모 클래스를 상속받는 자식 클래스를 업캐스팅해서 부모 클래스 변수에 할당했을 때, 특정 메서드는 코드의 실행 시점에 결정될 수 있기 때문이다.

 

동일한 메시지를 전송하지만 실제로 어떤 메서드가 실행될 것인지는 메시지를 수신하는 객체의 클래스가 무엇이냐에 따라 달라진다. 이를 다형성이라고 부른다.

이처럼 다형성은 컴파일 시간 의존성과 실행시간 의존성을 다르게 만들 수 있다는 객체지향의 특성을 이용해 서로 다른 메서드를 실행할 수 있게 해 준다.

다시 말해 메시지와 메서드를 실행 시점에 바인딩한다는 것이다. 이를 지연 바인딩(lazy binding) 또는 동적 바인딩(dynamic binding)이라고 부른다. 그에 반해 전통적인 함수 호출처럼 컴파일 시점에 실행될 함수나 프로시저를 결정하는 것을 초기 바인딩(early binding) 또는 정적 바인딩(static binding)이라고 부른다.

클래스를 상속받는 것만이 다형성을 구현할 수 있는 유일한 방법은 아니다.

 

여기서 언급되는 동적 바인딩이 위에서 말했던 예시인 것 같다. 

다형성이라는 개념을 상속을 통해 학습해서 그런지 상속이 다형성을 구현할 수 있는 유일한 방법이 아니라는 것이 흥미롭다.

상속과 합성

상속은 객체지향에서 코드를 재사용하기 위해 널리 사용되는 기법이다. 하지만 두 가지 관점에서 설계에 안 좋은 영향을 미친다. 하나는 상속이 캡슐화를 위반한다는 것이고, 다른 하나는 설계를 유연하지 못하게 만든다는 것이다.

인터페이스의 정의된 메시지를 통해서만 코드를 재사용하는 방법을 합성이라고 한다.

상속은 클래스를 통해 강하게 결합되는 데 비해 합성은 메시지를 통해 느슨하게 결합된다. 따라서 코드 재사용을 위해서는 상속보다는 합성을 선호하는 것이 더 좋은 방법이다[GOF94].

 

합성이라는 용어를 처음 들어보았는데 계속 사용하고 있던 방법이었다. 합성을 상속을 통해서도 구현을 할 수 있다는 것을 책의 예제를 통해 이해할 수 있었다.

추상화와 유연성

재사용 가능한 설계의 기본을 이루는 디자인 패턴이나 프레임워크 모두 추상화를 이용해 상위 정책을 정의하는 객체지향의 메커니즘을 활용하고 있기 때문이다.

유연성이 필요한 곳에 추상화를 사용하라.

따라서 책임의 위치를 결정하기 위해 조건문을 사용하는 것은 협력의 설계 측면에서 대부분의 경우 좋지 않은 선택이다. 항상 예외 케이스를 최소화하고 일관성을 유지할 수 있는 방법을 선택하라.

구현과 관련된 모든 것들이 트레이드오프의 대상이 될 수 있다는 사실이다. 여러분이 작성하는 모든 코드에는 합당한 이유가 있어야 한다. 비록 아주 사소한 결정이더라도 트레이드오프를 통해 얻어진 결론과 그렇지 않은 결론 사이의 차이는 크다. 고민하고 트레이드오프하라.

 

자잘한 생각들

  • 초반에 용어의 명확한 구분에 대한 내용이 언급된다. 예시로 등장하는 온라인 영화 예매 시스템에서 "영화"와 "상영"이라는 용어의 차이를 명확히 한다. 사용자가 실제로 예매하는 대상은 "영화"가 아니라 "상영"이기 때문이다. 이러한 용어 구분은 구현 단계에서 요구사항을 명확히 이해하고 설계할 수 있도록 도움이 될 것이다. 각자 생각하는 범위가 다르기 때문에 이를 구체적으로 정의할수록 이해하기 편한 것 같다. 
  • 책에서 추상 클래스와 인터페이스 트레이드오프 부분(68p)이 이해하기 어려웠다. 코드를 뜯어봐야 더욱 이해가 될 것 같다.
  • 객체에 초점을 맞춰 설계를 해봐야겠다.
  • TEMPLATE METHOD 패턴[GOF94](부모 클래스에 기본적인 알고리즘의 흐름을 구현하고 중간에 필요한 처리를 자식 클래스에게 위임하는 디자인 패턴)이 보통 상속에서 자주 사용했던 구현 방식이었는데, 이러한 방식의 디자인 패턴의 명칭을 알게 되었다.