이번 장에서 영화 예매 시스템을 예제로 설명한다.
영화 예매 시스템에서 실제로 사용자가 예매하는 대상은 영화
가 아닌 상영
이다.
예매자의 요금 할인은할인 조건
과 할인 정책
에 따라 할인액이 결정된다.
할인 조건
은 상영 순번을 이용해 결정하는 순서 조건
과 상영 시작 시간에 따라 결정하는 기간 조건
으로 나눌 수 있다.
할인 정책
은 일정 금액을 할인하는 금액 할인 정책
과 일정 비율의 요금을 할인하는 비율 할인 정책
으로 나눌 수 있으며, 한 영화에 하나의 할인 정책만 할당할 수 있다.
객체지향 프로그램을 작설할 때 어떤 클래스가 필요한가?
에 대한 고민이 아니라 어떤 객체가 필요한가?
에 초점을 맞추어야 한다.
1. 어떤 객체들이 어떤 상태와 행동을 가지는지 결정해야한다. (클래스는 공통적인 상태와 행동을 공유하는 객체들을 추상화한 것)
2. 객체는 다른 객체에 도움을 주거나 의존하며 살아가는 협력적인 존재다.
도메인이란 문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야를 말한다.
영화 예매 도메인에서 영화
는 여러 번 상영
될 수 있으며 상영
은 여러 번 예매
될 수 있다.
영화
는 할인 정책
을 1개만 할당하거나 할당하지 않을 수 있다.
할인 정책
에는 1개 이상의 할인 조건
이 존재한다.
클래스를 구현하거나 다른 개발자의 개발된 클래스를 사용할 때 가장 중요한 것은 클래스의 경계
를 구분 짓는것이다. 어떤 부분을 외부에 공개하고 어떤 부분을 감출지를 결정해야한다. private를 사용하여 객체의 속성에 대한 접근을 차단하고 public 메소드를 통해서 내부 상태를 변경할 수 있게 해야한다. 클래스의 내부와 외부를 구분하는 경계의 명확성은 객체의 자율성
을 보장한다.
객체는 상태
와 행동
을 가지는 존재이며 스스로 판단하고 행동하는 자율적인 존재
다.
객체라는 단위 안에 데이터(상태)와 기능(행동)을 한 덩어리로 묶는 것을 캡슐화
라고 부른다.
public, protedcted, private와 같은 접근 수정자
를 통해 외부에서 객체의 접근 제어
를 한다.
객체는 외부에서 접근이 가능한 퍼블릭 인터페이스
와 외부에서 접근이 불가능하며 내부에서만 접근이 가능한 구현
으로 나누어진다.
인터페이스와 구현의 분리
원칙은 객체지향 프로그램을 만들기 위한 핵심 원칙이다.
클래스 작성자는 필요한 부분만 공개하고 나머지는 숨김으로써 클라이언트 프로그래머가 숨긴 부분에 대한 접근을 차단하는 것을 구현 은닉
이라고 부른다.
영화를 예매하는 기능을 구현하는 메소드는 예매 정보를 담고 있는 Reservation의 인스턴스를 생성해 반환한다.
Reservation 인스턴스는 예매자 정보, 상영 정보, 예매요금, 인원 수를 속성으로 가진다.
예매요금을 계산하기위해 calculateFee 메서드를 호출하고, 이 메서드는 다시 Movie의 calculateMovieFee메소드를 호출한다.
calculateMovieFee메서드는 1인당 예매 요금을 반환한다.
calculateFee메서드는 전체 예매 요금을 구하기 위해 인원 수를 곱한 결과를 반환한다.
Money는 금액과 관련된 다양한 계산을 구현하는 클래스다.
Long타입과 같은 정수형 타입을 사용할 수도 있지만 이 경우 구현 관점의 제약
(변수 크기, 연산자 종류 등)은 표현할 수 있지만 Money타입 처럼 저장하는 값이 금액과 관련돼 있다는 의미
를 전달할 수 없다. 따라서 금액과 관련된 기능이 서로 다른 곳에 중복되어 구현되는 것을 막을 수 없다. 의미를 좀 더 명시적이고 분명하게 표현
(ex. Long타입 대신 Money타입)할 수 있다면 객체를 사용해서 해당 개념을 구현하는 것이 중요하다.
1. 협력의 관점에서 어떤 객체가 필요한지를 결정한다.
2. 객체가 어떤 상태와 행동을 가지는지 결정한다.
3. 객체들의 공통 상태와 행위를 구현하기 위한 클래스를 작성한다.
Movie의 calculateMovieFee는 discountPolicy에 calculateDiscountAmount 메시지를 전송해 할인 요금을 반환받는다.
예매 요금을 계산하기 위해서는 현재 영화에 적용돼 있는 할인 정책의 종류를 판단해야한다.
두 가지 할인 정책 AmountDiscountPolicy와 PercentDiscountPolicy는 대부분의 코드가 유사하지만 할인 요금을 계산하는 방식만 다르다.
중복 코드를 제거하기 위해 부모 클래스인 DiscountPolicy 안에 중복 코드를 두고 두 클래스가 이를 상속
받게 한다.
실제 애플리케이션에서는 DiscountPolicy의 인스턴스
를 생성할 필요가 없으므로 추상 클래스
로 구현한다.
calculateDiscountAmount 메소드는 전체 할인 조건에 대해서 isSatisfiedBy 메소드를 호출하며, 상영정보(Screening)을 인자로 전달한다.
조건을 만족한다면 추상 메서드
인 getDiscountAmount메소드에 상영정보를 전달하여 할인 요금을 계산하며 모든 조건을 만족하지 못한다면 0원을 반환한다.
이 추상 메서드 getDiscountAmount는 자식 클래스에서 오버라이딩한 메소드가 되며, 이처럼 부모 클래스에서 기본적인 알고리즘의 흐름을 구현하고 중간에 필요한 처리를 자식 클래스에게 위임하는 디자인 패턴을 TEMPLATE METHOD
패턴이라고 부른다.
DiscountCondition은 자바 인터페이스로 선언돼 있다. isSatisfiedBy 오퍼레이션
은 상영정보(Screening)를 인자로 받아 할인 여부를 반환한다.
두 가지 할인 조건 SequenceCondition과 PeriodCondition은 할인 여부를 반환하는 isSatisfiedBy 오퍼레이션을 실체화
한다.
오퍼레이션 : 기능의 추상적인 정의, 따라서 몸체(구현)이 존재하지 않는다.
메소드 : 오퍼레이션의 구체적인 구현이며 메소드 구현을 포함한다.
오버라이딩 : 부모 클래스(superclass)와 자식 클래스(child class)안에서 같은 메소드
오버로딩 : 한 클래스(same class)에서 같은 이름의 메소드지만 다른 인자(파라미터)를 가지는 메소드
Movie클래스가 DiscountPolicy클래스와 연결되어 있으며 영화 요금을 계산하기 위해서는 추상 클래스인 DiscountPolicy가 아닌 AmountDiscountPolicy와 PercentDiscountPolicy 인스턴스가 필요하다. 하지만 코드 수준에서 Movie클래스는 두 클래스 중 어떤 것에도 의존하지 않으며 추상 클래스인 DicountPolicy에만 의존한다.
Movie인스턴스를 생성시 인자로 DiscocuntPolicy타입의 객체를 받는데 이때 AmountDiscountPolicy와 PercentDiscountPolicy의 인스턴스를 전달하여 의존하게 된다. 이와 같이 코드의 의존성과 실행 시점의 의존성은 서로 다를 수 있다.
코드의 의존성과 실행 시점의 의존성이 다르면 다를수록 코드는 더 유연해지고 확장이 가능해진다.
하지만 설계가 유연해질수록 코드를 이해하고 디버깅하기는 점점 더 어려워진다.
무조건 유연한 설계도, 무조건 읽기 쉬운 코드도 정답이 아니니 유연성과 가독성을 적절히 조절하는 것이 중요하다.
상속 : 부모 클래스가 제공하는 모든 인터페이스를 자식 클래스가 물려받는 것. 상속은 구현 상속과 인터페이스 상속으로 분류할 수 있다.
인터페이스 : 모든 기능(오퍼레이션)을 추상화로 정의만 하고 구현자히 않은 것
추상클래스 : 하나 이상의 추상 메소드를 포함하는 클래스
인터페이스 vs. 추상클래스
인터페이스와 추상클래스는 모두 상속을 통해 기능에 대한 강제 구현 규칙을 가집니다.
이 둘의 가장 큰 차이점은다중 상속
의 가능 여부지만, 이건이 핵심이 아니라 둘의사용 목적
이 다르다는 것입니다.
인터페이스는 인터페이스에정의된 기능을 각 클래스의 목적에 맞게 구현
하기 위해 사용됩니다. 따라서 구현 객체가 같은 동작을 한다는 것을 보장할 수 있습니다.
추상클래스는부모클래스의 기능들을 하위 클래스로 확장
하기 위해 사용됩니다. 상속할 각 객체들의 공통점을 찾아 추상화한 것이 추상클래스이며 하위 클래스(자식 클래스)에서 부모 클래스가 가진 기능들을 구현합니다.
아래 두 그림에서 Human 추상 클래스와 Eatable 인터페이스의 차이를 생각해보자.
operation vs. method - Julian Pustkuchen
Overriding vs Overloading in Java - Pankaj
인터페이스를 사용하는 이유 - Alfred’s Computing Weblog
[Java] 자바 - 인터페이스(interface)의 이해 및 사용하는 이유 - KADOSHoly
[JAVA] ☕ 인터페이스 vs 추상클래스 차이점 - 완벽 이해하기 - Inpa Dev
추상 클래스와 인터페이스의 차이 - 북항
Abstract Class vs Interface in Java – Difference Between Them - James Hartman