오브젝트 Chapter 2, 3
🎶 오브젝트
이 글은 오브젝트를 읽고 작성한 글이다. 책을 통해서 얻은 내 나름의 핵심 개념을 정리하면서 책을 읽는 것을 목표로 한다. 개인적으로 개발 공부를 하며 가장 인상 깊게 읽은 인생 도서가 동일한 저자인 조영호님의 객체지향의 사실과 오해인만큼 정말 기대된다!!
2. 객체지향 프로그래밍
협력, 객체, 클래스
객체지향은 객체
를 지향하는 것이다. 따라서 클래스가 아닌 객체에 초점을 맞춰야 한다.
- 어떤 클래스가 필요한지를 고민하기보다 어떤 객체가 필요한지를 고민하라. 클래스는 공통적인 상태와 행동을 공유하는 객체들을 추상화한 것이다.
- 객체를 독립적인 존재가 아니라 기능을 구현하기 위해 협력하는 공동체의 일원으로 봐야한다.
도메인
소프트웨어는 사용자가 원하는 어떤 문제를 해결하기 위해 만들어진다. 이러한 문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야를 도메인
이라고 한다.
클래스 구현하기
- 인스턴스 변수의 가시성은
private
으로 설정하라. - 메서드의 가시성은
public
으로 설정하라.
클래스 내부와 외부를 구분해야하는 이유
1. 경계의 명확성이 객체의 자율성을 보장하기 때문이다.
2. 프로그래머에게 구현의 자유를 제공하기 때문이다.
자율적인 객체
-
객체는
상태
와행동
을 함께 가지는 복합적인 존재이다. -
객체는 스스로 판단하고 행동하는
자율적인
존재이다. -
객체의 상태는 숨기고 행동은 외부에 공개한다.(일반적으로)
협력에 대해
-
객체는 다른 객체의 인터페이스에 공개된 행동을 수행하도록
요청
할 수 있다. -
요청을 받은 객체는 자율적인 방법으로 요청을 처리하여
응답
한다. -
메시지 => 요청
-
메서드 => 응답
-
메시지를 어떻게 처리할지는 오직
수신하는 객체
가 결정한다.
오버라이딩과 오버로딩
- 오버라이딩 : 부모 클래스의 메서드를 자식 클래스에서 재정의하는 것
- 오버로딩 : 동일한 이름의 메서드를 여러 개 정의하고 매개변수의 타입과 개수를 다르게 하여 각각의 메서드를 구분하는 것
컴파일 시간 의존성과 실행 시간 의존성
- 컴파일 시간 의존성 : 클래스 사이의 의존성
- 실행 시간 의존성 : 객체(인스턴스) 사이의 의존성
의존성 주입을 예시로 들면, 컴파일 시간 의존성은
new
키워드를 통해 객체를 생성하는 것이고, 실행 시간 의존성은 생성된 객체를 주입하는 것이다.
설계가 유연해질수록 코드를 이해하고 디버깅하기는 점점 더 어려워진다.
반면 유연성을 억제하면 코드를 이해하고 디버깅하기는 쉬워지지만, 재사용성과 확장 가능성은 낮아진다.
상속
-
상속은
코드 재사용
을 위해 사용되는 것이 아니다. -
상속은
타입 계층
을 구축하기 위해 사용되어야 한다.- 부모 클래스가 제공하는 모든 인터페이스를 자식 클래스가 물려받는다.
-
구현 상속(서브클래싱): 코드를 재상효하기 위해 사용하는 상속
-
인터페이스 상속(서브타이핑): 타입 계층을 구축하기 위해 사용하는 상속
즉, 구현 상속을 지양하고 인터페이스 상속을 지향해야 한다.
인터페이스
객체가 이해할 수 있는 메시지의 목록을 정의한 것
업캐스팅
부모 클래스의 인스턴스 변수에 자식 클래스의 인스턴스를 대입할 수 있는 것. 마치 자식 클래스의 인스턴스가 부모 클래스의 인스턴스인 것처럼 동작한다고 해서 업캐스팅
이라고 한다.
다형성
동일한 메시지를 수신했을 때, 객체의 타입에 따라 다르게 응답할 수 있는 능력을 의미한다. 따라서 다형적인 협력에 참여하는 객체들은 모두 같은 메시지를 이해할 수 있어야 한다. 즉, 동일한 인터페이스를 공유해야 한다.
지연 바인딩(동적 바인딩): 실행 시점에 메시지를 수신하는 객체의 타입에 따라 실행 결과가 달라지는 것(다형성을 구현하는 방법)
추상화
추상화는 중요한 부분을 강조
하기 위해 불필요한 부분을 생략하는 것이다.
- 추상화를 사용하면 세부적인 내용을 무시한 채 상위 정책을 쉽고 간단하게 표현할 수 있다.
- 추상화를 이용한 설계는 필요에 따라 표현의 수준을 조정하는 것을 가능하게 해준다.
유연성이 필요한 곳에 추상화를 사용하라.
합성
인터페이스에 정의된 메시지를 통해서만 객체를 사용하도록 제한하는 것을 합성
이라고 한다.
- 합성을 통해 객체의 내부 구현에 강하게 결합되는 것을 피할 수 있다.
- 합성을 통해 객체의 내부 구현을 외부로부터 감추는 것이 가능하다.
합성은 상속보다 더 유연한 코드를 만들 수 있게 해준다.
즉, 코드를 재사용하는 경우에는
상속(구현 상속)
보다는합성
을 사용하는 것이 좋다.
핵심
객체지향 설계의 핵심은 적절한 협력을 식별
하고 협력에 필요한 역할을 정의
한 후에 역할을 수행할 수 있는 적절한 객체에게 적절한 책임을 할당
하는 것이다.
3. 역할, 책임, 협력
객체지향의 본질은 협력하는 객체들의 공동체를 창조
하는 것이다.
- 협력: 객체들이 애플리케이션의 기능을 구현하기 위해 수행하는 상호작용
- 책임: 객체가 협력에 참여하기 위해 수행하는 로직
- 역할: 객체가 수행하는 책임들의 집합
객체를 자율적으로 만다는 가장 기본적인 방법은 내부 구현을
캡슐화
하는 것이다.
협력
객체의 행동을 결정하는 것이 협력이라면 객체의 상태를 결정하는 것은 행동이다.
책임
객체가 유지해야 하는 정보
와 수행할 수 있는 행동
에 대해 개략적으로 서술한 문장이다. 즉, 무엇을 알고 있는가
와 무엇을 할 수 있는가
로 구성된다.
객체지향 설계는 협력에 필요한 메시지를 찾고 메시지에 적절한 객체를 선택하는 반복적인 과정을 통해 이뤄진다.
기본적으로 어떤 정보를 많이 알고 있는 객체에게 책임을 할당하는 것이 좋다.
객체지향 패러다임에 갓 입문한 사람들이 가장 쉽게 빠지는 실수는 객체의 행동이 아니라 상태에 초점을 맞추는 것이다.
상태는 단지 객체가 행동을 정상적으로 수행하기 위해 필요한 재료일 뿐이다.
역할
객체가 어떤 특정한 협력 안에서 수행하는 책임의 집합이다.
- 협력을 모델링할 때는 특정한 객체가 아니라 역할에게 책임을 할당한다고 생각하는 게 좋다.
- 적절한 역할이 무엇인가를 찾는다.
- 역할을 수행할 객체를 선택한다.
역할을 통해 유연하고 재사용 가능한 협력을 얻을 수 있다.
인터페이스와 추상 클래스
- 인터페이스: 객체가 수신할 수 있는 메시지의 목록을 정의
- 추상 클래스: 책임의 일부를 구현할 수도 있음
인터페이스와 추상 클래스는 역할을 정의하는 데 사용할 수 있는 도구이다.
역할은 객체의 페르소나이다.