2. 객체지향 프로그래밍

00. 선요약

  • 객체지향이란 말 그대로 객체를 지향한다는 것. 클래스가 아닌 객체에 초점을 맞춰야 한다.
  • 객체의 경계를 구분(access modifier 사용하여)함으로서, 객체에 자율성을 보장할 수 있다.
  • 추상화와 구체화의 트레이드오프를 잘 계산하고 상황에 따라 적절한 방식을 선택해야 한다.
  • 상황에 따라 상속과 합성을 적절히 선택해서 사용해야 한다.

01. 영화예매 시스템

요구사항 살펴보기

사용자는 영화 예매 시스템을 이용해 쉽고 빠르게 보고싶은 예매를 할 수 있다.

  • 용어의 정의

    • 영화
    • 상영
  • 할인액을 결정하는 규칙

    • 할인 조건
    • 할인 규칙
  • 예시

    영화 할인정책 할인조건
    아바타 금액 할인정책
    할인액 800원
    순번조건
      - 조조상영
    순번조건
      - 10회 상영
    기간조건
      - 월 10:00 - 12:00 상영 시작
    기간조건
      - 목 18:00 - 21:00 사이 상영시작

  • 예매시

    제목 아바타
    싱영정보 2019.12.26(목)
    7회 18:00-20:00
    인원 2
    정가 20,000
    결제금액 18,400

02. 객체지향 프로그래밍을 향해

협력 객체 클래스

  • 객체지향이란 말 그대로 객체를 지향한다는 것
  • 클래스가 아닌 객체에 초점.
    • 어떤 객체가 필요한지 고민
      • 어떤 객체들이 어떤 상태와 행동을 가지는 지 결정
      • 그러면 클래스의 윤곽을 잡을 수 있음
    • 객체를 독립적인 존재가 아니라 기능을 구현하기 위해 협력하는 일원으로 봐야 함
      • 설계를 유연하고 확장가능하게 해 준다.
      • 훌륭한 협력이 훌륭한 객체가, 훌륭한 객체가 훌륭한 클래스가 된다.

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

도메인이란 프로그램을 사용하는 분야를 말한다.
영화 예매 프로그램을 구성하는 개념과 관계를 표현하면 아래와 같다. 도메인구조

  • 영화는 여러 번 상영될 수 있다.
  • 상영은 여러 번 될 수 있다.
  • 영화에는 할인정책을 할당하지 않거나 하나만 할당할 수 있다.
  • 할인 정책이 존재하면 반드시 하나 이상의 할인조건이 존재함.
  • 할인정책의 종류에는 금액 할인 정책, 비율 할인 정책이 존재한다.
  • 할인조건의 종류로 순번조건과 기간조건이 존재

도메인 구조를 이용하여 클래스 구조를 만들어보면 아래와 같다. 도메인구조

  • 도메인 개념을 사용하여 클래스를 구현
  • 도메인 개념과 동일, 유사하게 클래스 이름을 짓는다.
  • 클래스 사이의 관계도 도메인 개념과 유사하게

클래스 구현하기

  • 인스턴스 변수의 가시성은 private로, 메서드의 가시성은 public로 설정하여 클래스의 경계를 구분한다.
    • 경계를 구분함으로서, 객체에 자율성을 보장할 수 있다.

자율적인 객체

객체는 상태와 행동을 함께 가진 복합적인 존재 객체는 쓰로 판단하고 행동하는 자율적인 존재. 데이터와 기능을 객체 내부에 함께 묶어 캡슐화를 시킴 객체 내부에 대해 접근을 통제(외부의 간섭을 최소화)하여 개체를 자율적인 존재로 만들 수 있음.

프로그래머의 자유

프로그래머의 역할을 클래스 작성자와 클라이언트 프로그래머로 구분하는 것이 유용함.
구현은닉을 사용해 둘을 구분할 수 있는데, 이를 통해

클라이언트 프로그래머는 내부의 구현은 무시한 채 인터페이스만 알고 있어도 클래스를 사용할 수 있기 때문에 머리에 담아둬야 하는 지식의 양을 줄일 수 있고
클래스 작성자는 인터페이스를 바꾸지 않는 한 외부에 미치는 영향을 걱정하지 않고도 내부 구현을 마음대로 변경할 수 있다. 변경을 관리하기 위해 설계가 필요하다.

협력하는 객체들의 공동체

객체지향의 장점은 객체를 이용해 도메인의 의미를 풍부하게 표현할 수 있음. 의미를 좀 더 명시적이고 분명하게 표현 가능. 하나의 인스턴스 변수만 포함하더라도 개념을 명시적으로 표현하는 것은 전체적인 설계의 명확성과 유연성을 높이는 첫걸음.

객체지향 프로그램을 개발 하다 보면 원하는 기능을 구현 하기 위해 많은 객체들이 상호작용(협력) 한다. 객체지향 프로그램을 작성할 때는 먼저 협력의 관점에서 어떤 객체가 필요한지 결정하고, 객체들의 공통 상태와 행위를 구현하기 위해 클래스를 작성함.

03. 할인 요금 구하기

할인 요금 계산을 위한 협력 시작하기

추상화를 통한 상속과 다형성으로

할인 정책과 할인 조건

할인 정책은 금액 할인 정책과 비율 할인 정책으로 구분된다. 두 가지 할인 정책을 각각 AmountDiscountPolicy, PercentDiscountPolicy 클래스로 구현할 것인데, 두 클래스는 대부분 코드가 유사하고 할인 요금을 계산하는 방식만 조금 다르다. 이때 공통된 부분을 DiscountPolicy라는 클래스에 두고, 이 클래스를 AmountDiscountPolicy, PercentDiscountPolicy에서 상속받게 한다.(Template Method 패턴)

