📖
Aaron's TECH BOOK
  • Intro
    • About me
  • Lecture
    • Kubernetes
      • Begin Kubernetes
    • Kafka
      • Begin Kafka
    • Kotlin
      • TDD, Clean Code Preview
      • woowa Kotlin
    • Java
      • Multithread Concurrency
      • The Java
    • Toby
      • Toby Spring 6
      • Toby Spring Boot
    • MSA
      • 01.Micro Service
      • 02.DDD 설계
      • 03.DDD 구현
      • 04.EDA 구현
    • Spring Boot
    • Spring Batch
    • Spring Core Advanced
    • Spring DB Part II
    • Spring DB Part I
    • JPA API and Performance Optimization
    • JPA Web Application
    • JPA Programming Basic
    • Spring MVC Part 2
      • 01.Thymeleaf
      • 02.ETC
      • 03.Validation
      • 04.Login
      • 05.Exception
    • Spring MVC Part 1
      • 01.Servlet
      • 02.MVC
    • Http
      • 01.Basic
      • 02.Method
      • 03.Header
    • Spring Core
    • Study
      • Concurrency issues
      • First Come First Served
      • Performance Test
      • TDD
      • IntelliJ
  • Book
    • Kafka Streams in Action
      • 01.카프카 스트림즈
      • 02.카프카 스트림즈 개발
      • 03.카프카 스트림즈 관리
    • Effective Kotlin
      • 01.좋은 코드
      • 02.코드 설계
      • 03.효율성
    • 이벤트 소싱과 MSA
      • 01.도메인 주도 설계
      • 02.객체지향 설계 원칙
      • 03-04.이벤트 소싱
      • 05.마이크로서비스 협업
      • 06.결과적 일관성
      • 07.CQRS
      • 08.UI
      • 09.클라우드 환경
    • 몽고DB 완벽 가이드
      • I. 몽고DB 시작
      • II. 몽고DB 개발
    • Kotlin Cookbook
      • 코틀린 기초
      • 코틀린 기능
      • ETC
    • Kotlin in Action
      • 함수/클래스/객체/인터페이스
      • 람다와 타입
      • 오버로딩과 고차 함수
      • 제네릭스, 애노테이션, 리플렉션
    • Kent Beck Tidy First?
    • 대규모 시스템 설계 기초
      • 01.사용자 수에 따른 규모 확장성
      • 02.개략적인 규모 추정
      • 03.시스템 설계 공략법
      • 04.처리율 제한 장치 설계
      • 05.안정 해시 설계
      • 06.키-값 저장소 설계
      • 07.유일 ID 생성기 설계
      • 08.URL 단축기 설계
      • 09.웹 크롤러 설계
      • 10.알림 시스템 설계
      • 11.뉴스 피드 시스템 설계
      • 12.채팅 시스템 설계
      • 13.검색어 자동완성 시스템
      • 14.유튜브 설계
      • 15.구글 드라이브 설계
      • 16.배움은 계속된다
    • 실용주의 프로그래머📖
    • GoF Design Patterns
    • 도메인 주도 개발 시작하기
      • 01.도메인 모델 시작하기
      • 02.아키텍처 개요
      • 03.애그리거트
      • 04.리포지터리와 모델 구현
      • 05.Spring Data JPA를 이용한 조회 기능
      • 06.응용 서비스와 표현 영역
      • 07.도메인 서비스
      • 08.애그리거트 트랜잭션 관리
      • 09.도메인 모델과 바운디드 컨텍스트
      • 10.이벤트
      • 11.CQRS
    • Effective Java 3/E
      • 객체, 공통 메서드
      • 클래스, 인터페이스, 제네릭
    • 소프트웨어 장인
    • 함께 자라기
    • Modern Java In Action
      • 01.기초
      • 02.함수형 데이터 처리
      • 03.스트림과 람다를 이용한 효과적 프로그래밍
      • 04.매일 자바와 함께
    • Refactoring
      • 01.리펙터링 첫 번째 예시
      • 02.리펙터링 원칙
      • 03.코드에서 나는 악취
      • 06.기본적인 리펙터링
      • 07.캡슐화
      • 08.기능 이동
      • 09.데이터 조직화
      • 10.조건부 로직 간소화
      • 11.API 리팩터링
      • 12.상속 다루기
    • 객체지향의 사실과 오해
      • 01.협력하는 객체들의 공동체
      • 02.이상한 나라의 객체
      • 03.타입과 추상화
      • 04.역할, 책임, 협력
      • 05.책임과 메시지
      • 06.객체 지도
      • 07.함께 모으기
      • 부록.추상화 기법
    • Clean Code
    • 자바 ORM 표준 JPA 프로그래밍
