
[참고 블로그]
https://techblog.woowahan.com/2502/
1장 협력하는 객체들의 공동체
■ 역할, 책임, 협력
시스템은 역할과 책임을 수행하는 객체로 분할되고, 시스템의 기능은 객체 간의 연쇄적인 요청과 응답의 흐름으로 구성된 협력으로 구현됩니다.
■ 커피 전문점을 객체지향적으로 바라보고 설계하고 구현하자
커피 전문점 도메인
- 도메인이란? 사용자들이 관심을 가지고 있는 특정 분야나 주제를 말하며 소프트웨어는 도메인에 존재하는 문제를 해결하기 위해 개발된다.
커피 주문
다음 예제의 목적은 커피 전문점에서 커피를 주문하는 과정을 객체들의 협력 관계로 구현하는 것입니다.
커피 전문점이라는 세상
1. 객체들로 구성된 작은 세상으로 바라보자
객체지향 패러다임에서 가장 중요한 것은? 당연히 객체이죠. (클래스가 아니라.. 라는 얘기를 최근에 주위 분들께 많이 들었습니다.ㅋㅋ) 그렇기 때문에 먼저, 커피 전문점을 구성하는 요소들(손님, 바리스타 등)을 객체들로 보고 커피 전문점은 그 객체들로 이루어진 작은 세상으로 생각해보겠습니다. 손님이 커피를 주문하는 이 예제를 한 줄로 요약해보면 다음과 같습니다.
→ 손님이 메뉴판에서 4가지 메뉴 항목들 중 하나를 선택해서 바리스타에게 선택한 메뉴(커피)를 주문하고, 바리스타는 커피를 제조해서 손님에게 건네줍니다.
여기서 손님, 메뉴판, 메뉴 항목들(4가지), 바리스타, 커피(4가지)가 각각 하나의 객체가 될 수 있습니다.
즉, 객체지향의 관점에서 커피 전문점이라는 도메인은 손님 객체, 메뉴 항목 객체, 메뉴판 객체, 바리스타 객체, 커피 객체로 구성된 작은 세상입니다.
커피를 주문하는 과정을 객체지향 관점에서 다시 설명하면:
→ 손님 객체는 메뉴판 객체 안에 적힌 메뉴 항목 객체들 중에서 하나를 선택해서 바리스타 객체에게 전달(주문)하는 것입니다. 그리고 바리스타 객체는 주문을 받은 메뉴에 해당하는 커피 객체를 제조하는 것이죠.
이와 같이 객체지향의 관점에서 객체들로 이루어진 커피 전문점 작은 세상을 그림으로 정리하면 아래와 같이 표현된다고 합니다. 글로만 보면서 머릿속으로 그리다가 직접 그림으로 보니까 눈에 확 들어오더라고요.

