2. 객체지향 프로그래밍

2.1. 영화예매 프로그래밍

→ skip

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

협력, 객체, 클래스

객체지향 프로그램을 작성할 때 가장 먼저 고려하는 것은 무엇인가? C++, 자비 루비, C#과 같이 클래스 기반의 객체지향 언어에 익숙한 사람이리면 가장 먼저 어떤 클래스(class)가 필요한지 고민할 것이다. 대부분의 사람들은 클래스를 결정한 후에 클래스에 어 떤 속성과 메서드가 펼요한지 고민한다. 안타깝게도 이것은 객체지향의 본질과는 거리가 멀다. 객체지향은 말 그대로 객체를 지향하는 것이다. - p. 40

→ 클래스와 객체의 차이점은 뭘까? 보통 둘을 동일한 의미로 사용하고 있지 않나?

Class and Object definition in Java

  • object: An object stores its state in fields (variables in some programming languages) and exposes its behavior through methods (functions in some programming languages).
  • class: A class is the blueprint from which individual objects are created.

객체에 초점을 맞추라

  1. 클래스보단 어떤 객체가 필요한지 고민하라.
    • 클래스는 특정 상태와 행동을 공유하는 객체를 추상화 한 것
    • 클래스를 정의하려면 특정 객체의 상태와 행동을 먼저 정의해야 함
  2. 객체는 기능 구현을 위해 협력하는 공동체의 일원으로 봐야함
    • 객체는 다른 객체와 상호작용하는 협력적인 존재임
    • 공동체의 일원으로 바라보면 설계가 유연해지고 확장 가능해진다 ??
    • 객체의 윤곽이 잡히면 클래스를 구현하라 ??

→ 클래스를 정의하는 것이 객체 정의와 다른 점이 뭘까?

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

도메인: 문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야(지식)

  • 객체지향 패러다임은 프로그램의 전 라이프사이클에서 객체 라는 동일한 추상화 기법을 사용할 수 있다.
  • 클래스의 이름은 도메인의 이름과 유사하게 명명해야 한다.
    • 프로그램의 구조를 이해하고 예상하기 쉽다.

클래스 구현하기

  • 인스턴스 가시성: 변수는 private으로, 메서드는 public 으로
  • 클래스의 경계 구분짓기
    • 객체의 자율성을 보장
    • 프로그래머에게 구현의 자유를 제공: 인터페이스는 지키되 구현은 프로그래머의 판단 하에!

자율적인 객체

  1. 객체는 상태(state)행동(behavior) 을 가지는 복합적인 존재
  2. 객체가 스스로 판단하고 행동하는 자율적인 존재
  • 절차지향 패러다임에선 데이터와 기능을 엮어 프로그램을 구성
    • 데이터와 기능은 하나의 단위로 묶이지 않는다.
    • 객체와는 다르다. 데이터와 기능은 개별적인 존재
  • 객체지향은 데이터와 기능을 객체 라는 단위로 묶는다 → 캡슐화
  • 객체지향 패러다임의 언어는 캡슐화에 더 나은 기능을 제공한다. (e.g. Java의 접근 수정자)
  • 접근 통제의 이유: 객체를 자율적인 존재로 만들기 위해
    • 객체 스스로 상태를 관리하기 위해 외부 간섭을 최소화(외부에서 임의로 상태변경 할 수 없게)
  • 캡슐화는 객체를 두 부분으로 나눈다.
    • Public Interface: 외부에서 접근 가능한 부분
    • Implementation: 내부에서만 접근 가능
    • 인터페이스와 구현의 분리 (Seperation of Interface and Implementation)

→ Spring MVC 패턴에 따라 정의된 객체는 저자가 말한 객체라고 볼 수 있을까?

  • 상태가 없다. 있으면 안된다.

프로그래머의 자유

  • 프로그래머의 역할을 둘로 나눔
    • 클래스 작성자: 새로운 데이터 타입을 프로그램에 추가
      • 클라이언트 프로그래머에게 필요한 부분만 공개하고 나머지는 감춘다
    • 클라이언트 프로그래머: 클래스 작성자가 추가한 데이터를 사용
      • 필요한 클래스들을 엮어 애플리케이션을 빠르게 구축
  • 왜 숨길까?
    • 클라이언트 프로그래머에 대한 영향도를 신경쓰지 않아도 된다. (영향이 없으니까)
    • 숨겼으니 내부 구현을 마음대로 변경할 수 있다. (퍼블릭 인터페이스만 그대로 동작한다면!)
    • → 구현의 은닉 (Implementation Hiding)

→ Java 프로그래머는 Logback의 내부 구현을 알지 못해도 사용할 수 있다. 스펙(인터페이스)대로 동작한다고 기대하고 필요한 인터페이스가 잘 동작하면 된다.

  • 설계는 변경을 관리하기 위해 필요하다.

협력하는 객체들의 공동체

  • 협력: 기능을 구현하기 위해 객체들 사이에 이뤄지는 상호작용