Powered by GitBook
On this page
  • 커피 전문점 도메인
  • 설계하고 구현하기
  • 코드와 세 가지 관점
  1. Book
  2. 객체지향의 사실과 오해

07.함께 모으기

코드와 모델을 밀접하게 연관시키는 것은 코드에 의미를 부여하고 모델을 적절하게 한다.
  • 객체지향 안에 존재하는 세 가지 상호 연관된 관점

    • 개념 관점 (Conceptual Perspective) 클래스가 은유하는 개념

      • 여기서 설계는 도메인 안에 존재하는 개념과 개념들 사이의 관계를 표현

      • 관점은 사용자가 도메인을 바라보는 관점을 반영

    • 명세 관점 (Specification Perspective) 클래스의 공용 인터페이스

      • 사용자의 영역인 도메인을 벗어나 개발자의 영역인 소프트웨어로 초점이 옮겨짐

      • 명세 관점에서 프로그래머는 객체가 협력을 위해 '무엇'을 할 수 있는가에 초점

    • 구현 관점 (Implementation Perspective) 클래스의 속성과 메서드

      • 실제 작업을 수행하는 코드와 연관

      • 객체의 책임을 '어떻게' 수행할 것인가에 초점

      • 인터페이스를 구현하는 데 필요한 속성과 메서드를 클래스에 추가

커피 전문점 도메인

구성 요소(객체)

  • 메뉴판 객체, 메뉴 객체

  • 손님 객체

  • 바리스타 객체

  • 아메리카노 객체, 카푸치노 객체, 카라멜 마키아또 객체 ...

동적인 객체를 정적인 타입으로

  • 손님 타입

  • 메뉴판 타입

  • 메뉴 항목 타입

  • 바리스타 타입

  • 커피 타입

타입 간 관계

  • 메뉴 항목 타입은 메뉴판 타입에 포함(containment) or 합성(composition) 관계

  • 손님 타입과 메뉴판 타입은 연관(association) 관계

  • 손님 타입과 바리스타 타입은 연관 관계

  • 바리스타 타입과 커피 타입은 연관 관계

  • 도메인 모델을 작성하는 단계에서 초점은 "어떤 타입이 도메인을 구성하느냐와 타입들 사이에 어떤 관계가 존재하는지를 파악함으로써 도메인을 이해하는 것"

설계하고 구현하기

적절한 객체에게 적절한 책임 할당하기 훌륭한 협력을 설계하는 것이 목표 .!

커피를 주문하기 위한 협력 찾기

  • 메시지를 먼저 선택하고 그 후에 메시지를 수신하기에 적절한 객체를 선택하자.

  • 메시지를 수신할 객체는 메시지를 처리할 책임을 맡게 되고 객체가 수신하는 메시지는 객체가 외부에 제공하는 공용 인터페이스에 포함된다.

  1. "커피를 주문하라(메뉴 이름)"라는 메시지를 수신할 객체는 "손님"

    • 손님 타입의 인스턴스가 메시지를 처리할 객체

    • 손님 객체는 커피를 주문할 책임을 할당 받았다.

  2. 손님으로부터 "메뉴 항목을 찾아라(메뉴 이름)"라는 메시지를 수신할 객체는 "메뉴판 객체"

    • 메뉴 이름에 대응되는 "메뉴 항목"을 반환

  3. 손님으로부터 "커피를 제조하라(메뉴 항목)"라는 메시지를 수신할 객체는 "바리스타"

    • 손님에게 "커피"를 반환

  4. 바리스타로부터 "커피를 생성하라"라는 메시지를 수신할 객체는 "커피"