■ 객체의 역할 특징
- 여러 객체가 동일한 역할을 수행할 수 있다.
- 커피 전문점에서 여러 명의 바리스타들이 모두 '커피 제조자'라는 동일한 역할을 수행할 수 있습니다. 아침 근무 바리스타와 저녁 근무 바리스타는 다른 사람이지만, 동일한 '바리스타' 역할을 수행합니다.
- 역할은 대체 가능성을 의미한다.
- 한 바리스타가 휴가를 가더라도 다른 바리스타가 그 역할을 대체할 수 있습니다. 손님 입장에서는 누가 바리스타인지보다 '바리스타'라는 역할이 수행되는 것이 중요합니다. 마찬가지로 시스템에서는 특정 객체가 아닌 역할을 수행할 수 있는 객체라면 언제든지 대체 가능합니다.
- 각 객체는 책임을 수행하는 방법을 자율적으로 선택할 수 있다.
- 바리스타 A는 에스프레소를 추출할 때 15초를 선호하고, 바리스타 B는 18초를 선호할 수 있습니다. 두 바리스타 모두 '커피 제조'라는 책임을 수행하지만, 구체적인 방법은 각자 다르게 선택합니다. 객체는 자신만의 방식으로 책임을 이행할 자유가 있습니다.
- 하나의 객체가 동시에 여러 역할을 수행할 수 있다.
- 작은 커피 전문점에서는 바리스타가 캐셔 역할도 함께 수행할 수 있습니다. 한 명의 직원이 주문을 받고(캐셔 역할), 커피를 제조하는(바리스타 역할) 두 가지 역할을 동시에 담당하는 것처럼, 하나의 객체도 상황에 따라 여러 역할을 수행할 수 있습니다.
■ 객체가 갖춰야 할 덕목
- 협력적
- 명령에 복종하는 것이 아닌 요청에 응답하는 것
- 커피 전문점에서 손님은 바리스타에게 "아메리카노 만들어!"라고 명령하지 않고, "아메리카노 한 잔 주문할게요"라고 요청합니다. 바리스타는 이 요청에 응답하여 자신의 전문성을 발휘해 커피를 제조합니다. 객체들도 마찬가지로 서로 명령이 아닌 요청과 응답을 주고받으며 협력합니다.
- 명령에 복종하는 것이 아닌 요청에 응답하는 것
- 자율적
- 자신의 행동을 스스로 결정하고 책임진다.
- 바리스타는 주문을 받은 후 어떤 원두를 사용할지, 물의 온도는 어떻게 할지, 추출 시간은 얼마나 할지 등을 스스로 결정합니다. 바리스타는 자신의 전문 영역 내에서 자율적으로 판단하고 행동하며, 결과물인 커피의 품질에 대한 책임을 집니다.
- 자신의 상태를 직접 관리하고 상태를 기반으로 스스로 판단하고 행동할 수 있다.
- 커피 머신 객체는 자신의 물 온도, 원두량, 압력 등의 상태를 스스로 관리합니다. 물이 부족하면 알림을 표시하고, 원두가 충분하면 추출을 시작하는 등 자신의 상태에 기반해 행동합니다. 마찬가지로 모든 객체는 자신의 내부 상태를 직접 관리하고, 그 상태에 따라 스스로 판단하고 행동합니다.
- 자신의 행동을 스스로 결정하고 책임진다.
02장: 이상한 나라의 객체
■ 객체지향이란 무엇인가?
객체지향이란 무엇일까요? 많은 사람들이 클래스나 상속 같은 구현 메커니즘에 초점을 맞추지만, 객체지향의 진정한 본질은 다른 곳에 있습니다.
■ 객체지향의 네 가지 핵심 개념
- 객체지향이란 시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고, 객체를 이용해 시스템을 분할하는 방법이다.
- 자율적인 객체란 상태(state)와 행위(behavior)를 함께 지니며, 스스로 자신을 책임지는 객체이다.
- 객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다. 각 객체는 협력 내에서 정해진 역할을 수행하며, 역할은 관련된 책임의 집합이다.
- 객체는 다른 객체와 협력하기 위해 메시지를 전송하고, 메시지를 수신한 객체는 적합한 메서드를 자율적으로 선택하여 메시지를 처리한다.
■ 객체지향의 핵심은 클래스가 아니다
객체지향의 본질은 클래스가 아니라 객체 간의 협력 관계이다.
- 핵심은 적절한 책임을 수행하는 역할 간의 유연하고 견고한 협력 관계를 구축하는 것이다.
- 클래스는 단지 객체를 만드는 데 필요한 구현 메커니즘일 뿐이다.
예시: 커피 전문점의 바리스타
- 바리스타 객체는 커피를 만드는 역할을 한다.
- 여러 바리스타가 존재할 수 있지만, 이들은 동일한 역할을 수행하며, 대체 가능하다.
- 바리스타 객체는 주문을 받으면, 스스로 적절한 방법을 선택하여 커피를 만든다.
- 이처럼, 커피 전문점에서 중요한 것은 바리스타라는 '직위'(클래스)가 아니라, '커피를 만드는 역할'과 '주문을 받는 역할' 간의 원활한 협력입니다. 클래스는 이러한 역할을 수행할 객체를 만드는 데 필요한 구현 메커니즘일 뿐입니다.
■ 앨리스 객체: 상태, 행동, 식별자
이제 객체의 특성을 이해하기 위해 이상한 나라의 앨리스를 살펴봅시다.
상태와 행동의 관계
"상태를 결정하는 것은 행동이지만, 행동의 결과를 결정하는 것은 상태입니다."
앨리스가 물약을 마시는 행동을 하면(행동) 키가 작아지는 결과가 나타납니다(상태 변화). 하지만 앨리스의 현재 키(상태)에 따라 같은 행동을 해도 다른 결과가 나올 수 있습니다. 이미 너무 작아진 앨리스가 또 물약을 마신다면 결과는 달라질 테니까요.
객체의 세 가지 특성
객체는 다음 세 가지 특성을 가집니다:
- 상태(State): 앨리스의 키, 위치, 행복 정도 등
- 행동(Behavior): 물약 마시기, 케이크 먹기, 토끼 따라가기 등
- 식별자(Identity): 앨리스를 다른 캐릭터와 구별할 수 있게 하는 고유한 특성
1. 상태(State)
상태는 왜 필요할까요? 행동의 과정과 결과를 단순하게 예측하기 위해서입니다.
앨리스의 키(상태)를 알면 문을 통과할 수 있는지(행동의 결과) 쉽게 예측할 수 있습니다. 키가 10cm라면 작은 문도 통과할 수 있겠지만, 3m라면 불가능하겠죠.
프로퍼티와 속성
- 프로퍼티: 객체의 상태를 구성하는 모든 특징
- 앨리스의 프로퍼티: 키, 위치, 가지고 있는 물건, 감정 상태 등
- 속성: 객체를 구성하는 단순한 값
- 앨리스의 속성: 키 값(150cm), 현재 감정(기쁨) 등
- 링크: 다른 객체를 가리키는 참조
- 앨리스가 들고 있는 병(다른 객체)에 대한 참조
중요한 점은 객체는 자신의 상태를 스스로 관리한다는 것입니다. 앨리스가 물약을 마시면 자신의 키를 스스로 변경합니다. 다른 사람이 앨리스의 키를 마음대로 바꿀 수는 없습니다. 이것이 객체의 자율성입니다.
2. 행동(Behavior)
행동은 객체가 외부 요청에 반응하는 방식입니다.
행동의 특징
- 행동은 상태 변경을 일으킨다(부수효과) 앨리스가 케이크를 먹는 행동은 그녀의 키를 크게 만드는 부수효과를 가져옵니다.
- 행동은 외부에서 관찰 가능해야 한다 토끼가 앨리스에게 "따라와!"라고 말했을 때(메시지), 앨리스가 실제로 따라가는 모습(행동)은 외부에서 관찰할 수 있어야 합니다.
- 행동은 캡슐화를 통해 자율성을 높인다 토끼는 앨리스에게 "따라와"라고 요청만 할 뿐, 앨리스가 어떻게 움직이는지(어떤 근육을 사용하는지, 어떤 경로로 가는지)는 알 필요가 없습니다. 이런 구현 세부사항은 앨리스가 캡슐화하고 있습니다.
실생활에서도 마찬가지입니다. 커피숍에서 "아메리카노 주세요"라고 말하면 바리스타는 어떻게 만들지 스스로 결정합니다. 우리는 제조 과정의 세부사항을 알 필요가 없죠.
3. 식별자(Identity)
식별자는 객체를 고유하게 구분할 수 있게 해주는 특성입니다.
객체 vs 값
- 값(Value): 상태만으로 두 인스턴스를 비교(동등성)
- 1달러 지폐 두 장은 같은 가치를 가지므로 동등함
- 객체(Object): 식별자로 두 인스턴스를 비교(동일성)
- 똑같이 생긴 쌍둥이라도 서로 다른 사람임
예를 들어, 두 개의 동일한 아메리카노 컵이 있다면 그 내용물(상태)은 같을 수 있지만, 각각은 서로 다른 음료(객체)입니다. 한 컵을 마셔도 다른 컵은 그대로 남아있죠.
행동이 상태를 결정한다
설계를 할 때, 초보자는 상태를 먼저 결정하고 행동을 나중에 정의하는 실수를 저지른다. 하지만 이는 몇 가지 문제를 초래한다.
문제점
- 캡슐화를 저해한다.
- 상태가 노출되면, 객체 내부의 동작이 외부에 드러나고 유지보수가 어려워진다.
- 객체를 고립된 섬처럼 만든다.
- 객체는 협력을 통해 가치를 창출해야 한다. 하지만 상태 중심 설계는 객체 간의 협력을 방해한다.
- 재사용성이 저하된다.
- 특정 상태를 전제로 한 객체는 범용적으로 사용하기 어렵다.
올바른 접근법
커피 전문점을 설계한다고 생각해보세요:
- 먼저 필요한 협력을 생각합니다
- "손님이 바리스타에게 커피를 주문하는 협력"
- 그 협력에 참여하기 위해 필요한 행동을 생각합니다
- "손님: 메뉴 선택하기, 주문하기, 지불하기"
- "바리스타: 주문 받기, 커피 제조하기, 커피 제공하기"
- 행동을 수행할 객체를 선택합니다
- "손님 객체, 바리스타 객체"
- 행동에 필요한 상태를 결정합니다
- "손님: 가진 돈, 선호하는 메뉴"
- "바리스타: 제조 가능한 메뉴 목록, 기술 수준"
이렇게 협력과 행동에서 출발하면 필요한 상태가 자연스럽게 도출됩니다. 결국 객체지향 설계는 객체들이 주고받는 메시지(협력)에서 시작해야 합니다.
■ 정리
객체지향의 핵심은 자율적인 객체들이 메시지를 주고받으며 협력하는 것입니다. 객체는 상태, 행동, 식별자를 가지며, 특히 행동을 중심으로 설계해야 합니다. 클래스는 단지 이러한 객체를 만들기 위한 구현 메커니즘일 뿐, 진정한 객체지향의 핵심은 책임과 협력에 있습니다.
03장: 타입과 추상화
■ 추상화
추상화는 현실에서 출발하되, 불필요한 부분을 제거하면서 사물의 본질을 드러내는 과정이다.
- 훌륭한 추상화는 목적에 부합하는 것이어야 한다.
- 복잡한 문제를 다루기 위해 특정 세부사항을 감추거나 생략함으로써 이해를 돕고 복잡성을 극복하는 방법이다.
- 추상화는 두 가지 차원에서 이루어진다:
- 일반화(Generalization): 사물들 간의 공통점을 취하고 차이점을 버려 단순하게 만드는 것.
- 세부사항 제거: 중요한 부분을 강조하기 위해 불필요한 세부사항을 제거하는 것.
예시: 커피 전문점의 메뉴판
커피 전문점에서 메뉴판을 보면 "아메리카노, 카페라떼, 카푸치노" 등의 항목이 있다. 하지만, 우리는 "에스프레소 베이스 음료"라는 개념으로 이들을 묶을 수 있다. 즉, 각각의 메뉴는 개별적인 객체지만, 공통적인 특징을 추출하여 '에스프레소 베이스 음료'라는 개념으로 일반화할 수 있다.
■ 개념(concept)
- 객체는 특정 개념을 적용할 수 있는 구체적인 사물이다.
- 예를 들어, 앨리스가 인물들의 차이점을 무시하고 '트럼프'라는 개념으로 단순화하는 것과 같다.
- 개념이 객체에 적용되었을 때, 그 객체를 개념의 인스턴스라고 한다.
- 개념은 객체를 분류할 수 있는 일종의 체계이며, 다음과 같은 세 가지 요소로 구성된다:
- 심볼(Symbol): 개념을 가리키는 이름 (예: "자동차")
- 내연(Intension): 개념을 객체에 적용할 수 있는지 판단하는 조건 (예: 바퀴 4개, 엔진이 있음)
- 외연(Extension): 개념에 속하는 객체들의 집합 (예: BMW, 테슬라, 현대차)
분류(Classification)
- 분류란 객체에 특정한 개념을 적용하는 작업이다.
- 객체에 특정 개념을 적용하면, 그 객체는 특정 집합의 멤버로 분류된다.
예시: 동물 분류
"강아지"와 "고양이"는 다르지만, 둘 다 "반려동물"이라는 개념으로 묶일 수 있다. 따라서, 강아지와 고양이는 "반려동물"이라는 상위 개념의 인스턴스로 분류될 수 있다.
■ 타입(type)
타입이란 공통적인 행동을 공유하는 객체들을 분류하는 개념이다.
1. 행동이 우선이다.
- 객체의 타입을 결정하는 것은 객체의 행동(Behavior)이다.
- 객체가 어떤 데이터를 보유하는지는 타입을 결정하는 데 중요한 요소가 아니다.
- 이 원칙은 객체지향 패러다임을 정의하는 중요한 개념이다.
예시: 자동차와 자전거
자동차와 자전거는 "이동 수단"이라는 공통점을 가진다. 하지만 내부 구조(데이터)는 다르다. 중요한 것은 "이동한다"는 행동이며, 이동 수단이라는 타입을 정의할 때 행동을 기준으로 일반화하는 것이 바람직하다.
- 다형성(Polymorphism)
- 같은 타입에 속한 객체는 동일한 행동을 수행할 수 있다.
- 하지만 행동을 처리하는 방식은 다를 수 있다.
- 예를 들어, 자동차는 엔진을 이용해 이동하고, 자전거는 사람이 페달을 밟아 이동한다.
- 다형성은 동일한 요청에 대해 객체가 서로 다른 방식으로 응답할 수 있는 능력이다.
- 캡슐화(Encapsulation)
- 객체의 데이터(내부 표현 방식)는 감추고, 행동을 통해서만 외부에 제공해야 한다.
- 예를 들어, 자동차 객체의 내부 연료 상태를 외부에서 직접 변경할 수 없도록 하고, "주유하기"라는 행동을 통해서만 상태를 변경할 수 있도록 한다.
2. 일반화 / 특수화 관계
- 일반화(Generalization): 여러 개체의 공통점을 모아 상위 개념을 만드는 과정.
- 특수화(Specialization): 일반 개념에 특정 조건을 추가해 하위 개념을 만드는 과정.
- 일반화된 개념은 더 많은 객체를 포함할 수 있지만, 행동의 수는 적다.
- 특수화된 개념은 더 적은 객체를 포함하지만, 행동의 수는 더 많다.
예시: 동물과 포유류
- "동물"은 일반화된 개념이다.
- "포유류"는 동물보다 특수화된 개념이며, "젖을 먹인다"라는 추가적인 행동을 가진다.
- 따라서, "포유류"는 "동물"보다 더 좁은 집합이지만, 더 많은 행동을 가진다.
3. 슈퍼타입 / 서브타입
- 슈퍼타입(Supertype): 일반화된 타입.
- 서브타입(Subtype): 특수화된 타입.
- 서브타입은 슈퍼타입의 행동을 그대로 수행할 수 있어야 한다. → 행위적 호환성(Behavioral Compatibility)을 만족해야 한다.
예시: 직원과 매니저
- "직원(Employee)"은 슈퍼타입이고, "매니저(Manager)"는 서브타입이다.
- 매니저는 직원이 수행하는 모든 행동을 할 수 있지만, 추가적으로 "팀 관리"라는 행동을 가질 수 있다.
- 따라서, "매니저"는 "직원"을 대체할 수 있어야 한다. (Liskov Substitution Principle, LSP)
4. 타입의 목적: 객체 상태의 정적 표현
타입은 동적으로 변하는 객체의 상태를 정적인 관점에서 표현할 수 있게 해줍니다.
커피를 예로 들면:
- 동적 상태: 온도가 낮아지고, 양이 줄어들고, 맛이 변하는 등
- 정적 표현: '아메리카노'라는 타입으로 추상화
이처럼 타입은 변화하는 현실 세계를 프로그램 안에서 안정적으로 다룰 수 있게 해주는 추상화 장치입니다.
■ 정리
- 객체를 분류하는 기준은 타입이며, 타입을 나누는 기준은 객체가 수행하는 행동이다.
- 객체를 분류하기 위해 타입을 결정한 후 프로그래밍 언어를 이용해 타입을 구현할 수 있는 한가지 방법이 클래스이다.

