오브젝트
조영호님의 책 오브젝트를 보며 정리한 내용입니다.
문제가 될 시 해당글 삭제하겠습니다.
책의 내용이 반복되는 느낌이 있지만, 이름이 가진 설계 원칙을 통해 기법들을 정리하는 것은
장황하게 설명된 내용들을 정리하고,
설계를 논의할 때 사용할 수 있는 공통 어휘를 정의한다는 점에서 의의가 있다.
[개방 폐쇄 원칙(Open-Close Principle, OCP)]
- 객체는 확장에 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다!
- 확장에 대해서 열려 있다 : 애플리케이션 변경사항이 생겼을 때, 변경에 맞게 새로운 동작을 추가할 수 있다.
- 수정에 대해 닫혀 있다 : 기존 코드를 수정하지 않고도 애플리케이션 동작을 추가 변경이 가능하다.
- 추상화가 핵심이다
- 개방 폐쇄 원칙의 핵심은 추상화에 의존하는 것이다.
- 추상화는 확장을 가능하게 하고 추상화에 대한 의존은 폐쇄를 가능하게 한다.
- 추상화를 했다고 해서 모든 수정에 대해 설계가 폐쇄되는 것은 아니다.
[생성 사용 분리]
- 메시지를 전송하지 않고, 객체를 생성하기만 했다면 문제가 되지 않는다.
- 객체를 생성하기만 하고, 메시지를 전송하기만 했다면 문제가 되지 않는다.
- 문제는 동일한 클래스 안에서 객체 생성과 사용이라는 두 가지 이질적인 목적을 가진 코드가 공존하는 것이 문제이다.
- 이 때, 두가지 역할을 하는 객체를 생성과 사용의 목적의 두 객체로 분리해야 한다.(생성 사용 분리)
FACTORY 추가하기
- 객체 생성과 관련된 지식이 Client 와 협력하는 클라이언트에게까지 새어나가기를 원하지 않는다고
가정할 때 필요한 패턴!
public class Factory{
public 객체이름 create(){
return new 객체이름(1,"test1");
}
public class Client {
private Factory factory;
public Client (Factory factory){
this.factory = factory;
}
public 객체이름 get(){
객체이름 instance = factory.create();
return avatar.get...();
}
}
순수한 가공물에게 책임 할당하기
- 시스템을 객체로 분해하는 두가지 방식
- 표면적 분해(representational decomposition)
- 도메인에 존재하는 사물 또는 개념을 표현하는 객체들을 이용해서 시스템을 분해하는 것
- 도메인과 소프트웨어 사이의 표현적 차이를 최소화하는 것을 목적으로 한다.
- 도메인 개념을 표현하는 객체에게 책임을 할당하는 것만으로 부족한 경우가 발생하게 되는데,
이 때 설계자가 편의를 위해서 임의로 만들어낸 가공의 객체에게 책임을 할당해서 문제를 해결한다. - 이처럼 도메인과 무관한 인공적인 객체를 PURE FABRICATION(순수한 가공물) 이라고 부른다
=> 이런 측면에서 객체지향이 실세계의 모방이라는 말은 옳지 않다!!
- 행위적 분해(behavioral decomposition)
- 표면적 분해(representational decomposition)
[의존성 주입]
- 생성과 사용을 분리하게 되면, 생성된 객체에는 오직 인스턴스를 사용하는 책임만 남게 된다.
- 이것은 외부의 다른 객체가, Movie 에게 생성된 인스턴스를 전달해야 한다는 것을 의미한다.
- 이처럼 사용하는 객체가 아닌, 독립적인 객체가 인스턴스를 생성한 후, 이를 전달해서 의존성을 해결하는 방법을
의존성 주입(Dependency Injection) 이라 부른다. - 의존성 주입의 방법
- 생성자 주입(constructor injection)
-> 가장 많이 사용(안정적인 설계, 한번 주입하게 되면 이후에 변경될 가능성이 없거나 적다) - setter 주입(setter injection)
-> 의존성을 런타임에 설정할 수 있다.
-> setter 주입은 객체가 생성된 후에 호출되어야 하기 때문에, setter 메서드를 누락한다면 비정상적인 객체가 생겨난다. - 메서드 주입(method injection)
-> 주입된 의존성이 한 두개의 메서드에서만 사용된다면 각 메서드의 인자로 전달하는 것이 더 나은 방법일 수 있다.
- 생성자 주입(constructor injection)
- 숨겨진 의존성은 나쁘다.
- SERVICE LOCATOR 패턴을 통해서, 의존성을 해결할 객체들을 보관하는 일종의 저장소를 만들어
DI 요청 할 수 있다. - 하지만!! 의존성을 감추어 객체에 필요한 의존성을 주입하지 못할수도 있다!!
ex) 생성자를 통한 객체 생성 때 의존성을 모르고 주입하지 못해 NullPointerException 예외가 던져진다.
=> 원인은 문제점을 발견할 수 있는 시점을 코드 작성 시점이 아닌, 실행 시점으로 미루기 때문이다.
- SERVICE LOCATOR 패턴을 통해서, 의존성을 해결할 객체들을 보관하는 일종의 저장소를 만들어
[의존성 역전 원칙]
추상화 의존성 역전
- 중요한 정책, 의사결정, 비지니스의 본질을 담고 있는 상위 수준 클래스가 하위 수준의 클래스에 의존하게 된다면,
- 하위 수준 변경에 따라 상위 수준 클래스가 영향을 받게 될 것이다.
(의존성의 방향이 중요하다! / 상위수준의 클래스는 하위수준의 클래스를 의존해서는 안된다!) - 추상화에 의존하는 것이 객체지향설계의 모든 문제를 해결하는 시작점이 될 것이다.
의존성 역전 원칙(Dependency Inversion Principle, DIP)
- 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안된다. 둘 모두 추상화에 의존해야 한다.
- 추상화는 구체적인 사항에 의존해서는 안 된다. 구체적인 사항은 추상화에 의존해야 한다.
[유연성에 대한 조언]
유연한 설계는 유연성이 필요할 때만 옳다.
- 설계의 미덕은 단순함과 명확함으로부터 나온다!
- 유연성은 항상 복잡성을 수반한다... (불필요한 유연성은 불필요한 복잡성을 낳는다)
- 유연성은 코드를 읽는 사람들이 복잡함을 수용할 수 있을 때만 가치가 있다.
- 하지만 복잡성에 대한 걱정보다 유연하고 재사용이 가능한 설계의 필요성이 더 크다면 유연한 코드를 만들어라!
협력과 책임이 중요하다.
- 설계를 유연하게 만들기 위해서는 먼저 역할, 책임, 협력에 초점을 맞추어야 한다.
- 다양한 컨텍스트에서 협력을 재사용할 필요가 없다면 설계를 유연하게 만들 당위성도 사라진다...
- 책임의 불균형이 심화되고 있는 상태에서 객체의 생성 책임을 지우는 것은 설계를 하부 특정한 매커니즘에 종속적으로 만들 확률이 크다.
ex) 불필요한 SINGLETON 패턴
'아키텍처 > OOP' 카테고리의 다른 글
[오브젝트] Chapter12 다형성 (0) | 2022.11.17 |
---|---|
[오브젝트] Chapter10, 11 상속과 코드 재사용 / 합성과 유연한 설계 (0) | 2022.11.09 |
[오브젝트] Chapter8 의존성 관리하기 (2) | 2022.11.04 |
[오브젝트] Chapter7 객체 분해 (0) | 2022.11.03 |
[오브젝트] Chapter6 메시지와 인터페이스 (0) | 2022.11.03 |