오브젝트 : 코드로 이해하는 객체지향 설계 - 조영호 저
사용자는 영화 예매 시스템을 이용해 쉽고 빠르게 보고 싶은 영화를 예매할 수 있다.
단어 정의
사용자가 실제로 예매하는 대상은 영화가 아니라 상영이다.
특정한 조건을 만족하는 예매자는 요금을 할인받을 수 있다.
할인액을 결정하는 두 가지 규칙
할인 조건 : 가격의 할인 여부 결정
할인 정책
영화별로 하나의 할인 정책만 할당할 수 있다.
할인을 적용하기 위해서는 할인 조건과 할인 정책을 함께 조합해서 사용한다.
먼저 사용자의 예매 정보가 할인 조건 중 하나라도 만족하는지 검사한다.
도메인(domain) : 문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야
객체지향 패러다임이 강력한 이유는 요구사항을 분석하는 초기 단계부터 프로그램을 구현하는 마지막 단계까지 객체라는 동일한 추상화 기법을 사용할 수 있기 때문이다.
일반적으로 클래스의 이름은 대응되는 도메인 개념의 이름과 동일하거나 적어도 유사하게 지어야 한다.
private
이고 메서드의 가시성은 public
public
메서드를 통해서만 내부 상태를 변경할 수 있게 해야 한다.객체가 상태(state)와 행동(behavior)을 함께 가지는 복합적인 존재라는 것이다.
객체가 스스로 판단하고 행동하는 자율적인 존재라는 것이다.
캡슐화 : 데이터와 기능을 객체 내부로 함께 묶는 것
대부분의 객체지향 프로그래밍 언어들은 외부에서 접근을 통제할 수 있는 접근 제어(access control) 메커니즘을 함께 제공한다.
캡슐화와 접근 제어는 객체를 두 부분으로 나눈다.
인터페이스와 구현의 분리(separation of interface and implementation) 원칙은 훌륭한 객체지향 프로그램을 만들기 위해 따라야 하는 핵심 원칙이다.
프로그래머의 역할을 클래스 작성자(class creator)와 클라이언트 프로그래머(client programmer)로 구분하는 것이 유용하다.
클라이언트 프로그래머의 목표는 필요한 클래스들을 엮어서 애플리케이션을 빠르고 안정적으로 구축하는 것이다.
클라이언트 프로그래머는 내부의 구현은 무시한 채 인터페이스만 알고 있어서 클래스를 사용할 수 있기 때문에 머릿속에 담아둬야 하는 지식의 양을 줄일 수 있다.
클래스 작성자는 인터페이스를 바꾸지 않는 한 외부에 미치는 영향을 걱정하지 않고도 내부 구현을 마음대로 변경할 수 있다.
클래스를 개발할 때마다 인터페이스와 구현을 깔끔하게 분리하기 위해 노력해야 한다.
설계가 필요한 이유는 변경을 관리하기 위해서라는 것을 기억하라.
의미를 좀 더 명시적이고 분명하게 표현할 수 있다면 객체를 사용해서 해당 개념을 구현하라.
협력(Collaboration) : 시스템의 어떤 기능을 구현하기 위해 객체들 사이에 이뤄지는 상호작용
객체지향 프로그램을 작성할 때는 먼저 협력의 관점에서 어떤 객체가 필요한지를 결정하고, 객체들의 공통 상태와 행위를 구현하기 위해 클래스를 작성한다.
객체는 다른 객체의 인터페이스에 공개된 행동을 수행하도록 요청(request)할 수 있다.
요청을 받은 객체는 자율적인 방법에 따라 요청을 처리한 후 응답(response)한다.
객체가 다른 객체와 상호작용할 수 있는 유일한 방법은 메시지를 전송(send a message)하는 것뿐이다.
다른 객체에게 요청이 도착할 때 해당 객체가 메시지를 수신(receive a message)했다고 이야기한다.
메서드(method) : 수신된 메시지를 처리하기 위한 자신만의 방법
TEMPLATE METHOD 패턴 : 부모 클래스에 기본적인 알고리즘의 흐름을 구현하고 중간에 필요한 처리를 자식 클래스에게 위임하는 디자인 패턴
오버라이딩과 오버로딩
오버라이딩(overriding) : 부모 클래스에 정의된 같은 이름, 같은 파라미터 목록을 가진 메서드를 자식 클래스에서 재정의하는 경우
오버로딩(overloading) : 이름은 같지만 제공되는 파라미터의 목록이 다르다.
상속을 이용하면 클래스 사이에 관계를 설정하는 것만으로 기존 클래스가 가지고 있는 모든 속성과 행동을 새로운 클래스에 포함시킬 수 있다.
차이에 의한 프로그래밍(programming by difference) : 부모 클래스와 다른 부분만을 추가해서 새로운 클래스를 쉽고 빠르게 만드는 방법
상속이 가치 있는 이유는 부모 클래스가 제공하는 모든 인터페이스를 자식 클래스가 물려받을 수 있기 때문이다.
인터페이스는 객체가 이해할 수 있는 메시지의 목록을 정의한다는 것을 기억하라.
결과적으로 자식 클래스는 부모 클래스가 수신할 수 있는 모든 메시지를 수신할 수 있기 때문에 외부 객체는 자식 클래스를 부모 클래스와 동일한 타입으로 간주할 수 있다.
업캐스팅(upcasting) : 자식 클래스가 부모 클래스를 대신하는 것
다형성 : 동일한 메시지를 수신했을 때 객체의 타입에 따라 다르게 응답할 수 있는 능력
추상화의 계층만 따로 떼어 놓고 살펴보면 요구사항의 정책을 높은 수준에서 서술할 수 있다.
설계가 좀 더 유연해진다.
책임의 위치를 결정하기 위해 조건문을 사용하는 것은 협력의 설계 측면에서 대부분의 경우 좋지 않은 선택이다.
추상화가 유연한 설계를 가능하게 하는 이유는 설계가 구체적인 상황에 결합되는 것을 방지하지 때문이다.
구현과 관련된 모든 것들이 트레이드오프의 대상이 될 수 있다.
작성하는 모든 코드에는 합당한 이유가 있어야 한다.
부모 클래스의 구현이 자식 클래스에게 노출되기 때문에 캡슐화가 약화된다.
상속은 부모 클래스와 자식 클래스 사이의 관계를 컴파일 시점에 결정된다.
합성은 상속이 가지는 두 가지 문제점을 모두 해결한다.
대부분의 설계에서는 상속과 합성을 함께 사용해야 한다.