2021. 12. 10. 05:29ㆍ책, 1년에 100권
존 아사라프의 해답을 빌리러갔다가 신간 코너에 프로그래밍 관련 기초 서적들이 몇권 있길래 그 중 첫번째 책으로 빌려왔다.
이 책의 1쇄는 2015년 6월에 발행되었고, 작년에 5쇄가 발행되었다. 나름 IT쪽 베스트셀러인가보다.
저자는 조용호. LG, 네이버, 쿠팡, NHN 등에서 개발하였으며 지금은 카카오에서 서비스 개발에 참여중이라고 한다. 소프트웨어 개발과 관련된 경험과 정보를 공유하기 위해 '이터너티(Eternity)'라는 필명으로 블로그도 운영중이라고 한다.
읽어보니 나에겐 꾀 도움이 되었다. C언어만을 주로 해본터라 객체지향 프로그래밍에 대한 개념과 설계 방법에 대해 몰랐었는데, 이 책을 통해 그 기초를 마련할 수 있었다. 객체지향에 대해서 설명하지만 코드는 거의 나오지 않는다. 객체지향 프로그래밍의 지식이나 경험이 있으면 책을 더 깊이, 혹은 빨리 이해할 수 있겠지만, 없어도 문제 되지 않을 정도이다. 객체지향 프로그래밍을 시작하는 사람이라면 꼭한번 읽어보라고 추천해주고 싶다.
책의 머릿말에, 이 책은 사실 다른 책을 집필하고 주변사람들에 피드백을 받는 과정에서, 개념이나 생소한 용어 때문에 내용이해가 어렵다는 리뷰 때문에, 객체 지향의 기본 지식과 개괄적으로 설명하는 내용을 써가다보니 따로 책이 한권이 될정도가 되었고 그래서 따로 출판을 하게 되었다고 한다.
그래서 저자는 얘기한다. 이책은 객체지향을 다루는 다른 책이나 자료들을 읽을 때 미리 알고 있으면 도움될 만한 기본 지식을 다루는 책이라고.
그래서 찾아보았다. 이 다음에 출간한 책이 무엇인지.
2019년에 출간한 책 "오브젝트 - 코드로 이해하는 객체지향 설계" 라는 책이 있었다.
사람들 리뷰도 아주 좋다. 봐야겠다.
책 내용 정리
객체지향의 본질
- 객체지향이란 시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분할하는 방법이다.
- 자율적인 객체란 상태와 행위를 함께 지니며 스스로 자기 자신을 책임지는 객체를 의미한다.
- 객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다. 각 객체는 협력 내에서 정해진 역할을 수행하며 역할은 관련된 책임의 집합이다.
- 객체는 다른 객체와 협력하기 위해 메시지를 전송하고, 메시지를 수신한 객체는 메시지를 처리하는 데 적합한 메서드를 자율적으로 선택한다.
훌륭한 객체지향 설계자가 되기 위해 거쳐야 할 첫번째 도전은 코드를 담는 클래스의 관점에서 메시지를 주고 받는 객체의 관점으로 사고의 중심을 전환하는 것이다. 중요한 것은 어떤 클래스가 필요한가가 아니라 어떤 객체들이 어떤 메시지를 주고 받으며 협력하는가다. 클래스는 객체들의 협력 관계를 코드로 옮기는 도구에 불과하다.
핵심은 적절한 책임을 수행하는 역할 간의 유연하고 견고한 협력 관계를 구축하는 것이다. 클래스는 협력에 참여하는 객체를 만드는 데 필요한 구현 메커니즘일 뿐이다.
클래스의 구조와 메서드가 아니라 객체의 역할, 책임, 협력에 집중하라. 객체지향은 객체를 지향하는 것이지 클래스를 지향하는 것이 아니다.
객체의 상태를 구성하는 모든 특징을 통틀어 객체의 프로퍼티(property)라고 한다. 일반적으로 프로퍼티는 변경되지 않고 고정되기 때문에 '정적'이다. 반면 프로퍼티 값(proverty value)은 시간이 흐름에 따라 변경되기 때문에 '동적'이다.
객체를 구성하는 단순한 값은 속성(attribute)이라고 한다. 객체의 프로퍼티는 단순한 값인 속성과 다른 객체를 가리키는 링크(link)라는 두가지 종류의 조합으로 표현할 수 있다.
객체는 상태를 캡슐 안에 감춰둔 채 외부로 노출하지 않는다. 객체가 외부로 노출하는 것은 행동뿐이며, 외부에서 객체에 접근할 수 있는 유일한 방법 역시 행동 뿐이다.
객체는 어떤 상태에 있더라도 식별자가 있기 때문에 유일하게 식별 가능하다.
객체는 다른 객체와 협력하기 위해 존재한다. 객체의 행동은 객체가 협력에 참여하는 유일한 방법이다. 따라서 객체가 적합한지를 결정하는 것은 그 객체의 상태가 아니라 행동이다. 설계자로서 우리는 협력의 문맥에 맞는 적절한 행동을 수행하는 객체를 발견하거나 창조해야 한다. 결과적으로 우리가 애플리케이션 안에서 어떤 행동을 원하느냐가 어떤 객체가 적합한지를 결정한다. 객체의 적합성을 결정하는 것은 상태가 아니라 객체의 행동이다.
객체지향 설계는 애플리케이션에 필요한 협력을 생각하고 협력에 참여하는 데 필요한 행동을 생각한 후 행동을 수행할 객체를 선택하는 방식으로 수행된다. 행동을 결정한 후에야 행동에 필요한 정보가 무엇인지 고려하게 되며 이 과정에서 필요한 상태가 결졍된다. 따라서 객체의 행동을 결정하고 그 후에 행동에 적절한 상태를 선택하게 된다.
추상화란 현실에서 출발하되 불필요한 부분을 도려내가면서 사물의 놀라운 본질을 드러나게 하는 과정이라 할 수 있다. 추상화의 목적은 불필요한 부분을 무시함으로써 현실에 존재하는 복잡성을 극복하는 것이다. 훌륭한 추상화는 목적에 부합하는 것이어야 한다.
"현실은 복잡하다. 법칙은 단순하다. 버릴게 무엇인지 알아내라" - 리처드 파인만
추상화는 두 차원에서 이뤄진다.
첫 번째 차원은 구체적인 사물들 간의 공통점은 취하고 차이점은 버리는 일반화를 통해 단순하게 만드는 것이다.
두 번째 차원은 중요한 부분을 강조하기 위해 불필요한 세부사항을 제거함으로써 단순하게 만드는 것이다.
타입(Type)은 공통점을 기반으로 객체들을 묶기 위한 틀이다.
데이터 타입은 메모리 안에 저장된 데이터의 종류를 분류하는데 사용하는 메모리 집합에 관한 메타데이터다. 데이터에 대한 분류는 암시적으로 어떤 종류의 연산이 해당 데이터에 대해 수행될 수 있는지를 결정한다.
일반화/특수화 관계는 슈퍼타입(supertype)/섭타입(suptype)의 관계와 같다.
객체의 모양을 빚는 것은 객체가 참여하는 협력이다. 어떤 협력에 참여하는지가 객체에 필요한 행동을 결정하고, 필요한 행동이 객체의 상태를 결정한다. 개별적인 객체의 행동이나 상태가 아니라 객체들 간의 협력에 집중하라.
역할, 책임, 그리고 협력.
객체 지향 설계는 협력에 참여하기 위해 어떤 객체가 어던 책임을 수행해야 하고 어떤 객체로부터 메시지를 수신할 것인지를 결정하는 것으로부터 시작된다. 어떤 클래스가 필요하고 어떤 메서드를 포함해야 하는지를 결정하는 것은 책임과 메시지에 대한 대략적인 윤곽을 잡은 후에 시작해도 늦지 않다.
어떤 객체가 수행하는 책임의 집합은 객체가 협력 안에서 수행하는 역할을 암시한다.
역할을 이용하면 협력을 추상화함으로써 단순화 할 수 있다. 구체적인 객체로 추상적인 역할을 대체해서 동일 한 구조의 협력을 다양한 문맥에서 재사용할 수 있는 능력은 객체지향만의 힘이다.
올바른 객체를 설계하기 위해서는 먼저 견고하고 깔끔한 협력을 설계해야 한다.. 협력을 설계한다는 것은 설계에 참여하는 객체들이 주고 받을 요청과 응답의 흐름을 결정한다는 것을 의미한다. 이렇게 결정된 요청과 응답의 흐름은 객체가 협력에 참여하기 위해 수행될 책임이 된다. 객체에게 책임이 할당되고 나면 책임은 객체가 외부에 제공하게 될 행동이 된다. 이후에 그 행동을 수행하는데 필요한 데이터를 고민해야 한다. 그리고 객체가 협력에 참여하기 위해 필요한 데이터와 행동이 어느정도 결정된 후에 클래스의 구현 방법을 결정해야한다. 결과적으로 클래스와 데이터는 협력과 책임의 집합이 결정된 후에야 무대위에 등장할 수 있다.
역할, 책임, 협력의 관점에서 애플리케이션을 설계하는 유용한 세 가지 기법
1. 책임 주도 설계(Responsibility driven design)
협력에 필요한 책임들을 식별하고 적합한 객체에게 책임을 할당하는 방식
- 시스템이 사용자에 제공해야 하는 기능인 시스템 책임을 파악한다.
- 스스템 책임을 더 작은 책임으로 분할 한다.
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당한다.
- 객체가 책임을 수행하는 중에 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾는다.
- 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력하게 된다.
2. 디자인 패턴 (Design Pattern) - 전문가들이 반복적으로 사용하는 해결방법을 정의해 놓은 설계 템플릿의 모음
3. 테스트 주도 개발(Test driven developement)
구체적인 코드를 작성해나가면서 역할, 책임, 협력을 식별하고 식별된 역할 책임 협력이 적합한지 피드백 받는 것
테스트 주도 개발은 테스트를 작성하는것이 아니라 책임을 수행할 객체 또는 클라이언트가 기대하는 객체의 역할이 메시지를 수신할 때 어떤 결과를 반환하고 그 과정에서 어떤 객체와 협력할 것인지에 대한 기대를 코드의 형태로 작성하는 것이다.
객체지향의 세계에서 객체들이 서로 협력하기 위해 사용할 수 있는 유일한 방법은 메시지를 전송하는 것이다. 다른 객체와 협력할 필요가 있는 객체는 메시지를 전송하고, 메시지를 수신한 객체는 미리 정의된 방법에 따라 수신된 메시지르 처리한다. 메시지를 수신한 객체 역시 메서드를 실행하는 중에 다른 객체의 도움이 필요하다고 판단되면 적합한 객체에게 메시지를 전송한다. 객체 지향 애플리케이션의 중심 사상은 연쇄적으로 메시지를 전송하고 수신하는 객체들 사이의 협력 관계를 기반으로 사용자에게 유용한 기능을 제공하는 것이다.
객체 설계의 핵심은 객체를 두개의 분리된 요소로 분할해 설계하는 것이다. 그것은 바로 외부에 공개되는 인터페이스와 내부에 감춰지는 구현이다.
책임이 자율적일수록 적절하게 '추상화'되며, '응집도'가 높아지고, '결합도'가 낮아지며, '캡슐화'가 증진되고, '인터페이스 와 구현이 명확히 분리'되며, 설계의 '유연성'과 '재사용성'이 향샹된다. 객체지향의 강력함을 누리기 위한 출발점은 책임을 자율적으로 만드는 것이다. 그리고 이것은 여러분이 선택하는 메시지에 다라 달라진다.
스프트웨어 제품의 설계에는 두가지 측면이 존재한다. 하나는 '기능'측면의 설계이고 다른 하나는 '구조'측면 설계다. 기능 측면의 설계는 제품이 사용자를 위해 무엇을 할 수 있는지 초점을 맞춘다. 구조 측면의 설계는 제품의 형태가 어떠해야 하는지에 초첨을 맞춘다. 이 두 측면이 조화를 이루도록 만들어야 한다.
미래에 대비하는 가장 좋은 방법은 변경을 예측하는 것이 아니라 변경을 수용할 수 있는 선택의 여지를 설계에 마련해 놓는 것이다.
구조는 사용자나 이해관계자들이 도메인(Domain)에 관해 생각하는 개념과 개념들 간의 관계로 표현한다.
기능은 사용자의 목표를 만족시키기 위해 책임을 수행하는 시스템의 행위로 표현한다.
일반적으로 기능을 수집하고 표현하기 위한 기법을 유스케이스 모델이라고 하고 구조를 수집하고 표현하기 위한 기법을 도메인 모델이라고 한다.
은행은 자산을 관리하고 보호하기 위해 소프트웨어르 사용한다. 심심한 사람들은 무료한 시간을 달래기 위해 게임 소프트웨어에 열중한다. 이처럼 사용자가 프로그램을 사용하는 대상 분야를 도메인이라고 한다.
도메인 모델이란 사용자가 프로그램을 사용하는 대상 영역에 관한 지식을 선택적으로 단순화하고 의식적으로 구조화한 형태다.
유스케이스의 특성
1. 유스케이스는 사용자와 시스템간의 상호작용을 보여주는 '텍스트'다.
2. 유스케이스는 하나의 시나리오가 아니라 여러 시나리오들의 집합이다. 시나리오는 유스케이스를 통해 시스템을 사용하는 하나의 특정한 이야기 또는 경로다. 시나리오를 유스케이스의 인스턴스라고도 한다.
3. 유스케이스는 사용자 인터페이스와 관련된 세부 정보를 포함하지 말아야 한다.
4. 유스케이스는 내부 설계와 관련된 정보를 포함하지 않는다.
5. 유스케이스는 단순한 피처(Feature) 목록과 다르다 .단순한 기능을 나열한 것이 아니라 이야기를 통해 연관된 기능들을 함께 묶을 수있다는 점이다.
변경에 유연한 소프트웨어를 만들기 위해서는 유스케이스에 정리된 시스템의 기능을 도메인 모델을 기반으로 한 객체들의 책임으로 분배해야 한다.
객체가 수신한 메시지가 객체의 인터페이스를 결정한다는 사실을 기억하라. 메세지가 객체를 선택했고, 선택된 객체는 메시지를 자신의 인터페이스로 받아들인다. 각 객체를 협력이라는 문맥에서 떼어내어 수신 가능한 메시지만 추려내면 객체의 인터페이스가 된다.
다시한번 강조한다 인터페이스와 구현을 분리하라.
훌륭한 설계를 결정하는 측면은 명세 관점인 객체의 인터페이스다. 명세 관점이 설계를 주도하게 하면 설계의 품질이 향상될 수 있다는 사실을 기억하라
중요한 것은 여러분이 클래스를 봤을 때 클래스를 명세 관점과 구현 관점으로 나눠볼 수 있어야 한다는 것이다. 캡슐화를 위반해서 구현을 인터페이스 밖으로 노출해서도 안되고, 인터페이스와 구현을 명확하게 분리하지 않고 흐릿하게 섞어놓아서도 안된다.
어떤 객체의 클래스가 수신된 메시지를 이해할 수 없다면 메시지를 클래스의 부모 클래스로 위임(Delegation)한다. 만약 부모 클래스에서도 메시지를 이해할 수 없다면 자신의 부모 클래스로 다시 메시지를 위임한다. 클래스 간의 위임사슬은 계층 내의 어떤 클래스가 메시지를 처리하거나 최상위 부모 클래스에 위임될 때까지 계속 된다.
'책, 1년에 100권' 카테고리의 다른 글
전략의 탄생 (Art of Strategy) (0) | 2022.03.08 |
---|---|
객체지향 사고 프로세스 (0) | 2021.12.30 |
The Answer (0) | 2021.11.26 |
스몰토크 (0) | 2021.04.29 |
나는 왜 시간에 쫒기는가 (0) | 2021.04.26 |