객체지향 프로그래밍
in Dev-Study on Architecture
공부하는 내용을 정리하는 목적으로 작성하고 있습니다. 잘못 작성된 내용을 지적해주시면 좀더깊이 공부해서 내용을 수정하겠습니다.
객체지향 프로그래밍의 핵심 요소
협력: 객체들이 어플리케이션의 기능을 구현하기 위해 수행하는 상호작용
책임: 객체가 협력에 참여하기 위해 수행하는 로직
역할: 객체가 특정한 협력안에서 수행하는 책임의 집함
협력
메시지 전송
객체사이의 협력을 위해 사용할 수 있는 유일한 수단
객체는 캡슐화로 인해 다른객체의 내부 구현에는 접근할 수 없으므로 외부에 노출된 메시지를 통해서만 요청을 전달할 수 있다.
메시지를 수신한 객체는 메서드를 실행해 요청에 응답한다.
메시지 != 메서드
(호출한 객체는 전송한 메시지에 대해 원하는 응답만 받으면되며, 처리 로직은 메시지를 수신한 객체가 스스로 정한다.)
객체란 상태와 행동을 함께 캡슐하화는 실행 단위
- 객체가 가질 수 있는 상태와 행동은 어떤 기준으로 결정해야 하는가?
객체는 어떤 협력에 참여하고 있기 때문에 존재할 수 있다.
객체가 협력에 참여할 수 있는 이유는 해당 협력에 필요한 행동을 보유하고 있기 때문이다.
책임
하는 것(doing)
- 스스로 계산을 수행하거나 객체를 생성하는 것
- 다른 객체의 행동을 실행하는 것(다른 객체에게 메시지를 전송하는 것)
아는 것(knowing)
- 관련된 객체에 대해 아는 것
- 자신이 유도하거나 계산할 수 있는것에 대해 아는 것
객체는 자신의 역할을 수행하기 위해 필요한 책임이 있다.
객체는 자신이 맡은 책임을 수행하기 위해 필요한 정보를 알고 있을 책임이 있다. (하는 것과 아는 것의 조합)
객체는 자신이 맡은 책임을 수행하기 위해 부족한 무언가가 있다면 도움이 필요한 다른객체를 알 책임이 있다.
객체는 자신이 맡은 책임을 수행하기 위해 다른 객체에게 일부 책임을 위임할 수 있다.
먼저 책임을 찾은 후, 책임을 수행할 객체를 찾아 해당 책임을 할당하는 방식
(어떤 책임을 선택하느냐에 따라 전체 설계의 방향/흐름이 변경될 수 있다.)
책임을 할당할 때 고려 사항
1. 메시지가 객체를 결정한다.
- 객체를 먼저 선택한 후 객체에게 필요한 책임을 메시지로 표현하는것이 아니라, 책임 할당을 위해 필요한 메시지를 먼저 식별 한 후 메시지를 처리할 객체를 선택한다.
- 메시지를 먼저 선택하면 최소한의 인터페이스와 추상적인 인터페이스를 갖을 수 있다.
- 필요한 메시지 외에는 노출할 필요가 없으므로 나머지는 내부 구현으로 구성할 수 있기 때문이다.
2. 행동이 상태를 결정한다.
- 객체가 존재하는 이유는 협력에 참여하기 때문이고 협력에 참여하기 위해서는 협력을 위해 적절한 행동을 제공해야 한다.
- 즉 협력에 필요한 것은 적절한 행동이며, 상태는 행동을 나타내기 위한 수단/재료일 뿐이다.
역할
역할을 통해 유연하고 재사용 가능한 협력을 얻을 수 있다.
만약 어떤 역할을 책임질 객체가 다수가 될 때 협력의 초점을 객체에 맞추면 동일한 책임을 수행하기위한 코드가 중복될 수 있다.
즉, 협력을 위한 구성원은 객체가 아닌 역할이 된다.
역할을 수행하기 위해 객체들이 선택 될 수 있다. 이것은 협력이 실행될 시점에 요구되는 사항에 따라 적절한 객체가 선택되게 된다.
객체는 클래스를 이용해 구현될 수 있다.
티켓 예매 어플리케이션
요구사항
- 사용자는 원하는 영화를 예매할 수 있어야 하며 다수의 티켓도 예매 가능해야 한다.
- 영화 티켓가격은 정가가 있지만 특정 조건에 해당되면 일부 금액이 할인된다.
- 영화마다 할인 정책이 다르게 책정될 수 있다.
세부요구사항
- 상영될 영화는 크게 요금할인 정책과 비율할인 정책이 적용된다.
- 요금할인 정책은 정해진 할인가격만큼 할인되는 방식이며, 비율할인 정책은 정해진 할인비율만큼 할인되는 방식이다.
- 할인 조건으로는 정해진 날짜(요일), 정해진 순번(조조, 심야 등) 일치여부가 있으며 해당 조건에 포함되면 할인조건이 충족된다.
클래스 다이어그램
구현 과정
- 티켓 예매라는 협력을 구성하기 위해 먼저 “예매하기” 라는 메시지를 찾았다.
- 고객은 시스템에 예매하기 메시지를 전송하고, 그 결과로 예매 완료 결과를 수신할 수 있어야 한다.
- 영화 티켓을 예매하기 위해서는 영화 정보(제목,요금,요금할인정책)에 대해 알아야하며, 상영정보(상영시작시각, 상영시간) 등을 알아야하므로 가장 적합한 객체로 Screening(상영)을 선택했다.
- Screening은 영화정보에 대해 알 책임이 있으므로 Movie 객체를 멤버로 추가했다.
- Screening은 상영정보에 대해 알 책임이 있으므로 객체가 생성될 때 상영시간을 파라미터로 추가했다.
- Movie는 영화정보를 제공할 유일한 객체이므로 생성할 때 영화제목을 파라미터에 추가하였다.
- Movie는 영화 예매가격을 제공할(결정할) 유일한 객체이므로 정가와 할인정책을 통해 영화 예매 금액을 계산할 책임이 있다. 따라서 생성할 때 정가와 할인정책을 파라미터에 추가하였다.
- 할인정책은 크게 요금할인 정책과 비율할인 정책으로 구현될 수 있고 둘 다 할인정책이라는 역할을 수행하므로 할인정책은 DiscountPolicy 추상클래스로 구현하였다.
- 요금할인 정책과 비율할인 정책은 DiscountPolicy를 확장하여 각각 할인요금이 적용되도록 구현하였다.
- 요금할인이 적용되지 않는 경우가 있을 수 있으므로 NoneDiscountPolicy 클래스를 추가하였다.
- NoneDiscountPolicy는 할인조건을 확인할 책임이 없으므로 DiscountPolicy 클래스를 확장하기에는 불필요한 부분이 생긴다.(is A에 맞지 않는 부분 발생)
- 따라서 기존 DiscountPolicy 추상클래스는 DefaultDiscountPolicy로 이름을 변경했고, DiscountPolicy라는 인터페이스를 추가하였다.
- DefaultDiscountPolicy, NoneDiscountPolicy 클래스는 DiscountPolicy의 할인요금구하기 인터페이스를 구현하도록 하였다.(추상메서드)
- 할인정책이 충족되는지 확인하기위해 DiscountPolicy는 할인조건(DiscountCondition)을 확인할 책임이 있다.
- 따라서 DiscountPolicy가 생성될 때 파라미터로 DiscountCondition Array를 받도록 구현했다. (조건은 2개 이상이 가능하므로 배열로 받도록 구현했다.)
- DiscountCondition은 할인조건이 충족되는지 확인할 책임이 있고 그 외에 다른 책임은 없으므로 인터페이스로 구현했다.
- 할인 조건으로는 상영시간의 할인시간대에 포함여부와 상영순번과 할인순번의 일치여부를 통해 구현할 수 있으므로 각각 할인조건 충족여부 인터페이스를 구현하였다.
- 고객이 Screening에게 예매를 요청하면, Screening은 Movie에게 영화예매 금액을 요청한다.
- Movie는 DiscountPolicy에 의해 할인된 요금을 계산하여 실제 티켓 가격을 Screening에 제공한다.
- Screening은 예매 인원수와 티켓가격을 곱하여 고객에게 예매정보를 제공한다.
- Money Class의 경우 로직과는 무관한 값 타입이므로 구현내용은 생략했다.
느낀점
평소 개발할 때 추상클래스와 인터페이스 선택에 일부 갈등이 생길때가 많았다.
(그 외에는 모두 인터페이스로 구현)
로직을 설계할 때 퍼블릭 인터페이스를 어떻게 구성할까에 대해 고민을 많이 했었다.
(내부 구현을 캡슐화하는것은 예전부터 많이 진행해서 익숙해지긴 했다.)
하지만 이번 챕터를 공부하면서 객체보다 메시지가 우선시 되야만 필요한 책임이 명확하게 인터페이스로 구성될 수 있음을 깨달았다.
이전에는 퍼블릭 인터페이스로 선언했다가 개발하다보니 불필요한 부분을 다시 내부 구현으로 감추는 등 개발하면서 설계가 일부 변경된 경험이 있다.
예전에 토끼책을 먼저 봤기 때문인지 그때 공부했던 내용들이 많이 중복되는 부분도 있긴하다.
일부 내용은 아직 아리까리하다.
(책임과 행동의 구분? 메시지!=메서드라면 여기서 말하는 메서드는 내부 구현메서드를 말하는것인지?)
Object (조영호 저) 도서를 통해 학습한 내용을 정리하였습니다.