it 책/오브젝트: 코드로 이해하는 객체지향 설계

오브젝트: 코드로 이해하는 객체지향 설계 2장을 읽으며

lgvv 2024. 11. 8. 00:16

오브젝트: 코드로 이해하는 객체지향 설계 2장을 읽으며

 

느낀점

최근들어서 대규모 시스템 설계에서 합성의 중요성을 깊게 느끼고 있음. 경험에 의해서 상속은 부모 클래스가 비대해지고 확장성이 떨어지는 것 때문에 상속 자체를 꺼리고 있었는데, 객체 간 결합도를 높여 시스템 유지보수를 어렵게 만든다는 점을 이론적으로 이해할 수 있었음.

합성을 통해 각 객체가 독립적으로 동작할 수 있도록 설계하는 것이 왜 중요한지, 그리고 이렇게 설계했을 때 시스템이 얼마나 유연하게 변화에 대응할 수 있는지를 다시 느낌.

 

설계에 근거를 점점 더 갖추게 되는 2장!

 

협력, 객체, 클래스

객체지향은 객체를 지향하는 것이지, 클래스(class)를 결정한 후 어떤 어떤 속성과 메소드가 필요한 지 결정하느 는 것과는 거리가 멂.

  • 진정한 객체지향 패러다임으로의 전환은 클래스가 아닌 객체에 초점을 맞출때만 얻을 수 있음.

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

  • 어떤 상태나 행동을 가지는지 먼저 결정

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

  • 객체들의 윤곽이 잡히면 공통된 특성과 상태를 가진 객체들을 타입으로 분류하고 이 타입을 기반으로 클래스를 구현

 

도메인의 구조를 따르는 프로그램 구조

문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야를 도메인(domain)이라고 함

  • 진정한 객체지향 패러다임으로의 전환은 클래스가 아닌 객체에 초점을 맞출때만 얻을 수 있음.

 

자율적인 객체

객체에 대한 두가지 중요한 사실을 먼저 알아보자

  • 객체가 상태(state) 행동(behavior)를 함께 가지는 복합적인 존재
  • 스스로 판단하고 행동하는 자율적인 객체라는 것

객체라는 단위 안에 데이타와 기능을 한 덩어리로 묶음으로써 문제 영역의 아이디어를 적절하게 표현

  • 데이터와 기능을 객체 내부로 함께 묶는 것을 캡슐화라고 함.

상태와 행동을 캡슐하는 것에서 더 나아가 접근 제어(access control) 메커니즘 제공

  • private, public 등과 같은 접근 수정자(access modifier)를 제공

캡슐화와 접근 제어는 객체를 두 부분으로 나눔.

  • 외부에서 접근 가능한 퍼블릭 인터페이스(public interface)
  • 오직 내부에서만 접근 가능한 구현(implementation)
  • 인터페이스 구현의 분리(separation of interface and implementation) 원칙

 

일반적으로 객체의 상태는 숨기고 행동만 외부에 공개해야 한다.

 

 

프로그래머의 자유

프로그래머의 역할을 클래스 작성자(class creator) 클라이언트 프로그래머(client programmer)로 나눔

  • 클래스 작성자는 새로운 데이터  타입을 프로그램에 추가
  • 클라이언트 프로그래머는 클래스 작성자가 추가한 데이터 타입을 사용

클래스 작성자

  • 클라이언트 프로그래머에게 필요한 부분만 공개하고 나머지는 숨겨야 하며 이를 구현 은닉(implementation hiding)이라 함
  • 인터페이스를 바꾸지 않는 한 외부에 미치는 영향을 고려하지 않고 내부 구현 변경 가능

설계가 필요한 이유는 변경을 관리하기 위해서. 세부 구현 내용을 private 영역 안에 감춤으로써 변경으로 인한 혼란을 최소화 가능

 

협력에 관한 짧은 이야기

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

  • 객체가 다른 객체와 상호작용할 수 있는 메시지를 전송(send a message)하는 것 뿐
  • 다른 객체에게 요청이 도착할 때 해당 객체가 메시지를 수신(receive a message)했다고 이야기 함.
  • 객체가 수신된 메시지를 자율적으로 처리하기 위한 자신만의 방법을 메서드(method)라고 함.

메시지와 메서드를 구분하는 건 매우 중요

  • 메시지와 메서드의 구분에서 다형성(polymorphism)이 출발
  • 인터페이스를 바꾸지 않는 한 외부에 미치는 영향을 고려하지 않고 내부 구현 변경 가능

