(책의 내용 중 일부분과 개인적으로 요약한 부분이 같이 있어 저자분의 내용과 다르게 요약되었을 수 있습니다.)
1장 "협력하는 객체들의 공동체"
객체 지향의 목표는 실세계를 모방하는 것이 아니다. 오히려 새로운 세계를 창조하는 것이다. 소프트웨어 개발자의 역할은 단순히 실세계를 소프트웨어 안으로 옮겨 담는 것이 아니라 고객과 사용자를 만족시킬 수 있는 신세계를 창조하는 것이다.
역할, 책임, 협력
역할이라는 단어는 책임이라는 개념을 내포한다.
역할에 적합한 책임을 수행한다
- 여러 사람이 동일한 역할을 수행할 수 있다 ( 여러 명의 바리스타, 여러 명의 손님 )
- 역할은 대체 가능성을 의미한다. (일하고 있던 바리스타가 일을 그만두어도 다른 사람으로 대체 가능하다)
- 책임을 수행하는 방법은 자율적으로 선택할 수 있다 ( 바리스타가 라테 아트를 하는 사람인지 하지 않는 사람인지와 같이)
- 한 사람이 동시에 여러 역할을 수행할 수 있다. ( 한 명의 캐셔가 바리스타이면서, 캐셔일 수 있다 )
흔히 객체를 상태와 행동을 함께 지닌 실체라고 정의한다.
객체의 자율성을 객체의 내부와 외부를 명확하게 구분하는 것으로부터 나온다. 객체의 사적인 부분은 객체 스스로 관리하고 외부에서 일체 간섭할 수 없도록 차단해야 하며, 객체의 외부에서는 접근이 허락된 수단을 통해서만 객체와 의사소통 해야 한다. 객체는 다른 객체가 '무엇(what)'을 수행하는지는 알 수 있지만 '어떻게(how)'수행하는지에 대해서는 알 수 없다.
객체가 수신된 메시지를 처리하는 방법을 메서드라고 부른다.
메시지와 메서드의 분리는 객체와 협력에 참여하는 개체들 간의 자율성을 증진시킨다.
객체지향의 본질
- 객체지향이란 시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분할하는 방법이다.
- 자율적인 객체란 상태와 행위를 함께 지니며 스스로 자기 자신을 책임지는 객체를 의미한다.
- 객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다. 각 객체는 협력 내에서 정해진 역할을 수행하며 역할은 관련된 책임의 집합이다.
- 객체는 다른 객체와 협력하기 위해 메시지를 전송하고, 메시지를 수신한 객체는 메시지를 처리하는데 적합한 메서드를 자율적으로 선택한다.
지나치게 클래스를 강조하는 프로그래밍 언어적인 관점은 객체의 캡슐화를 저해하고 클래스를 서로 강하게 결합시킨다. 애플리케이션을 협력하는 객체들의 공동체가 아닌 클래스로 구성된 설계도로 보는 고나 점은 유연하고 확장 가능한 애플리케이션의 구축을 방해한다.
훌륭한 객체지향 설계자가 되기 위해 거쳐야 할 첫 번째 도전은 코드를 담는 클래스의 관점에서 메시지를 주고받는 객체의 관점으로 사고의 중심을 전화하는 것이다. 중요한 것은 어떤 클래스가 필요한가 가 아니라 어떤 객체들이 어떤 메시지를 주고받으며 협력하는가 이다.
클래스의 구조와 메서드가 아니라 객체의 역할, 책임, 협력에 집중하라. 객체지향은 객체를 지향하는 것이지 클래스를 지향하는 것이 아니다.
2장 "이상한 나라의 객체"
객체와, 행동, 상태에 대한 이야기를 하였다.
상태 중심이 아닌 행동중심으로 코딩하기. (행동이 상태를 결정한다)
객체지향을 현실세계의 추상화라고도 하는데, 그 안에는 현실 세계를 모방해서 단순화한다는 의미가 숨어있다. 추상화하는 것은 실제의 사물에서 자신이 원하는 특성만 취하고 필요 없는 부분을 추려 표현하는 것이고 실제 객체지향 세계는 현실 세계의 단순한 모방이 아니다.
객체지향은 현실세계를 오롯이 모방한다는 것은 오해이고 객체지향 세계는 현실을 은유하여 표현한다. 결론적으로는 객체지향 설계자로서 현실을 모방하는 것이 아닌 단지 이상한 나라를 새로 창조하는 것이다.
3장 "타입과 추상화"
객체지향의 핵심은 추상화이다. 추상화란, 현실 세계에서 출발하지만 필요한 정보만을 추출하여 단순화하는 것이다.
분류와 일반화는 추상화를 위한 도구이다. 이를 통해 객체를 단순화하고, 객체 간의 관계를 명확하게 정의할 수 있다.
분류는 객체를 특정 그룹으로 분류하는 것이다. 예를 들어, 사람을 여성과 남성으로 분류하거나, 동물을 포유류와 조류로 분류할 수 있다. 분류를 통해 객체를 그룹화하고, 이를 기반으로 객체 간의 관계를 파악할 수 있다. 객체를 잘 설계하여야 유지보수가 편리한 애플리케이션을 설계할 수 있다. 최대한 직관적으로 설계하자.
일반화는 객체 간의 공통점을 찾아내서 이를 상위 개념으로 일반화하는 것이다. 이를 통해 객체 간의 관계를 더욱 명확하게 정의할 수 있다. 예를 들어, 새와 곤충은 모두 날개를 가지고 있으므로, 이들을 날개를 가진 동물로 일반화할 수 있다.
객체지향에서 가장 중요한 것은 동적으로 변하는 객체의 '상태'와 상태를 변경하는 '행위'이다.
애플리케이션을 설계할 때는 동적인 관점과 정적인 관점을 모두 고려해야 한다.
객체가 외부에 제공해야 하는 행동을 먼저 생각하여, 이 행동에 따라 객체를 분리할 수 있다
객체를 결정하는 것은 행동이다. 데이터는 단지 행동을 따르기 위한 수단일 뿐이며, 이것이 객체를 객체답게 만드는 가장 핵심적인 원칙이다. 객체를 데이터 중심으로 설계하면, 객체의 상태가 변경되었을 때 이에 따른 행동을 처리하기 어렵게 된다. 따라서 객체의 행동에 초점을 맞추어 객체를 설계해야 한다.(DDD -> RDD를 고안한 이유)
4장 : 역할, 책임, 협력
개별적인 객체의 행동이나 상태가 아니라 객체들 간의 협력에 집중하라.
크레이그 라만 "객체지향 개발에서 가장 중요한 능력은 책임을 능숙하게 소프트 웨어 객체에 할당하는 것이다"
역할 : 책임의 집합 (바리스타 (역할) : 커피를 내린다, 원두를 관리한다. 손님(역할): 커피를 산다, 커피를 마신다와 같은)
역할은 협력 내에서 다른 객체로 대체할 수 있음을 나타내는 일종의 표식이다. 협력 안에서 역할은 " 이 자리는 해당 역할을 수행할 수 있는 어떤 객체라도 대신할 수 있습니다"라고 말하는 것과 같다.
역할은 설계의 단순성, 유연성, 재사용성을 뒷받침하는 핵심 개념이다.
역할을 이용하여 협력을 추상화 함으로써 단순화 할 수 있다.
객체가 역할을 대체 가능하기 위해서는 협력 안에서 역할이 수행하는 모든 책임을 동일하게 수행할 수 있어야 한다.
- 객체지향 설계 기법
책임 주도 설계 (RDD)
디자인 패턴 : 좋은 디자인 패턴을 알고 활용할 수 있으면 RDD를 활용하는 것과 마찬가지이다.
테스트 - 주도 개발 (TDD)
5장 : 책임과 메시지
객체지향 설계의 아름다움은 적절한 책임을 적절한 객체에게 할당하는 과정 속에 있다.
책임은 협력에 참여하는 의도를 명확하게 설명할 수 있는 수준 안에서 추상적이어야 한다.
자율적인 객체란 스스로의 의지와 판단에 따라 각자 맡은 책임을 수행하는 객체를 의미한다.
사실 객체가 다른 객체에게 접근할 수있는 유일한 방법은 요청을 전송하는 것뿐이다. 그리고 이 요청을 우리는 메시지라고 부른다. 메시지는 객체로 하여금 자신의 책임 즉 행동을 수행하게 만드는 유일한 방법이다.
요약하면 메시지는 객체들이 서로 협력하기 위해 사용할 수 있는 유일한 의사소통 수단이다.
메시지를 처리하기 위해 내부적으로 선택하는 방법을 메서드라고 한다.
다형성은 역할, 책임, 협력과 깊은 관련이 있다.
송신자 관점에서 다형적인 수신자들을 구별할 필요가 없으며 자신의 요청을 수행할 책임을 지닌다는 점에서 모두 동일하다.
기본적으로 다형성은 대체 가능성을 의미한다.
다형성은 객체들의 대체 가능성을 이용해 설계를 유연하고 재사용 가능하게 만든다. 또한, 수신자의 종류를 캡슐화한다.
객체 지향이 유연하고 확장가능한, 그리고 재사용성이 높다는 명성을 가지게 된 것을 다형성이라는 강력한 무기가 있기 때문이다.
[유용하고 확장가능하며 재사용성이 높은 협력의 의미]
1. 협력이 유연해진다.
2. 협력이 수행되는 방식을 확장할 수 있다.
3. 협력이 수행되는 방식을 재사용할 수 있다.
주요한 포인트는 메시지이다. 메시지를 통해서 송신자와 수신자 사이의 결합도를 낮춤으로써 설계를 유연하고, 확장 가능하고 , 재사용 가능하게 만든다. 또한, 메시지가 수신자의 책임을 결정한다. 협력이라는 문맥 안에서 객체의 책임을 결정하는 것은 메시지다.
클래스는 단지 동적인 객체들의 특성과 행위를 정적인 텍스트로 표현하기 위해 사용할 수 있는 추상화 도구일 뿐이다. 중요한 것은 클래스가 아니라 객체다.
훌륭한 객체지향 설계는 어떤 객체가 어떤 메시지를 전송할 수 있는가와 어떤 객체가 어떤 메시지를 이해할 수 있는가를 중심으로 객체 사이의 협력관계를 구성하는 것이다.
"묻지 말고 시켜라" 객체는 다른 객체의 결정에 간섭하지 말아야 하고 묻지 않아야 한다. 즉 "묻지 말고 시켜라"는 객체를 자율적으로 만들고 캡슐화를 보장하며 결합도를 낮게 유지시켜 주기 때문에 설계를 유연하게 한다.
메시지를 믿어라. 그러면 자율적인 책임은 저절로 따라올 것이다.
인터페이스 : 어떤 두 사물이 마주치는 경계 지점에서 서로 상호작용할 수 있게 이어주는 방법이나 장치를 의미한다.
1. 사용법을 익히기만 하면 내부 구조나 동작방식을 몰라도 쉽게 사용할 수 있다.
2. 인터페이스가 변경되지 않고 내부구조만 변경되는 경우 인퍼페이스 사용자에게 아무런 영향이 없다.
3. 인터페이스가 동일하기만 하다면 어떤 대상과도 상호작용 할 수 있다.
훌륭한 객체란 구현을 모른 채 인터페이스만 알면 쉽게 상호작용할 수 있는 객체를 의미한다.
➡️ 인터페이스와 구현의 분리 원칙
객체 내부의 수정이 외부에 영향을 미쳐서는 안된다. (객체의 상태와 메서드는 객체의 내부이다.)
객체의 외부와 내부를 병확하게 구분해라
책임이 자율적일 수록 적절하게 '추상화'되며 , '응집도'가 높아지고, '결합도'가 낮아지며 '캡슐화'가 증진되고 '인터페이스와 구현이 명확히 분리'되면 설계의 '유연성'과 '재사용성'이 향상된다.
객체 지향의 강력함을 누리기 위한 출발점은 책임을 자율적으로 만드는 것이다.
6장 : 객체 지도
안정적인 구조를 중심으로 기능을 종속시키는 접근법은 벙용적이고 재사용 가능하며 변경에 유연하게 대처할 수 있는 모델을 만든다.
즉, 객체지향은 자주 변경되는 기능이 아니라 안정적인 구조를 기반으로 시스템을 구조화한다.
이번 장에서는 기능이 아니라 구조를 바탕으로 시스템을 분할하는 객체지향의 또 다른 측면에 관해 설명한다.
훌륭한 설계자는 사용자가 만족할 수 있는 훌륭한 기능을 제공하는 동시에 예측 불가능한 요구사항 변경에 유연하게 대처할 수 있는 안정적인 구조를 제공하는 능력을 갖춰야 한다.
미래에 대비하는 가장 좋은 방법은 변경을 예측하는 것이 아니라 변경을 수용할 수 있는 선택의 여지를 설계에 마련해 놓는 것이다.
객체지도는 빠르게 변화하는 기능을 수용할 수 있는 자리를 제공한다. 객체 지도는 안정적이며 재사용 가능하면서도 범용적이다. 사람들에게 길을 묻지 마라. 객체를 이용해 지도를 만들어라. 기능은 지도에 표시된 길을 따라 자연스럽게 흘러갈 것이다.
객체지향 세계를 구축하기 위해서는 사용자에게 제공할 '기능'과 기능을 담을 안정적인 '구조'라는 재료가 준비돼 있어야 한다.
기능을 수집하고 표현하기 위한 기법 ❯ 유스케이스 모델링
구조를 수집하고 표현하기 위한 기법 ❯ 도메인 모델링(소프트웨어에 대한 멘탈 모델이다)
도메인 모델
객체와 현실 객체 사이의 관계를 가장 효과적으로 표현할 수 있는 단어는 은유다.
은유를 통해 투영해야 하는 대상 ❯ 사용자가 도메인에 대해 생각하는 개념들
사용자 모델에 포함된 개념과 규칙은 비교적 변경될 확률이 적기 때문에 사용자 모델을 기반으로 설계와 코드를 만들면 변경에 쉽게 대처할 수 있을 가능성이 커진다.
안정적인 구조를 제공하는 도메인 모델을 기반으로 소프트웨어 구조를 설계하면 변경에 유연하게 대응할 수 있는 탄력적인 소프트웨어를 만들 수 있다. 도메인 모델은 여러분이 기능을 구현할 때 참조할 수 있는 궁극적인 지도다.
유스케이스
사용자의 목표를 달성하기 위해 사용자와 시스템 간에 이뤄지는 상호작용의 흐름을 텍스트로 정리한 것을 유스케이스 라고 한다.
(이에 대한 5가지 특성: 텍스트이다. 여러 시나리오의 집합이다. 단순한 수행 가능 목록과 다르다. 사용자 인터페이스와 관련된 세부 정보를 포함하지 말아야 한다. 유스케이스는 내부 설계와 관련된 정보를 포함하지 않는다.)
유스케이스 안에 도메인 모델을 구축할 수 있는 모든 정보가 포함돼 있다는 착각에 빠지지 말기 바란다. 유스케이스 안에는 영감을 불러 일으킬 수 있는 약간의 힌트만이 들어있을 뿐이다.
안정적인 도메인 모델을 기반으로 시스템의 기능을 구현하면 시스템의 기능이 변경되더라도 비즈니스의 핵심 정책이나 규칙이 변경되지 않는 한 전체적인 구조가 한 번에 흔들리지 않는다.
객체지향은 연결완전성 그리고 이것의 역방향도 성립한다.
"안정적인 도메인 모델을 기반으로 시스템의 기능을 구현하라. 도메인 모델과 코드를 밀접하게 연관시키기 위해 노력하라. 그것이 유지보수하기 쉽고 유연한 객체지향 시스템을 만드는 첫걸음이 될 것이다.
7장 : 함께 모으기
개념관점 : 도메인 안에 존재하는 개념과 개념들 사이의 관계를 표현한다.
명세관점 : 사용자의 영역인 도메인을 벗어나 개발자의 영역인 소프트웨어로 초점이 옮겨진다. ❯ 객체의 인터페이스를 바라보게 된다.(객체가 협력을 위해 무엇을 할 수 있는가) 객체의 인터페이스를 바라보는
구현관점 : 객체들이 책임을 수행하는 데 필요한 동작하는 코드를 작성하는 것이다. ❯ 객체의 책임을 어떻게 수행할 것이냐.
클래스는 세 가지 관점을 모두 수용할 수 있도록 개념, 인터페이스, 구현을 함께 드러내야 한다.
명세 관점 :각 객체를 협력이라는 문맥에서 떼어내어 수신 가능한 메시지만 추려내면 객체의 인터페이스가 된다.
구현 관점 :인터페이스를 식별한 이후에 오퍼레이션을 수행하는 방법을 메서드로 구현한다.
코드의 세가지 관점
개념 관점 :
클래스가 보인다. 도메인 개념의 특성을 최대한 수용하면 변경을 관리하기 쉽고 유지보수성을 향상할 수 있다.
소프트웨어 클래스와 도메인 클래스 사이의 간격이 좁으면 좁을수록 기능을 변경하기 쉬어 뒤적거려야 하는 코드의 양도 점점 줄어든다.
명세 관점 :
클래스의 인터페이스를 바라본다.
객체의 인터페이스는 수정하기 어렵다는 사실을 명심하라. 최대한 변화에 안정적인 인터페이스를 만들기 위해서는 인터페이스를 통해 구현과 관련된 세부사항이 드러나지 않게 해야 한다.
구현관점 :
클래스의 내부 구현을 바라본다.
클래스의 메서드와 속성은 구현에 속하며 공용 인터페이스의 일부가 아니다.
메서드와 속성은 클래스 내부의 비밀이다. 외부의 클래스는 자신이 협력하는 다른 클래스의 비밀 때문에 우왕좌왕해서는 안된다.
클래스 설계 시에 하나의 클래스 안에 세 가지 관점을 모두 포함하면서도 각 관점에 대응되는 요소를 명확하고 깔끔하게 드러내자.
인터페이스와 구현을 분리하라
명세관점은 클래스의 안정적인 측면을 드러내야 한다.
구현 관점은 클래스의 불안정한 측면을 드러내야 한다.
명세관점이 설계를 주도하게 되면 설계의 품질이 향상될 수 있다는 사실을 기억하라.
캡슐화를 위반해서 구현을 인터페이스 밖으로 노출해서도 안되고 인터페이스와 구현을 명확하게 분리하지 않고 흐릿하게 섞어 놓아서도 안된다.
결국 세 가지 관점 모두에서 클래스를 바라볼 수 있으려면 훌륭한 설계가 뒷밤침 되어야 하는 것이다.
부록 추상화 기법
객체지향 프로그래밍 언어를 이용해 타입을 구현하는 가장 보편적인 방법은 클래스를 이용하는 것이다. 여기서 '타입을 구현한다'라고 표현한 이유는 클래스와 타입이 동일한 개념이 아니기 때문이다
클래스는 객체가 공유하는 본질적인 속성을 정의한다.
어떤 타입이 다른 타입의 서브타입이 되기 위해서는 is-a 규칙을 준수해야 한다고 말한다.
서브타입은 슈퍼타입으로 행위적으로 대체 가능해야한다. ( 행위적인 순웅을 리스코프 치환 원칙이라고 한다)
상속은 서브 타이핑과 서브클래싱의 두가지 용도로 사용된다.
서브클래스가 슈퍼클래스를 대체할 수 있는 경우 이를 서브 타이핑이라 한다. ❯❯ 상속, 설계의 유연성을 위하여
서브클래스가 슈퍼클래스를 대체할 수 없는 경우 서브 클래싱 이라 한다. ❯❯ 구현상속, 코드 중복을 줄이기 위하여
객체와 객체 사이의 전체-부분관계를 구현하기 위해서는 합성 관계를 사용한다.
합성 관계가 내부에 포함된 객체들의 존재를 감충으로써 내부구조를 추상화하는 것처럼 패키지는 내부에 포함된 클래스들을 감춤으로써 시스템의 구조를 추상화한다.
댓글