[참고 블로그]
https://techblog.woowahan.com/2502/
1장 협력하는 객체들의 공동체
■ 역할, 책임, 협력
시스템은 역할과 책임을 수행하는 객체로 분할되고, 시스템의 기능은 객체 간의 연쇄적인 요청과 응답의 흐름으로 구성된 협력으로 구현됩니다.
■ 커피 전문점을 객체지향적으로 바라보고 설계하고 구현하자
커피 전문점 도메인
- 도메인이란? 사용자들이 관심을 가지고 있는 특정 분야나 주제를 말하며 소프트웨어는 도메인에 존재하는 문제를 해결하기 위해 개발된다.
커피 주문
다음 예제의 목적은 커피 전문점에서 커피를 주문하는 과정을 객체들의 협력 관계로 구현하는 것입니다.
커피 전문점이라는 세상
1. 객체들로 구성된 작은 세상으로 바라보자
객체지향 패러다임에서 가장 중요한 것은? 당연히 객체이죠. (클래스가 아니라.. 라는 얘기를 최근에 주위 분들께 많이 들었습니다.ㅋㅋ) 그렇기 때문에 먼저, 커피 전문점을 구성하는 요소들(손님, 바리스타 등)을 객체들로 보고 커피 전문점은 그 객체들로 이루어진 작은 세상으로 생각해보겠습니다. 손님이 커피를 주문하는 이 예제를 한 줄로 요약해보면 다음과 같습니다.
→ 손님이 메뉴판에서 4가지 메뉴 항목들 중 하나를 선택해서 바리스타에게 선택한 메뉴(커피)를 주문하고, 바리스타는 커피를 제조해서 손님에게 건네줍니다.
여기서 손님, 메뉴판, 메뉴 항목들(4가지), 바리스타, 커피(4가지)가 각각 하나의 객체가 될 수 있습니다.
즉, 객체지향의 관점에서 커피 전문점이라는 도메인은 손님 객체, 메뉴 항목 객체, 메뉴판 객체, 바리스타 객체, 커피 객체로 구성된 작은 세상입니다.
커피를 주문하는 과정을 객체지향 관점에서 다시 설명하면:
→ 손님 객체는 메뉴판 객체 안에 적힌 메뉴 항목 객체들 중에서 하나를 선택해서 바리스타 객체에게 전달(주문)하는 것입니다. 그리고 바리스타 객체는 주문을 받은 메뉴에 해당하는 커피 객체를 제조하는 것이죠.
이와 같이 객체지향의 관점에서 객체들로 이루어진 커피 전문점 작은 세상을 그림으로 정리하면 아래와 같이 표현된다고 합니다. 글로만 보면서 머릿속으로 그리다가 직접 그림으로 보니까 눈에 확 들어오더라고요.

