2020. 3. 18. 21:45ㆍ[공부] 독서/오브젝트
09 - 유연한 설계
코드를 수정하지 않고 새로운 동작을 추가하는 방법은 컴파일 타임 의존성을 고정시키고 런타임 의존성을 변경하는 것이다. [283p]
개방 폐쇄 원칙의 핵심은 추상화에 의존하는 것이다. [284p]
객체의 생성은 피할 수 없다.
어딘가에는 객체를 생성해야한다.
객체 생성이 문제가 아니라 부적절한 곳에 객체가 생성되는 것이 문제다.
동일한 클래스에서 객체 생성과 사용이 이루어지는 것이 문제다. [287p]
팩토리 패턴으로 생성 책임을 전가하자.
생성과 사용을 분리하기 위해 객체 생성에 특화된 객체를 팩토리라고 부른다. [289p]
팩토리는 도메인 모델에 속하지 않는 순수한 기숧의 결정이다.
팩토리와 같이 도메인과 아무런 관련 없는 객체를 순수한 가공물(Pure fabrication)이라고 부른다.[291p]
도메인 모델에서 출발해서 설계의 유연성을 위해 책임을 옮기다보면 자연스레 순수한 가공물들이 생겨나게된다. [293p]
객체가 아닌 외부의 독립적인 객체가 인스턴스를 생성하고 이를 전달해서 의존성을 해결하는 방법을 의존서 주입이라고한다.
- 생성자 주입
- Setter 주입
- 메서드 주입 (매개변수)
의존성 문제를 해결하기 위한 DI 이외의 다른 방법은 Service locator 패턴이다. [295p]
단 Service locator 의 치명적인 단점은 의존성을 숨기기 때문에 클래스 사용법을 익혀야한다는 것이다.
결국 클래스의 사용법을 익히기 위해 내부를 뒤져봐야하고 결과적으로 캡슐화가 무너진다. [298p]
결과적으로 명시적 의존성이 숨겨진 의존성보다 좋다. [299p]
하위 수준의 클래스 변경이 상위 수준의 클래스에 영향이 가서는 안된다.
상위 수준의 클래스 변경이 하위 수준의 클래스에 영향이 가야한다. [300p]
구체 클래스를 참조하기 시작하면 하위 수준의 클래스 변경이 상위 수준의 클래스에 영향이 갈 확률이 높다.
그러므로 추상화에 의존해야한다.
전통적인 패러다임에서 상위 수준 모듈이 하위 수준 모듈에 의존했다.
객체지향 패러다임에서는 상위 수준 모듈, 하위 수준 모듈 모두 추상화에 의존해야한다.
훌륭한 객체지향 설계는 의존성을 역전 시킨다.
그리고 의존성을 역전 시켜야만 유연하고 재사용 가능한 설계를 얻을 수 있다.[304p]
유연한 설계라는 말은 복잡한 설계라는 의미를 내포한다.
유연함은 단순성과 명확성의 희생으로 나온다. [305p]
불필요한 싱글톤 패턴은 객체생성에 관해 너무 이른 시기에 도입되는 경향이 있다.
객체를 생성하는 방법에대한 결정은 모든 책임이 자리 잡은 후 마지막에 하는 것이 좋다. [307p]
10 - 상속과 코드 재사용
전통적인 패러다임에서 코드를 재사용하는 법은 복붙 하는 것이다.
객체 지향에서는 코드를 재사용하기 위해 새로운 코드를 추가한다. [309p]
중복 코드는 변경을 방해한다.
중복 코드에 변경이 있다면 모든 코드를 일관되게 수정해야한다.
중복을 판단하는 기준은 변경이다.
요구사항이 변경됐을 때 중복 코드를 함께 수정해야한다면 이는 중복이다. [309p]
이미 존재하는 클래스와 유사한 클래스가 필요하다면 코드를 복사하지 말고 상속을 이용하라. [318p]
사설) 앞으로 곧 상속보다 합성을 이용하라고 말이 바뀐다.
상속을 염두하지 않고 설계된 클래스는 상속을 이용해 재사용하기 쉽지 않다. [320p]
상속을 위한 경고
- 자식 클래스에서 super 참조를 이용하면 강한 결합이 된다. 이를 피해라 [322p]
- 상속 받은 부모 클래스의 메소드가 자식 클래스의 내부 규칙을 깨트릴 수 도 있다. [326p]
- 자식 클래스가 부모 클래스를 오버라이딩 할 경우, 의도치 않게 부모 클래스가 자신의 메소드를 사용하는 방법에 따라 자식 클래스가 결합될 수 도 있다. [328p]
- 상속을 이용하게되면 변경이 필요할 때 부모, 자식 클래스의 구현을 영원히 변경하지 않거나 둘다 변경하거나 둘 중 하나다. [332p]
부모 클래스의 변경으로 인해 자식 클래스가 영향 받는 현상을 '취약한 기반 클래스 문제'라고 부른다. [323p]
상속은 높은 결합도로 인해 부모 클래스의 점진적인 개선을 어렵게 만든다.
상속을 이용할 경우 부모 클래스의 퍼블릭 인터페이스가 아닌 구현을 변경하더라도 자식 클래스가 영향을 받기 쉬워진다. [323p]
상속은 코드 재사용을 위해 캡슐화를 희생한다 [329p]
결국 추상화가 핵심이다. 추상화에 의존해야한다 [340p]
책임을 아무리 잘 분리해도 인스턴스 변수 추가는 종종 상속 계층 전반에 변경을 유발한다.
상속으로 인한 클래스 사이의 결합은 피할 수 없다.
상속은 어떤식으로든 부모, 자식클래스를 결합시킨다. [343p]
정말로 필요한 경우에만 상속을 사용하라 [345p]
11 - 합성과 유연한 설계
코드 재사용을 할 것이라면 객체 합성이 상속보다 더 좋은 방법이다. [347p]
상속 남용의 문제점 [348p]
- 부모 클래스의 버플릭 인터페이스를 모두 사용할 수 있게되서 인스턴스 상태가 불안정해진다.
- 메소드 오버라이딩이 부모 클래스에 의해 영향 받을 수 있다.
- 수정이 필요하면 부모 클래스와 자식 클래스를 동시 수정해야한다.
상속을 남용해서 하나의 기능 추가를 위해 필요 이상의 클래스가 추가되는 경우를 클래스 폭발 문제라고 부른다. [367p]
위임 vs 포워딩 [429p]
위임 : 자신이 수신한 메시지를 다른 객체에게 this 참조와 함께 동일하게 전달하는 것
포워딩 :자신이 수신한 메시지를 다른 객체에게 this 참조 없이 동일하게 전달하는 것
포워딩 메소드 : 포워딩만 하는 메소드
*) 자바에서는 부모 클래스의 메소드를 호출하면 자동으로 this 가 전달된다.
몽키 패치 : 현재 실행중인 환경에 맞춰서 동작하도록 코드를 일부만 수정, 확장하는 행위.
자바는 언어 차원에서 지원하지 않기 때문에 바이트 코드 조작이나 AOP 를 이용해서 몽키패치를 구현한다. [352p]
믹스인 : 객체를 생성할 때 코드 일부를 클래스 안에 섞어 넣어 재사용하는 기법
객체 합성이 클래스 상속보다 더 좋은 방법이다. [379p]
상속에서는 super 참조가 컴파일 시점에 결정된다.
따라서 부모, 자식 클래스의 관계를 변경할 방법이 없다.
하지만 스칼라의 트레이트에서는 super 참조를 동적으로 결정하게 할 수 있다. [383p]
12 - 다형성
상속의 목적은 코드 재사용이 아니다.
상속은 타입 계층을 구조화 하기 위해서 사용되어야한다. [389p]
다형성이란 런타임 시점에 메세지 처리를 위해 적합한 메소드를 동적으로 탐색하는 동작이다. 상속은 이를 간단하게 해줄 뿐이다. [390p]
이외의 다형성으로는 다음과 같은 것들이 있다.
- 매개변수 다형성 : 제네릭
- 포함 다형성 : 서브타입핑 (상속)
- 오버로딩
- 강제 다형성 : 자동 타입 변환
동적 메소드 탐색의 두가지 원리 [409p, 416p]
- 자동 메세지 위임 : 자신이 이해할 수 없는 메시지가 있다면 부모에게 위임한다.
- 동적인 문맥 사용 : '수신한 객체가 무엇이냐?'에 따라 다른 메소드가 사용된다.
업캐스팅으로 인해 미래의 자식 클래스와도 협력할 수 있게 된다. [407p]
클래스는 객체를 편리하게 정의하고 생성하기 위한 도구일 뿐 중요한 것은 메시지와 협력이다.
클래스 없이도 객체 사이의 협력 관계를 구축할 수 있다.
상속 없이도 다형성을 구현할 수 있다.[434p]
'[공부] 독서 > 오브젝트' 카테고리의 다른 글
오브젝트 부록 (0) | 2020.03.22 |
---|---|
오브젝트 13 ~ 15 (0) | 2020.03.19 |
오브젝트 05 ~ 08 (0) | 2020.03.16 |
오브젝트 01 ~ 04 (0) | 2020.03.15 |