할인 정책 구성하기

04. 상속과 다형성

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

의존성 : 어떤 클래스가 다른 클래스에 접근할 수 있는 경로를 가지거나 해당 클래스의 객체의 메서드를 호출할 경우 두 클래스 사이에 의존성이 존재한다고 말한다

discountpolicy_상속계층 위 클래스 다이어그램은 DiscountPolicy의 상속계층을 나타낸다.
Movie 클래스는 영화 요금을 계산하기 위해 AmountDiscountPolicy, PercentDiscountPolicy의 인스턴스에 의존 해야 하지만 이 다이어그램을 보면 Movie 클래스는 DiscountPolicy 클래스에만 연결되어 있다.

컴파일 시간(코드 수준)에는 DiscountPolicy에 의존하지만 실행 시간 에는 AmountDiscountPolicy, PercentDiscountPolicy 클래스에 의존하게 된다. 확장 가능한 객체지향 설계가 가지는 특징 이다.

코드 수준 의존성과 실행시간의 의존성이 다르면 다를수록 더 유연해 지고 확장 가능 가능성이 커지지만 코드 수준 의존성과 실행시간의 의존성이 다르면 다를수록 코드를 이해하기가 어려워 지지고 디버깅하기 점점 더 어려워 진다.

차이에 의한 프로그래밍

부모 클래스와 다른 부분만 추가해서 새로운 클래스를 쉽고 빠르게 만드는 방법을 차이에 의한 프로그래밍이라 한다.

상속과 인터페이스

상속을 통해 자식 클래스는 부모 클래스가 제공하는 모든 인터페이스를 뮬려받을 수 있다. 결과적으로 자식 클래스는 뷰모 클래스가 수신할 수 있는 모든 메시지를 수신할 수 있기 때문에 외부 클래스 에서는 자식 클래스를 부모 클래스와 동일한 타입으로 간주할 수 있다.(자식 클래스가 부모 클래스를 대신하는 것을 업캐스팅이라 한다)

다형성

앞선 DiscountPolicy 관련 클래스 다이어그램에서 보면, Movie클래스는 DiscountPolicy 클래스에 메시지를 전달하지만 실행 시점에 실제실행되는 메서드는 Movie 클래스에서 협력하고있는 실제 클래스가 무엇인지에 따라서 달라진다.

05. 추상화와 유연성

추상화의 특징

세부적인 내용을 무시한 채 상위 정책을 쉽고 간단하게 표현 가능
추상화를 통해 상위정책을 표현했다면 기존 구조를 수정하지 않고도 새로운 기능을 쉽게 추가하고 확장할 수 있다.

추상화의 힘

  • 추상화 계층만 따로 떼어 놓고 살펴보면 요구사항의 정책을 높은 수준에서 서술할 수 있다.
  • 추상화를 이용하면 설계가 조금 더 유연해 진다.

예를 들어 영화 예매 프로그램의 영화 예매 기능을

  • 영화 예매 요금은 최대 하나의 '할인 정책'과 다수의 '할인 조건'을 이용해 계산할 수 있다.

로 표현 가능하다.
이 문장은

  • 영화의 예매 요금은 '금액 할인 정책'과 '두 개의 순서 조건, 한 개의 기간 조건'을 이용하여 계산할 수 있다.

라는 문장을 포함할 수 있다. '할인 정책'과 '할인 조건' 이라는 좀 더 추상적인 개념들을 사용하여 문장을 작성하였기 때문이다.

유연한 설계

추상화는 설계가 구체적인 상황에 결합되는 것을 방지하기 때문에 유연한 설계가 가능하다.

코드의 재사용

코드를 재사용 하기 위한 방법으로 상속과 합성이 있다. 상속이나 합성을 이용해 기능적으로 완벽히 동일한 결과물을 낼 수 있지만 많은 사람들이 상속 대신 합성을 이용한다.

상속

상속은 캡술화를 위반하고 설계를 유연하지 못하게 만들기 때문이다.

캡술화를 위반의 문제

부모 클래스에 자식 클래스가 강하게 결합 되어 있기 때문에 부모 클래스를 변경할 때 자식 클래스도 함께 변경될 확률이 높아지고, 결과적으로 상속을 과도하게 사용한 코드는 변경하기가 어려워 진다.

유연하지 못한 설계의 문제

두 번쨰 단점은 부모클래스와 자식클래스 사이의 관계가 컴파일 시점에 결정되기 때문에 실행 시점에 객체의 종류를 변경하는 것이 불가능하다.

합성

합성은 상속이 가지는 두 가지 문제를 모두 해결한다.

캡술화를 위반의 문제

인터페이스에 정의된 메시지를 통해서만 재사용이 가능하기 때문에 구현을 효과적으로 캡슐화할 수 있다.

유연하지 못한 설계의 문제

의존하는 인스턴스를 교체하는 것은 비교적 쉽기 때문에 설계가 유연해 진다.

하지만 무조건 합성을 사용해야 한다는 것은 아니다. 전체 클래스 다이어그램을 살펴보면, MovieDiscountPolicy는 합성관계로 연결돼 있고 DiscountPolicyAmountDiscountPolicy, PercentDiscountPolicy는 상속관계로 연결돼 있다. 이처럼 코드를 재사용하는 경우는 합성을 사용하는 것이 옳지만 다형성을 위해 인터페이스를 재사용 하는 경우에는 상속과 합성을 조합해 사용할 수밖에 없다.

Last Updated: 12/14/2019, 7:48:31 PM