■ 객체의 역할 특징
- 여러 객체가 동일한 역할을 수행할 수 있다.
- 커피 전문점에서 여러 명의 바리스타들이 모두 '커피 제조자'라는 동일한 역할을 수행할 수 있습니다. 아침 근무 바리스타와 저녁 근무 바리스타는 다른 사람이지만, 동일한 '바리스타' 역할을 수행합니다.
- 역할은 대체 가능성을 의미한다.
- 한 바리스타가 휴가를 가더라도 다른 바리스타가 그 역할을 대체할 수 있습니다. 손님 입장에서는 누가 바리스타인지보다 '바리스타'라는 역할이 수행되는 것이 중요합니다. 마찬가지로 시스템에서는 특정 객체가 아닌 역할을 수행할 수 있는 객체라면 언제든지 대체 가능합니다.
- 각 객체는 책임을 수행하는 방법을 자율적으로 선택할 수 있다.
- 바리스타 A는 에스프레소를 추출할 때 15초를 선호하고, 바리스타 B는 18초를 선호할 수 있습니다. 두 바리스타 모두 '커피 제조'라는 책임을 수행하지만, 구체적인 방법은 각자 다르게 선택합니다. 객체는 자신만의 방식으로 책임을 이행할 자유가 있습니다.
- 하나의 객체가 동시에 여러 역할을 수행할 수 있다.
- 작은 커피 전문점에서는 바리스타가 캐셔 역할도 함께 수행할 수 있습니다. 한 명의 직원이 주문을 받고(캐셔 역할), 커피를 제조하는(바리스타 역할) 두 가지 역할을 동시에 담당하는 것처럼, 하나의 객체도 상황에 따라 여러 역할을 수행할 수 있습니다.
■ 객체가 갖춰야 할 덕목
- 협력적
- 명령에 복종하는 것이 아닌 요청에 응답하는 것
- 커피 전문점에서 손님은 바리스타에게 "아메리카노 만들어!"라고 명령하지 않고, "아메리카노 한 잔 주문할게요"라고 요청합니다. 바리스타는 이 요청에 응답하여 자신의 전문성을 발휘해 커피를 제조합니다. 객체들도 마찬가지로 서로 명령이 아닌 요청과 응답을 주고받으며 협력합니다.
- 명령에 복종하는 것이 아닌 요청에 응답하는 것
- 자율적
- 자신의 행동을 스스로 결정하고 책임진다.
- 바리스타는 주문을 받은 후 어떤 원두를 사용할지, 물의 온도는 어떻게 할지, 추출 시간은 얼마나 할지 등을 스스로 결정합니다. 바리스타는 자신의 전문 영역 내에서 자율적으로 판단하고 행동하며, 결과물인 커피의 품질에 대한 책임을 집니다.
- 자신의 상태를 직접 관리하고 상태를 기반으로 스스로 판단하고 행동할 수 있다.
- 커피 머신 객체는 자신의 물 온도, 원두량, 압력 등의 상태를 스스로 관리합니다. 물이 부족하면 알림을 표시하고, 원두가 충분하면 추출을 시작하는 등 자신의 상태에 기반해 행동합니다. 마찬가지로 모든 객체는 자신의 내부 상태를 직접 관리하고, 그 상태에 따라 스스로 판단하고 행동합니다.
- 자신의 행동을 스스로 결정하고 책임진다.
02장: 이상한 나라의 객체
■ 객체지향이란 무엇인가?
객체지향이란 무엇일까요? 많은 사람들이 클래스나 상속 같은 구현 메커니즘에 초점을 맞추지만, 객체지향의 진정한 본질은 다른 곳에 있습니다.
■ 객체지향의 네 가지 핵심 개념
- 객체지향이란 시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고, 객체를 이용해 시스템을 분할하는 방법이다.
- 자율적인 객체란 상태(state)와 행위(behavior)를 함께 지니며, 스스로 자신을 책임지는 객체이다.
- 객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다. 각 객체는 협력 내에서 정해진 역할을 수행하며, 역할은 관련된 책임의 집합이다.
- 객체는 다른 객체와 협력하기 위해 메시지를 전송하고, 메시지를 수신한 객체는 적합한 메서드를 자율적으로 선택하여 메시지를 처리한다.
■ 객체지향의 핵심은 클래스가 아니다
객체지향의 본질은 클래스가 아니라 객체 간의 협력 관계이다.
- 핵심은 적절한 책임을 수행하는 역할 간의 유연하고 견고한 협력 관계를 구축하는 것이다.
- 클래스는 단지 객체를 만드는 데 필요한 구현 메커니즘일 뿐이다.
예시: 커피 전문점의 바리스타
- 바리스타 객체는 커피를 만드는 역할을 한다.
- 여러 바리스타가 존재할 수 있지만, 이들은 동일한 역할을 수행하며, 대체 가능하다.
- 바리스타 객체는 주문을 받으면, 스스로 적절한 방법을 선택하여 커피를 만든다.
- 이처럼, 커피 전문점에서 중요한 것은 바리스타라는 '직위'(클래스)가 아니라, '커피를 만드는 역할'과 '주문을 받는 역할' 간의 원활한 협력입니다. 클래스는 이러한 역할을 수행할 객체를 만드는 데 필요한 구현 메커니즘일 뿐입니다.
■ 앨리스 객체: 상태, 행동, 식별자
이제 객체의 특성을 이해하기 위해 이상한 나라의 앨리스를 살펴봅시다.
상태와 행동의 관계
"상태를 결정하는 것은 행동이지만, 행동의 결과를 결정하는 것은 상태입니다."
앨리스가 물약을 마시는 행동을 하면(행동) 키가 작아지는 결과가 나타납니다(상태 변화). 하지만 앨리스의 현재 키(상태)에 따라 같은 행동을 해도 다른 결과가 나올 수 있습니다. 이미 너무 작아진 앨리스가 또 물약을 마신다면 결과는 달라질 테니까요.
객체의 세 가지 특성
객체는 다음 세 가지 특성을 가집니다:
- 상태(State): 앨리스의 키, 위치, 행복 정도 등
- 행동(Behavior): 물약 마시기, 케이크 먹기, 토끼 따라가기 등
- 식별자(Identity): 앨리스를 다른 캐릭터와 구별할 수 있게 하는 고유한 특성
1. 상태(State)
상태는 왜 필요할까요? 행동의 과정과 결과를 단순하게 예측하기 위해서입니다.
앨리스의 키(상태)를 알면 문을 통과할 수 있는지(행동의 결과) 쉽게 예측할 수 있습니다. 키가 10cm라면 작은 문도 통과할 수 있겠지만, 3m라면 불가능하겠죠.
프로퍼티와 속성
- 프로퍼티: 객체의 상태를 구성하는 모든 특징
- 앨리스의 프로퍼티: 키, 위치, 가지고 있는 물건, 감정 상태 등
- 속성: 객체를 구성하는 단순한 값
- 앨리스의 속성: 키 값(150cm), 현재 감정(기쁨) 등
- 링크: 다른 객체를 가리키는 참조
- 앨리스가 들고 있는 병(다른 객체)에 대한 참조
중요한 점은 객체는 자신의 상태를 스스로 관리한다는 것입니다. 앨리스가 물약을 마시면 자신의 키를 스스로 변경합니다. 다른 사람이 앨리스의 키를 마음대로 바꿀 수는 없습니다. 이것이 객체의 자율성입니다.
2. 행동(Behavior)
행동은 객체가 외부 요청에 반응하는 방식입니다.
행동의 특징
- 행동은 상태 변경을 일으킨다(부수효과) 앨리스가 케이크를 먹는 행동은 그녀의 키를 크게 만드는 부수효과를 가져옵니다.
- 행동은 외부에서 관찰 가능해야 한다 토끼가 앨리스에게 "따라와!"라고 말했을 때(메시지), 앨리스가 실제로 따라가는 모습(행동)은 외부에서 관찰할 수 있어야 합니다.
- 행동은 캡슐화를 통해 자율성을 높인다 토끼는 앨리스에게 "따라와"라고 요청만 할 뿐, 앨리스가 어떻게 움직이는지(어떤 근육을 사용하는지, 어떤 경로로 가는지)는 알 필요가 없습니다. 이런 구현 세부사항은 앨리스가 캡슐화하고 있습니다.
실생활에서도 마찬가지입니다. 커피숍에서 "아메리카노 주세요"라고 말하면 바리스타는 어떻게 만들지 스스로 결정합니다. 우리는 제조 과정의 세부사항을 알 필요가 없죠.
3. 식별자(Identity)
식별자는 객체를 고유하게 구분할 수 있게 해주는 특성입니다.
객체 vs 값
- 값(Value): 상태만으로 두 인스턴스를 비교(동등성)
- 1달러 지폐 두 장은 같은 가치를 가지므로 동등함
- 객체(Object): 식별자로 두 인스턴스를 비교(동일성)
- 똑같이 생긴 쌍둥이라도 서로 다른 사람임
예를 들어, 두 개의 동일한 아메리카노 컵이 있다면 그 내용물(상태)은 같을 수 있지만, 각각은 서로 다른 음료(객체)입니다. 한 컵을 마셔도 다른 컵은 그대로 남아있죠.
행동이 상태를 결정한다
설계를 할 때, 초보자는 상태를 먼저 결정하고 행동을 나중에 정의하는 실수를 저지른다. 하지만 이는 몇 가지 문제를 초래한다.
문제점
- 캡슐화를 저해한다.
- 상태가 노출되면, 객체 내부의 동작이 외부에 드러나고 유지보수가 어려워진다.
- 객체를 고립된 섬처럼 만든다.
- 객체는 협력을 통해 가치를 창출해야 한다. 하지만 상태 중심 설계는 객체 간의 협력을 방해한다.
- 재사용성이 저하된다.
- 특정 상태를 전제로 한 객체는 범용적으로 사용하기 어렵다.
올바른 접근법
커피 전문점을 설계한다고 생각해보세요:
- 먼저 필요한 협력을 생각합니다
- "손님이 바리스타에게 커피를 주문하는 협력"
- 그 협력에 참여하기 위해 필요한 행동을 생각합니다
- "손님: 메뉴 선택하기, 주문하기, 지불하기"
- "바리스타: 주문 받기, 커피 제조하기, 커피 제공하기"
- 행동을 수행할 객체를 선택합니다
- "손님 객체, 바리스타 객체"
- 행동에 필요한 상태를 결정합니다
- "손님: 가진 돈, 선호하는 메뉴"
- "바리스타: 제조 가능한 메뉴 목록, 기술 수준"
이렇게 협력과 행동에서 출발하면 필요한 상태가 자연스럽게 도출됩니다. 결국 객체지향 설계는 객체들이 주고받는 메시지(협력)에서 시작해야 합니다.
■ 정리
객체지향의 핵심은 자율적인 객체들이 메시지를 주고받으며 협력하는 것입니다. 객체는 상태, 행동, 식별자를 가지며, 특히 행동을 중심으로 설계해야 합니다. 클래스는 단지 이러한 객체를 만들기 위한 구현 메커니즘일 뿐, 진정한 객체지향의 핵심은 책임과 협력에 있습니다.
03장: 타입과 추상화
■ 추상화
추상화는 현실에서 출발하되, 불필요한 부분을 제거하면서 사물의 본질을 드러내는 과정이다.
- 훌륭한 추상화는 목적에 부합하는 것이어야 한다.
- 복잡한 문제를 다루기 위해 특정 세부사항을 감추거나 생략함으로써 이해를 돕고 복잡성을 극복하는 방법이다.
- 추상화는 두 가지 차원에서 이루어진다:
- 일반화(Generalization): 사물들 간의 공통점을 취하고 차이점을 버려 단순하게 만드는 것.
- 세부사항 제거: 중요한 부분을 강조하기 위해 불필요한 세부사항을 제거하는 것.
예시: 커피 전문점의 메뉴판
커피 전문점에서 메뉴판을 보면 "아메리카노, 카페라떼, 카푸치노" 등의 항목이 있다. 하지만, 우리는 "에스프레소 베이스 음료"라는 개념으로 이들을 묶을 수 있다. 즉, 각각의 메뉴는 개별적인 객체지만, 공통적인 특징을 추출하여 '에스프레소 베이스 음료'라는 개념으로 일반화할 수 있다.
■ 개념(concept)
- 객체는 특정 개념을 적용할 수 있는 구체적인 사물이다.
- 예를 들어, 앨리스가 인물들의 차이점을 무시하고 '트럼프'라는 개념으로 단순화하는 것과 같다.
- 개념이 객체에 적용되었을 때, 그 객체를 개념의 인스턴스라고 한다.
- 개념은 객체를 분류할 수 있는 일종의 체계이며, 다음과 같은 세 가지 요소로 구성된다:
- 심볼(Symbol): 개념을 가리키는 이름 (예: "자동차")
- 내연(Intension): 개념을 객체에 적용할 수 있는지 판단하는 조건 (예: 바퀴 4개, 엔진이 있음)
- 외연(Extension): 개념에 속하는 객체들의 집합 (예: BMW, 테슬라, 현대차)
분류(Classification)
- 분류란 객체에 특정한 개념을 적용하는 작업이다.
- 객체에 특정 개념을 적용하면, 그 객체는 특정 집합의 멤버로 분류된다.
예시: 동물 분류
"강아지"와 "고양이"는 다르지만, 둘 다 "반려동물"이라는 개념으로 묶일 수 있다. 따라서, 강아지와 고양이는 "반려동물"이라는 상위 개념의 인스턴스로 분류될 수 있다.
■ 타입(type)
타입이란 공통적인 행동을 공유하는 객체들을 분류하는 개념이다.
1. 행동이 우선이다.
- 객체의 타입을 결정하는 것은 객체의 행동(Behavior)이다.
- 객체가 어떤 데이터를 보유하는지는 타입을 결정하는 데 중요한 요소가 아니다.
- 이 원칙은 객체지향 패러다임을 정의하는 중요한 개념이다.
예시: 자동차와 자전거
자동차와 자전거는 "이동 수단"이라는 공통점을 가진다. 하지만 내부 구조(데이터)는 다르다. 중요한 것은 "이동한다"는 행동이며, 이동 수단이라는 타입을 정의할 때 행동을 기준으로 일반화하는 것이 바람직하다.
- 다형성(Polymorphism)
- 같은 타입에 속한 객체는 동일한 행동을 수행할 수 있다.
- 하지만 행동을 처리하는 방식은 다를 수 있다.
- 예를 들어, 자동차는 엔진을 이용해 이동하고, 자전거는 사람이 페달을 밟아 이동한다.
- 다형성은 동일한 요청에 대해 객체가 서로 다른 방식으로 응답할 수 있는 능력이다.
- 캡슐화(Encapsulation)
- 객체의 데이터(내부 표현 방식)는 감추고, 행동을 통해서만 외부에 제공해야 한다.
- 예를 들어, 자동차 객체의 내부 연료 상태를 외부에서 직접 변경할 수 없도록 하고, "주유하기"라는 행동을 통해서만 상태를 변경할 수 있도록 한다.
2. 일반화 / 특수화 관계
- 일반화(Generalization): 여러 개체의 공통점을 모아 상위 개념을 만드는 과정.
- 특수화(Specialization): 일반 개념에 특정 조건을 추가해 하위 개념을 만드는 과정.
- 일반화된 개념은 더 많은 객체를 포함할 수 있지만, 행동의 수는 적다.
- 특수화된 개념은 더 적은 객체를 포함하지만, 행동의 수는 더 많다.
예시: 동물과 포유류
- "동물"은 일반화된 개념이다.
- "포유류"는 동물보다 특수화된 개념이며, "젖을 먹인다"라는 추가적인 행동을 가진다.
- 따라서, "포유류"는 "동물"보다 더 좁은 집합이지만, 더 많은 행동을 가진다.
3. 슈퍼타입 / 서브타입
- 슈퍼타입(Supertype): 일반화된 타입.
- 서브타입(Subtype): 특수화된 타입.
- 서브타입은 슈퍼타입의 행동을 그대로 수행할 수 있어야 한다. → 행위적 호환성(Behavioral Compatibility)을 만족해야 한다.
예시: 직원과 매니저
- "직원(Employee)"은 슈퍼타입이고, "매니저(Manager)"는 서브타입이다.
- 매니저는 직원이 수행하는 모든 행동을 할 수 있지만, 추가적으로 "팀 관리"라는 행동을 가질 수 있다.
- 따라서, "매니저"는 "직원"을 대체할 수 있어야 한다. (Liskov Substitution Principle, LSP)
4. 타입의 목적: 객체 상태의 정적 표현
타입은 동적으로 변하는 객체의 상태를 정적인 관점에서 표현할 수 있게 해줍니다.
커피를 예로 들면:
- 동적 상태: 온도가 낮아지고, 양이 줄어들고, 맛이 변하는 등
- 정적 표현: '아메리카노'라는 타입으로 추상화
이처럼 타입은 변화하는 현실 세계를 프로그램 안에서 안정적으로 다룰 수 있게 해주는 추상화 장치입니다.
■ 정리
- 객체를 분류하는 기준은 타입이며, 타입을 나누는 기준은 객체가 수행하는 행동이다.
- 객체를 분류하기 위해 타입을 결정한 후 프로그래밍 언어를 이용해 타입을 구현할 수 있는 한가지 방법이 클래스이다.