객체지향에는 상속(Inhretance)과 다형성이 있으며 그 기반에서는 추상화(abstract class)가 섞여 있음.

  • 인스턴스 생성이 필요없으므로 부모는 추상 클래스(abstract class)
  • 자식에서 호출하는건 추상 메서드(abstract method)

흐름을 정의하고 흐름 중간에 필요한 처리를 자식에게 위임하는 것을 템플리 메서드 패턴이라고 함.

 

오버라이딩과 오버로딩

오버라이딩

  • 부모 클래스를 자식 클래스에서 재정의

오버로딩

  • 메서드 이름은 같지만 제공되는 파라미터 목록이 다름

 

컴파일 시간 의존성과 실행 시간 의존성

코드 의존성과 실행 시점의 의존성이 서로 다를 수 있음

  • 코드를 이해하기 위해 코드뿐만 아니라 연결하는 부분을 찾아야 해서 이해하기 어려워 질 수 있음
  • 유연해지고 확장 가능
  • 의존성의 양면성은 설계가 트레이드오프의 산물

 

차이에 의한 프로그래밍

자식 클래스에서 상속 받은 부모 클래스와 다른 부분만을 추가해서 새로운 클래스로 빠르게 만드는 방법

  • 코드를 이해하기 위해 코드뿐만 아니라 연결하는 부분을 찾아야 해서 이해하기 어려워 질 수 있음
  • 유연해지고 확장 가능
  • 의존성의 양면성은 설계가 트레이드오프의 산물

 

다형성

메시지와 메서드는 다른 개념.

  • 객체 A는 인터페이스, 상속 등에 의한 메시지를 전송하고, 전달 받는 객체가 실행하는게 메서드. 메서드는 전달 받는 객체에 따라 달라짐.
  • 이를 다형성이라고 표현

다형성을 구현하는 방법

  • 지연 바인딩(lazy binding) 혹은 동적 바인딩(dynamic binding): 메시지와 메서드를 실행 시점에 바인딩 하는 것
  • 초기 바인딩(early binding) 혹은 정적 바인딩(static binding): 컴파일 타임에 실행될 함수나 프로시저를 결정하는 것

 

추상화의 힘

추상화를 사용하면 세부적인 내용을 무시한 채 상위 정책을 쉽고 간단하게 표현할 수 있음.

  • 특정 세부사항에 억눌리지 않고 상위 개념만으로 도메인의 중요한 개념을 설명할 수 있음.
  • 정책(policy) 조건(condition)을 자식 클래스들은 추상화를 이용해서 정의한 상위의 협력 흐름을 그대로 따라감. 디자인 패턴이나 프레임워크 모두 추상화를 이용해 상위 정책을 정의하는 객체지향 메커니즘을 활용하고 있기 때문


추상 클래스와 인터페이스 트레이드오프

통일성을 위한 특정 인터페이스를 추가하는건 때로는 과하다는 판단이 들수도 있음. 즉, 구현과 관련한 모든 것들은 트레이드 오프

  • 특정 세부사항에 억눌리지 않고 상위 개념만으로 도메인의 중요한 개념을 설명할 수 있음.

 

코드 재사용

상속은 코드를 재사용하기 위해 널리 사용되는 방법. 

  • 그러나 가장 좋은 방법은 아니며, 객체 지향 설계와 관련된 자료를 조금이라도 본 사람들은 코드 재사용을 위해서는 합성(composition)이 더 좋은 방법

 

상속

코드 재사용을 위해 가장 널리 사용. 

  • 하지만 가장 큰 문제점은 캡슐화를 위반한다는 것이며, 다른 하나는 설계를 유연하게 하지 못함.

캡슐화 위반

  • 결과적으로 부모 클래스의 구현이 자식 클래스에게 노출되기 때문에 캡슐화가 약화.
  • 캡슐화의 약화는 자식 클래스가 부모 클래스에 강하게 결합되도록 만들기 때문에 부모 클래스를 변경할 때 클래스도 함께 변경될 확률을 높임.

설계가 유연하지 않다는 것

  • 상속은 부모 - 자식 객체간에 관계를 컴파일 시점에 결정하여, 실행 시간에 객체의 종류를 변경하는게 불가능
  • 따라서 다른 변경하려고 할 경우 다른 종류 객체의 인스턴스를 생성한 후 넣어주어야 함.

 

합성

합성은 인터페이스에 의해서 약하게 결합된다는 것

  • 인터페이스에 정의된 메시지만을 통해 코드를 재사용 하는 방법을 합성이라고 부름
  • 합성은 상속이 가지는 두가지 문제를 해결함.