인터페이스 정리하기

  • 객체가 수신한 메시지가 객체의 인터페이스를 결정한다!

  • 객체가 어떤 메시지를 수신할 수 있다는 것은 그 객체의 인터페이스 안에 메시지에 해당하는 오퍼레이션이 존재한다는 것을 의미한다.

class Customer {
    public void order(String menuName)
}

class MenuItem {
}

class Menu {
    public MenuItem choose(String name) {}
}

class Barista {
    public Coffee makeCoffee(MenuItem menuItem) {}
}

class Coffee {
    public Coffee(MenuItem menuItem) {}
}

구현하기

// Customer의 협력 : Menu에게 menuName에 해당하는 MenuItem을 찾아달라고 요청
class Customer {
    public void order(String menuName, Menu menu, Barista barista) {
        MenuItem menuItem = menu.choose(menuName);
        Coffee coffee = barista.makeCoffee(menuItem);
        // ...
    }
}
  • 설계는 코드로 구현하는 단계에서 대부분 변경된다.

    • 협력을 구상하는 단계에 너무 오랜 시간 쏟지 말고 최대한 빨리 코드를 구현하자.

// Menu는 menuName에 해당하는 MenuItem을 찾아야 하는 책임이 있다.
class Menu {
    private List<MenuItem> items;
    
    public Menu(List<MenuItem> items) {
        this.items = items;
    }
    
    public MenuItem choose(String name) {
        for(MenuItem each : items) {
            if(each.getName().equals(name)) {
                return each;
            }
        }
        return null;
    }
}
  • 객체가 어떤 책임을 수행해야 하는지를 결정한 후에야 책임을 수행하는 데 필요한 객체의 속성을 결정하자.

  • 이것이 객체의 구현 세부 사항을 객체의 공용 인터페이스에 노출시키지 않고 인터페이스와 구현을 깔끔하게 분리할 수 있는 기본적인 방법이다.

// Barista는 MenuItem을 이용해서 커피를 제조
class Barista {
    public Coffee makeCoffee(MenuItem menuItem) {
        Coffee coffee = new Coffee(menuItem);
        return coffee;
    }
}
// Coffee는 자기 자신을 생성하기 위한 생성자를 제공
class Coffee {
    private String name;
    private int price;
    
    public Coffee(MenuItem menuItem) {
        this.name = menuItem.getName();
        this.price = menuItem.getPrice();
    }
}
public class MenuItem {
    private String name;
    private int price;
    
    public MenuItem(String name, int price) {
        this.name = name;
        this.price = price;
    }
    
    public int cost() {
        return price;
    }
    
    public String getName() {
        return name;
    }
}
  • 인터페이스는 객체가 다른 객체와 직접적으로 상호작용하는 통로다.

  • 코드를 작성해 가면서 협력을 설계하자.

코드와 세 가지 관점

코드는 세 가지 관점을 모두 제공해야 한다

  • 개념 관점

    • 소프트웨어 클래스와 도메인 클래스 사이의 간격이 좁으면 좁을수록 기능을 변경하기 위해 뒤적거려야 하는 코드의 양도 점점 줄어든다.

  • 명세 관점

    • 최대한 변화에 안정적인 인터페이스를 만들기 위해서는 인터페이스를 통해 구현과 관련된 세부 사항이 드러나지 않게 해야 한다.

  • 구현 관점

    • 메서드의 구현과 속성의 변경은 원칙적으로 외부의 객체에게 영향을 미쳐서는 안 된다.

    • 메서드와 속성이 철저하게 클래스 내부로 캡슐화돼야 한다.

훌륭햔 객체지향 프로그래머는 하나의 클래스 안에 세 가지 관점을 모두 포함하면서도 각 관점에 대응되는 요소를 명확하고 깔끔하게 드러낼 수 있다.

도메인 개념을 참조하는 이유

  • 메시지를 수신할 객체를 선택하는 첫 번째 전략은 도메인 개념 중이서 가장 적절한 것을 선택하는 것

  • 소프트웨어 클래스가 도메인 개념을 따르면 변화에 쉽게 대응할 수 있다.

인터페이스와 구현을 분리하라

Last updated 1 year ago