개발하는 프로 국밥러
article thumbnail

오브젝트

조영호님의 책 오브젝트를 보며 정리한 내용입니다.

문제가 될 시 해당글 삭제하겠습니다.


책의 내용이 반복되는 느낌이 있지만, 이름이 가진 설계 원칙을 통해 기법들을 정리하는 것은
장황하게 설명된 내용들을 정리하고, 
설계를 논의할 때 사용할 수 있는 공통 어휘를 정의한다는 점에서 의의가 있다.

 

[개방 폐쇄 원칙(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)

[의존성 주입]

  • 생성과 사용을 분리하게 되면, 생성된 객체에는 오직 인스턴스를 사용하는 책임만 남게 된다.
  • 이것은 외부의 다른 객체가, Movie 에게 생성된 인스턴스를 전달해야 한다는 것을 의미한다.
  • 이처럼 사용하는 객체가 아닌, 독립적인 객체가 인스턴스를 생성한 후, 이를 전달해서 의존성을 해결하는 방법을
    의존성 주입(Dependency Injection) 이라 부른다.
  • 의존성 주입의 방법
    • 생성자 주입(constructor injection)
      -> 가장 많이 사용(안정적인 설계, 한번 주입하게 되면 이후에 변경될 가능성이 없거나 적다)
    • setter 주입(setter injection)
      -> 의존성을 런타임에 설정할 수 있다.
      -> setter 주입은 객체가 생성된 후에 호출되어야 하기 때문에, setter 메서드를 누락한다면 비정상적인 객체가 생겨난다.
    • 메서드 주입(method injection)
      -> 주입된 의존성이 한 두개의 메서드에서만 사용된다면 각 메서드의 인자로 전달하는 것이 더 나은 방법일 수 있다.
  • 숨겨진 의존성은 나쁘다.
    • SERVICE LOCATOR 패턴을 통해서, 의존성을 해결할 객체들을 보관하는 일종의 저장소를 만들어
      DI 요청 할 수 있다.
    • 하지만!! 의존성을 감추어 객체에 필요한 의존성을 주입하지 못할수도 있다!!
      ex) 생성자를 통한 객체 생성 때 의존성을 모르고 주입하지 못해 NullPointerException 예외가 던져진다.
      => 원인은 문제점을 발견할 수 있는 시점을 코드 작성 시점이 아닌, 실행 시점으로 미루기 때문이다.

[의존성 역전 원칙]

추상화 의존성 역전

  • 중요한 정책, 의사결정, 비지니스의 본질을 담고 있는 상위 수준 클래스가 하위 수준의 클래스에 의존하게 된다면,
  • 하위 수준 변경에 따라 상위 수준 클래스가 영향을 받게 될 것이다.
    (의존성의 방향이 중요하다! / 상위수준의 클래스는 하위수준의 클래스를 의존해서는 안된다!)
  • 추상화에 의존하는 것이 객체지향설계의 모든 문제를 해결하는 시작점이 될 것이다.

의존성 역전 원칙(Dependency Inversion Principle, DIP)

  • 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안된다. 둘 모두 추상화에 의존해야 한다.
  • 추상화는 구체적인 사항에 의존해서는 안 된다. 구체적인 사항은 추상화에 의존해야 한다.

[유연성에 대한 조언]

유연한 설계는 유연성이 필요할 때만 옳다.

  • 설계의 미덕은 단순함과 명확함으로부터 나온다!
  • 유연성은 항상 복잡성을 수반한다... (불필요한 유연성은 불필요한 복잡성을 낳는다)
  • 유연성은 코드를 읽는 사람들이 복잡함을 수용할 수 있을 때만 가치가 있다.
  • 하지만 복잡성에 대한 걱정보다 유연하고 재사용이 가능한 설계의 필요성이 더 크다면 유연한 코드를 만들어라!

협력과 책임이 중요하다.

  • 설계를 유연하게 만들기 위해서는 먼저 역할, 책임, 협력에 초점을 맞추어야 한다.
  • 다양한 컨텍스트에서 협력을 재사용할 필요가 없다면 설계를 유연하게 만들 당위성도 사라진다...
  • 책임의 불균형이 심화되고 있는 상태에서 객체의 생성 책임을 지우는 것은 설계를 하부 특정한 매커니즘에 종속적으로 만들 확률이 크다.
    ex) 불필요한 SINGLETON 패턴
profile

개발하는 프로 국밥러

@gugbab2

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!