본문 바로가기
Study/디자인 패턴의 아름다움

[디자인 패턴의 아름다움] 6. 생성 디자인 패턴

by Nahwasa 2024. 5. 17.

스터디 메인 페이지

목차

    - ☆ 표시가 붙은 부분은 스터디 중 나온 얘기 혹은 제 개인적인 생각이나 제가 이해한대로 적어놓은 것으로, 책에 나오지 않는 내용입니다. 따라서 책에서 말하고자 하는 바와 다를 수 있습니다.

    - 모든 이미지의 출처는 디자인패턴의 아름다움(왕정 저) 책 입니다.

     


     

    CHAPTER 06. 생성 디자인 패턴

    • 생성 디자인 패턴은 주로 객체 생성 문제를 해결하고 복잡한 생성 프로세스를 캡슐화하며 객체의 생성 코드와 사용 코드를 분리한다.
    • 싱글턴 패턴 : 전역적으로 유일한 객체를 생성하는 데 사용
    • 팩터리 패턴 : 같은 상위 클래스나 인터페이스를 상속하는 하위 클래스와 같이 비록 유형은 다르지만 서로 관련되어 있는 객체를 주어진 객체 타입에 맞게 생성하는 데 사용
    • 빌더 패턴 : 복잡한 객체를 생성하는 데 사용되는데, 서로 다른 선택적 매개변수를 설정하여 다양한 객체를 사용자 정의할 수 있다.
    • 프로토타입 패턴 : 기존 객체를 복사하는 방법을 사용하여 생성 비용이 높은 객체를 생성하는 시간을 절약한다.

     

    6.1 싱글턴 패턴 (1)

    • 어떤 클래스의 객체 또는 인스턴스를 단 하나만 생성할 수 있다면, 해당 클래스는 싱글턴 클래스이며, 이 디자인 패턴을 싱글턴 패턴이라고 한다.
    • 싱글턴 패턴의 구현 : ☆ 이 책에 나온 내용과 비슷한걸 정리해둔게 있다. '자바 싱글톤 패턴의 변화 (다양한 싱글톤 패턴 구현 방법)'
    • 싱글턴 패턴의 단점
      • 클래스 간의 의존성을 감춘다. : ☆ 특히 스프링의 경우 어차피 component들은 기본적으로 싱글턴으로 동작한다. 따라서 개인적으론 의존성 감춘걸 풀기 위해 라이브러리에서 싱글턴을 쓴 경우 component로 한번 래핑해서 사용하는걸 선호한다.
      • 테스트 용이성에 영향을 미친다.
      • 매개변수가 있는 생성자를 지원하지 않는다. (☆ 사용못한다기 보다는, 매개변수가 의미 없다.)
    • 싱글턴 패턴의 대안
      • 정적 메서드
      • 의존성 주입

     

     

    6.3 팩터리 패턴 (1)

    • 객체 생성과 사용이 섞여있는 경우, 클래스의 단일 책임을 명확히 하기 위해 별도의 클래스에 생성을 분리해 객체 생성만 담당하도록 할 수 있다. 이렇게 생성되는 클래스가 단순 팩터리 패턴이 적용된 팩터리 클래스이다. (== 정적 팩터리 메소드, 정적 팩터리 패턴)
    • 생성을 위한 코드를 가끔씩만 수정한다면 개방 폐쇄 원칙을 완전히 만족하지 않아도 괜찮다.
    • if 분기가 매우 많은 것이 아니라면 if 분기가 있어도 무방하다. (☆ 너무 SOLID 이런거 생각하지 말고, 트레이드 오프이니 적당하다면 그냥 그대로 해도 된다는 얘기이다.)
    • 이 때, 분기 판단 논리 대신 다형성을 사용한 팩터리 메서드 패턴으로 변경하여 if 분기 판단 논리를 제거할 수 있다. (☆ 근데 어차피 생성을 위해 switch나 if 등의 코드가 들어가게 된다. 이 경우 사용하는 쪽은 OCP를 지킬 수 있어도 생성부는 switch나 if문을 수정할 수 밖에 없다. 참고로 당연하게도 완전히 만족하도록 짤 수도 있는데(예를들어 옵저버 패턴 등을 적용 / 스프링부트에서도 마찬가지로 OCP를 완전히 만족하도록 주입 및 적용 가능하다.), 역시 당연하게도 코드가 복잡해진다. 즉, 트레이드 오프이다.)
    • 객체 생성 과정이 복잡한 경우 객체 생성 프로세스를 캡슐화하고 객체 생성과 사용을 분리하여 코드의 복잡성을 줄이는 팩터리 패턴 사용을 고려할 수 있다.
    • 팩터리 패턴의 적용
      • 코드에 if 분기 판단 논리가 있으며, 유형에 따라 다른 객체를 동적으로 생성하는 경우 팩터리 패턴을 사용하여 큰 객체 생성 코드를 추출해 팩터리 클래스에 넣는 것을 고려할 수 있다.
      • 각 객체의 생성 과정이 상대적으로 복잡한 경우, 팩터리 클래스가 너무 복잡해지는 것을 피하기 위해 팩터리 메서드 패턴을 사용할 수 있다.
      • 혹은 유형에 따라 다른 객체를 생성할 필요는 없지만, 단일 객체 자체의 생성 프로세스가 비교적 복잡한 경우에도 적용할 수 있다.
    • 팩터리 패턴의 역할
      • 생성 과정을 캡슐화 함으로써 생성 과정의 변경 사항이 호출자에게 투명성을 가질 수 있다.
      • 생성을 재상죵할 수 있다.
      • 생성 과정을 캡슐화하므로, 호출자는 객체 생성 방법을 알 필요가 없다.
      • 생성 과정과 사용 과정이 분리되어 복잡한 코드가 간결해진다.

     

     

    6.5 빌더 패턴

    • 생성자를 사용한 객체 생성의 경우 매개변수의 순서나 개수를 잘못 계산하면 잘못된 값을 전달하기 쉽기 때문에 숨겨진 버그가 발생할 수 있다.
    • 반면 setter를 통해 모든 값을 설정한다면 모든 값을 설정하기 위해 긴 매개변수를 전부 지정할 필요가 없어 코드의 가독성과 사용 편의성이 훨씬 향상된다.
    • 다만 이 경우 다음과 같은 문제점이 있을 수 있다.
      • setter 메서드를 통해 설정하면, 필수 항목이 설정되었는지 확인할 방법이 없다.
      • 설정 항목 사이에 의존성이 있을 수 있다. (A가 설정되어 있다면 D,Z가 필수로 설정되어야 한다 처럼)
      • 클래스의 객체가 불변 객체여야 할 경우 setter 메서드를 노출할 수 없다.
    • 위와 같은 문제점을 해결하기 위해 빌더 패턴을 사용한다.
    • 빌더 패턴을 사용하여 객체를 생성하면 잘못된 상태의 객체를 방지할 수 있다.
    • 빌더 패턴과 팩터리 패턴은 모두 객체 생성 시 사용할 수 있는데, 팩터리 패턴은 동일한 상위 클래스나 인터페이스를 상속하는 하위 클래스 그룹과 같이 유형은 다르지만 연관되어 있는 객체를 생성할 때 사용된다. 빌더 패턴은 동일한 유형의 복잡도가 높은 객체를 생성하는데, 이때 선택적인 매개변수를 설정하거나 사용자 정의를 통해 다른 객체를 생성한다.

     

     

     

     

    댓글