협력에 관한 짧은 이야기

  • 객체는 다른 객체의 공개된 행동(public interface)을 수행하도록 요청 한다.
    • 메시지를 전송 (send a message)
  • 요청받은 객체는 자율적으로 요청을 처리한 후 응답 한다.
    • 메시지를 수신 (receive a message)
  • 메서드: 수신한 메시지를 처리하기 위한 객체 자신의 방법
  • 메시지와 메서드를 구분하는 것은 중요하다. 왜??
    • 메시지와 메서드의 구분에서 다형성 이 출발한다.
    • 동적타입 언어에선 시그니쳐가 다른 메서드를 통해서 메시지에 응답할 수 있다.

2.3. 할인요금 구하기

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

  • calculateMovieFee() 의 이상한 점
    • 할인 정책(discountPolicy)을 결정하는 코드가 없다.
  • 상속(Inheritance)과 다형성(Polymorphism), 그리고 추상화(Abstraction)

할인 정책과 할인 조건

  • 추상 클래스 DiscountPolicy는 할인 여부/요금 계산의 전체 프로세스를 정의하지만 요금 계산하는 부분은 추상 메서드인 getDiscountAmount()에게 위임 → Template Method 패턴

할인 정책 구성하기

  • 클래스 생성자로 도메인의 제약을 구현
    • 올바른 상태를 가진 객체 생성을 보장한다

→ Dependency Injection 얘기는 Out of Scope?

2.4. 상속과 다형성

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

  • Movie 클래스는 DiscountPolicy 추상클래스에 의존하고 있다.

  • 실제 필요한 클래스는 구현체

  • 실행(runtime) 시에 구현체 인스턴스에 의존해야 함

  • 런타임에 협력가능한 이유?

  • 코드 의존성(compile time)과 실행시점 의존성(runtime)은 다를 수 있다. → 객체지향의 특성

    • 유연하고 쉽게 재사용 가능
    • 코드를 이해하기 어려워진다.
      • → 코드를 이해하기 위해 구현체를 헤맨적이 없는지?
  • 설계의 trade off

    • 설계가 유연해지면 코드 디버깅이 어려워 진다.
    • 유연성이 억제되면 재사용성과 확장성이 낮아진다.

차이에 의한 프로그래밍

  • 상속: 코드를 재상용하기 위해 널리 사용되는 방법
    • 관계 설정으로 부모 클래스의 속성과 행동을 포함시킬 수 있다.
    • 부모 클래스의 구현은 공유하되 다른 행동을 정의할 수 있다. (overriding)

→ 차이에 의한 프로그래밍 (programming by difference)

상속과 인터페이스

  • 상속의 장점: 부모의 모든 인터페이스를 물려받을 수 있다.
    • 사람들은 메서드나 프로퍼티 재사용 때문이라는데?
  • 인터페이스는 객체가 이해할 수 있는 메시지의 목록
  • 자식 클래스는 부모 클래스가 수신할 수 있는 모든 메시지를 수신할 수 있다.
    • 동일한 타입으로 간주할 수 있다!!!
    • 생성자 인자가 추상 클래스임에도 런타임에서 구현체를 전달할 수 있는 이유 (Upcasting)

다형성

  • 메시지와 메서드는 다르다
  • 요청자는 calculateDiscountAmount 메시지를 보내지만 연결된 객체에 따라 실행하는 메서드는 달라진다.
    • 추상클래스와 구현 클래스의 calculateDiscountAmount 메서드는 모두 다르다!
  • 다형성
    • 동일한 메시지를 수신했을 때 객체의 타입에 따라 다르게 응답할 수 있는 능력을 의미한다.
    • 컴파일 타임과 런타임의 의존성이 다르다는 것에 기반한다.
    • 다형적인 협력에 참여하는 객체들은 모두 같은 메시지를 이해할 수 있어야 한다. → 인터페이스가 동일하다
    • 메시지와 메서드를 실행시점에 바인딩 → Lazy Binding, Dynamic Binding
    • 클래스 상속이 다형성의 전부가 아니다.
  • 구현 상속과 인터페이스 상속
    • 구현 상속: 코드 재사용을 위해
    • 인터페이스 상속: 다형적인 협력을 위해
    • 구현 상속은 변경에 취약한 코드를 생성한다. → 가격변경이력...

인터페이스와 다형성

  • 순수하게 인터페이스만 공유하고 싶을 때? Java는 interface를 사용

2.5. 추상화와 유연성

추상화의 힘

  • 인터페이스에 초점을 맞춘다
  • 추상화 계층만 보면 요구사항을 높은 수준에서 서술할 수 있다.
  • 설계가 유연해진다.

유연한 설계

  • 예외케이스를 최소화하고 일관성을 유지할 수 있는 방법을 고려하라
  • 유연성이 필요한 곳에 추상화를사용하라.

추상 클래스와 인터페이스 트레이드오프

  • 설계의 트레이드 오프를 항상 염두해야 한다.

코드의 재사용

  • 상속과 합성

상속

  • 코드를 재사용할 수 있다.
  • 캡슐화를 위반한다.
  • 설계 유연성이 떨어진다. (변경이 어려워진다.)

합성

  • 인터페이스를 통해 약하게 결합된다.

질문거리

  • 클래스와 객체의 차이점은 뭘까? 보통 둘을 동일한 의미로 사용하고 있지 않나?
  • 클래스를 정의하는 것이 객체 정의와 다른 점이 뭘까?
  • Spring MVC 패턴으로 정의된 인스턴스는 저자가 말한 객체라고 볼 수 있을까?
    • 내부 상태가 없다. (있으면 안된다)