객체지향 사고 프로세스
기본정보
- 저자 : 맷 와이스펠
- 도서링크
- 출판사 : 제이펍
- 완독일 : 24년 1월 6일
문헌노트
p.xx,
UML 같은 모델링 언어를 배우는 과정도 중요하지만, 그보다는 먼저 객체지향 기술을 배우는 게 훨씬 더 중요하다. 객체지향 개념을 완전히 이해하기도 전에 UML 을 배운다는 것은, 전기에 대해 전혀 알지 못한 채로 전기 회로도를 읽는 방법을 배우는 것과 비슷하다.
p.2,
객체지향 개념을 목록으로 나타내면 다음과 같다.
- 캡슐화
- 상속
- 다형성
- 합성
p.6,
제대로 설계된 객체지향 모델이라면 전역 데이터가 전혀 없을 것이라고 말할 수 있다. 이로 인해 객체지향 시스템에서는 데이터 무결성 data integrity 이 달성된다.
p.10,
객체는 객체지향 프로그램의 빌딩블록이다. 객체지향 기술을 사용하는 프로그램은 기본적으로 객체들의 모음 collection 인 것이다.
p.38,
구조적 개발과 객체지향 개발이 공존한다. 실제로 객체지향 애플리케이션을 작성할 때 모든 곳에서 구조적 구문을 사용한다.
p.39,
강력한 객체지향 설계를 구축하는 비결 중 하나는 인터페이스 interface 와 구현부 implementation 의 차이점을 이해하는 것이다. 따라서 클래스를 설계할 때 사용자가 알아야 할 사항, 그리고 아마도 더 중요한 건 사용자가 몰라야 할 사항을 잘 구분해 둬야 한다는 점이다.
p.44,
최소화한 인터페이스 minimal interface 를 결정하는 한 가지 방법은 처음에는 사용자에게 공개 인터페이스를 제공하지 않는 것이다. 물론 그런 클래스는 쓸모가 없지만, 이로 인해 사용자가 다시 와서 ‘이 기능이 필요하다’고 말할 수 밖에 없게 한다. 그러면 여러분은 그와 협상할 수 있을 것이다. 따라서 요청이 있을 때만 인터페이스를 추가하자. 사용자에게 무언가가 필요하다고 미리 가정하지 마라.
p.69,
클래스 한 개로부터 여러 객체를 인스턴스화할 수 있다. 이러한 각 객체에는 고유한 ID와 상태가 있다. 이게 핵심이다. 각 객체는 개별적으로 생성되며, 자체 메모리에 할당된다. 그러나 일부 속성 및 메서드는 올바르게 선언된 경우에 동일한 클래스에서 인스턴스화된 모든 객체가 공유할 수 있으므로 이러한 클래스 속성 및 메서드에 할당된 메모리를 공유할 수 있다.
p.76,
오버로딩은 강력한 메커니즘이다. 그러나 코드를 읽는 사람들이나 유지보수하는 사람들에게 혼동만 안겨줄 수 있다. 실제로도 개발자들이 이런 연산자들을 헷갈려 하기도 한다. 이런 연산자 오버로딩을 극단적으로 하게 된다면 덧셈 연사자를 뺄셈을 수행하는데 쓰게 오버로딩을 할 수도 있을 것이다.
p.97,
클래스를 설계할 때 가장 중요한 문제는 공개 인터페이스 public interface 를 최소한으로 유지하는 것이다. 클래스를 만들기 위한 목적은 유용하고 간결한 것을 제공하는 데 있다. 길버트와 맥커티는 저서인에서 ‘잘 설계된 객체의 인터페이스는 클라이언트가 원하는 서비스를 설명한다’고 언급했다. 사용자에게 유용한 서비스를 제공하지 않는 클래스라면 처음부터 빌드할 필요는 없다.
p.98,
사용자에게 영향을 미치지 않고 구현부가 변경될 수 있는 경우라야 클래스가 최대로 유용해진다. 기본적으로 구현부를 변경해도 사용자의 애플리케이션 코드를 변경할 필요가 없다. 다시 말하지만 행위를 바꿀 수 있게 하는 가장 좋은 방법은 인터페이스와 컴포지션 composition 을 사용하는 것이다
p.105,
범위를 최대한 작게 유지하면 추상화와 구현부 은닉이 자연스럽게 이뤄진다. 가능한 많은 속성과 행위를 지역화 localization 를 하자는 것이다. 이런 식으로, 클래스를 유지하고 테스트하고 확장하는 편이 훨씬 쉽다. 인터페이스를 사용하면 이런 면이 더 강화된다.
p.107,
대부분의 설계 및 프로그래밍 함수와 마찬가지로 반복적인 과정 iterative process 을 겪는 게 바람직하다. 이것은 최소한의 인터페이스를 제공한다는 개념과 잘 어울린다. 기본적으로, 이 말은 ‘모든 코드를 한 번에 작성하지 말아야 한다 Don’t write all the code at once! 는 뜻이다. 코드 크기를 작게 해서 작성한 다음에 각 단계별로 코드를 빌드하고 테스트하자. 테스트 계획을 잘 짜 놓으면 인터페이스가 충분하지 않은 영역을 빨리 찾아낼 수 있다. 이런 식으로 클래스에 적절한 인터페이스가 있을 때까지 과정을 반복할 수 있다.
p.131,
상속과 합성은 객체지향 시스템 설계에서 중요한 역할을 한다. 실제로 가장 어렵고 흥미로운 설계 결정 중 많은 부분이 상속과 합성 사이에서 결정된다.
p.137,
Dog 상속 트리에서 Dog 클래스는 맨 위에 있으므로 가장 일반적인 범주를 나타낸다. 다양한 품종별 GoldenRetriever, LhasaApso, Basenji 클래스가 가장 구체적이다. 상속이라는 개념을 설명하자면 상속이란 공통된 성질을 배제해 나가면서 일반화에서 특수화로 나아가는 일이다.
p.138,
여러분이 개 사육사이고 모든 개를 추적하는 시스템을 계약했다고 가정해보자. 짓는 개와 하울링하는 개를 포함하는 시스템 모델을 잘 작동한다. 그러나 여러분은 하울링을 하는 어떤 개도 기르지 않는다고 가정하자. 아마도 여러분은 하울링을 하는 개와 짖는 개를 구별하는 복잡성을 포함할 필요가 없을 것이다. 이렇게 하면 시스템을 덜 복잡하게 하면서도 필요한 기능성을 제공할 수 있다.
…
하이에나 및 여우와 같은 개과 canines 에 속한 그 밖의 동물까지 포함하도록 Dog 시스템에 대한 설계를 확장하겠는가? 동물원 사육사라면 이런 설계까지 고려해야 하겠지만, 길들여진 개만 길러 판매하는 경우라면 Canine 클래스를 확장하지 않아도 될 것이다.
p.143,
지나치게 많은 합성을 하면 더 복잡해진다. 충분히 표현할 수 있을 만큼 세밀한 표현이 가능한 객체 모델을 만드는 일과 너무 세밀해서 이해하고 유지하기 어려운 모델을 만드는 일 사이에 미세한 경계선이 존재한다.
p.143,
길버트와 맥커티는 캡슐화를 ‘프로그램을 이루고 있는 각 클래스를 인터페이스와 임플리멘테이션이라는 두 가지 뚜렷한 부분으로 나누는 과정’이라고 정의한다. 이런 정의는 이 책에서 계속 주장하는 바와 같다.
p.144,
슈퍼클래스에서 구현부를 상속한 후에 해당 구현부를 변경해 버리면 이러한 슈퍼클래스 내의 변경 내용이 클래스 위계구조를 통해 파급된다는 점이다. 이렇게 줄줄이 전파되는 효과는 잠재적으로 모든 서브클래스에 영향을 미친다.
p.146,
많은 사람들이 다형성 polymorphism 을 객체지향 설계의 초석이라고 생각한다. 완전히 독립적인 객체를 만들 목적으로 클래스를 설계하는 것이 객체지향의 핵심이다. 잘 설계된 시스템에서 객체는 그것에 관한 모든 중요한 질문에 답할 수 있어야 한다. 일반적으로 객체는 스스로 어떤 역할을 해야한다. 이 독립성은 코드 재사용의 기본 메커니즘 중 하나다.
p.155,
객체지향 지지자들이 홍보한 주요 장점 중 하나는 코드를 처음에 제대로 작성해 놓은 후에는 마음껏 재사용할 수 있다는 점이다. 이런 주장은 어느 정도만 사실이다. 모든 설계 방식과 마찬가지로 코드의 효용성과 재사용 가능성은 코드를 설계하고 구현한 수준에 따라 달라진다. 객체지향 설계를 해야만 코드를 재사용할 수 있는 것은 아니다.
p.159,
추상 클래스는 구현부가 없는 메서드가 한 개 이상 들어 있는 클래스다.
p.167,
상속은 엄격한 is-a 관계가 있을 때 사용하지만, 인터페이스는 그다지 엄격하지 않은 관계에도 사용할 수 있다. 예를 들면 이렇다.
- 개는 포유동물의 일종이다(is-a)
- 파충류는 포유류의 일종이 아니다(is not a)
따라서 파충류 Reptile 클래스는 Mammal 클래스에서 상속할 수 없다. 그러나 인터페이스는 다양한 클래스를 초월한다. 예를 들면 이렇다.
- 개에 이름을 지어줄 수 있다(nameable)
- 도마뱀에 이름을 지어줄 수 있다(nameable)
p.188,
가능한 한 간단하게 유지한다는 규칙이다. 큰 소프트웨어 시스템이 제대로 작동하게 하고 유지보수를 쉽게 할 수 있으려면 더 작고 관리하기 쉬운 부분으로 나누어야 한다. 노벨상 수상자인 허버트 사이먼 Herbert Simon 은 1962년에 집필한 Architecture of Complexity 라는 기사에서 안정적인 시스템에 대해 의견을 말했다.
- 복잡하지만 안정된 시스템은 보통 위계구조라는 형태를 취하는데, 각 시스템은 더 단순한 하부 시스템을 사용해 구축되며, 각 하부 시스템은 여전히 그보다 더 단순한 하부 시스템으로부터 구축된다.
- 안정적이면서도 복잡한 시스템은 거의 다 분해해 볼 수 있다.
- 복잡하지만 안정된 시스템은 거의 항상 서로 다른 조합으로 배열된 몇 가지 종류의 자식시스템으로만 합성된다.
- 작동 중인 안정 시스템은 거의 항상 작동해 왔던 단순한 시스템에서 발전했다.
p.192,
응집체 Aggregates 는 다른 객체들을 가지고 합성한 복합 객체다. 결합체 Associates 는 한 객체가 그 밖의 객체들로부터 서비스 받기를 원할 때 사용된다.
p.222,
상속을 사용하는 방법에 초점을 둔 이유는 묶임 coupling 문제와 관련이 있다. 상속 사용에 대한 논쟁은 거의 재사용성과 확장성 및 다형성에 관해서이지만, 사실상 상속은 클래스들 간에 의존체 dependency 가 있게 하는 꼴이므로 문제를 일으킬 수 있다. 이러한 의존체들로 인해 잠재적으로 유지보수할 때나 테스트할 때에 문제가 생길 수 있다.
p.228,
경험 법칙 중 하나를 들자면, 다형성이 꼭 필요한 상황에서만 상속을 사용하라는 것이다. 따라서 Shape 에서 상속된 Circle 와 Rectangle 은 상속을 합법적으로 사용하는 것일 수 있다. 반면에 걷기와 날기와 같은 행위는 상속에 대한 좋은 후보가 아닐 수 있다. 예를 들어 Dog 에서 fly() 메서드를 무시하려면 명백한 옵션은 no-op (아무것도 하지 않음) 이다.
p.235,
로버트 마틴 Robert Martin 에 따르면 객체지향이 우리가 생각하는 방식과 밀접하다고 말하는 것은 그저 마케팅에 불과하다고 한다. 그 대신에 그는 객체지향이란 핵심 의존성들을 역전시킴으로써 경직된 코드나 취약한 코드 및 재사용이 불가능한 코드가 되지 않게 하는 식으로 의존체들을 관리하는 일이라고 말한다.
p.253,
상속보다는 합성이라는 개념이 탄력을 받는 주요 이유 중 하나는 상속 위계구조가 필요하지 않다는 점이다. 여러분은 위계구조 모델로부터 하위 형식을 만드는 대신에 하위 형식을 합성한다.
감상평
좋은 설계 원칙에 대한 조언을 많이 얻을 수 있었다.
인터페이스의 도입여부 등 회사내에서 옳다고 여겨지는 것과 차이가 있는 부분도 있었다.
객체지향 언어에 대한 경험을 쌓는 것이 중요하다고 생각하게 되었다.