웹개발 - Back 관련/Architecture

[ GOF ] 팩토리 매서드 패턴 - Factory Method Pattern with Kotlin

정상호소인 2024. 9. 11. 22:48
Topic =  팩토리 매서드의 의도와 개념 학습 후 예제 작성해보면서 정리해보기

 

🔑 Purpose

팩토리 매서드 패턴은 객체 생성 로직을 추상화하여 작성하는 것으로 객체 생성에 대한 로직 정의와, 객체가 사용되는 로직을 분리한 것으로 SRP( 단일 책임 원칙)와 OCP( 개방/폐쇄 원칙 ) 를 따를 수 있게 된다.

 

💡 디자인 패턴들을 분리해서 이해하지 않기 → 싱글톤과 같은 디자인 패턴은 다른 패턴들을 보완하기도 하고, 탬플릿 매서드 패턴을 상위 구조로두고 객체 생성에 초점을 맞춘 하위 구조로 팩토리 매서드 구조를 이용할 수 있다.

  • 이 전 글에서 배운 탬플릿 매서드 패턴 ( 중복 알고리즘 분리하여 재사용 )과 상당히 유사한 구조로 이루어져 있다. 팩토리 패턴은 탬플릿 매서드 패턴의 응용 버전이기 때문이다. 팩토리 매서드 패턴은 중복되는 객체 생성 알고리즘을 분리할 뿐더러, 추상 메서드에서 객체를 반환함으로 유연하게 객체를 생성할 수 있도록 하는 데 중점을 둔 것이다.
  • 각각의 디자인 패턴들은 크게 행동 패턴, 생성 패턴, 구조 패턴으로 나누어 볼 수 있으며, 각각 패턴들은 주 목적을 이루기 위해서 다른 패턴들을 응용해서, 보완하여 사용한다.

 

TodoList 예제로 정리하는 팩토리 매서드 패턴 개념

 

📜요구사항

  • TodoList에 속성 ( category - 텍스트, 날짜, 선택 목록 등 ) 은 여러개가 존재할 수 있으며 앱 업데이트 시 추가될 수 있다.
  • TodoList에 속성이 추가될 때, 각 속성은 사용자가 사전에 속성 세부 설정에 따른다.
    • ➡️ 필드 변수로 사용하지 않고, 객체로 만들어서 사용해야 한다.

 

팩토리 매서드 적용 예제

 

[ Product, ConcreteProduct ]

interface Category {
    fun click()
}

class Title : Category {
    override fun click() {
        logger.info { "할 일 제목 클릭" }
    }
}

class Calendar() : Category {
    override fun click() {
        logger.info { "캘린더 날짜 지정 로직 실행!" }
    }
}

class DropdownMenu : Category {

    override fun click() {
        logger.info { "드롭다운 메뉴 설정 로직 실행!" }
    }
}

enum class CategoryList {
    CALENDAR,
    DROPDOWN_MENU,
}
  • Title Category는 별다른 상태 없이 TodoList에 기본으로 제공하는 속성으로 설정할 것이므로, 별도로 Enum, Factory 사용 X

 

[ Creator ]

private val logger = KotlinLogging.logger {  }
abstract class CategoryFactory {

    fun addCategory() : Category {

        val category = createCategory()
        logger.info { "작업 목록 추가 - [ 새로운 todoList 카테고리 생성 ] " }

        return category
    }

    protected abstract fun createCategory(): Category
}

 

[ ConcreteCreator 1. 메서드 인자로 제어 ]

class ConcreteCategoryFactory() : CategoryFactory() {

    override fun createCategory(category: CategoryList): Category {
        return when(category) {
            CategoryList.DROPDOWN_MENU -> DropdownMenu()
            CategoryList.CALENDAR -> Calendar()
        }
    }
}
  • Product 생성자 파라미터가 같거나, 둘 다 없을 경우에는 위와 같이 Product 별로 서브 클래스를 생성하지 않고 사용할 수도 있지만, 유연하게 확장 하기가 어렵다. 

 

[ ConcreteCreator 2. 서브 클래스로 제어 ]

class CalendarFactory : CategoryFactory() {

    override fun createCategory(): Category {
        return Calendar()
    }
}

class DropdownMenuFactory : CategoryFactory() {

    override fun createCategory(): Category {
        return DropdownMenu()
    }
}
  • Dropdown 카테고리는 속성을 추가할 때, 메뉴 항목을 설정하여 매서드 파라미터로 받게 되는 로직을 추가하게 된다면, 카테고리의 생성을 정의하는 단계에서는 DropdownMenuFactory와 Product인 DropdownMenu만 수정해주면 된다.

 

[ Client - 사용 단계 ]

class TodoList {

    private val categories = mutableListOf<Category>(Title())

    fun addCategory(categoryFactory : CategoryFactory) {
        val category = categoryFactory.addCategory()

        categories.add(category)
    }
}

fun main {
    val studyTodoList = TodoList()

    studyTodoList.addCategory(CalendarFactory())
    studyTodoList.addCategory(DropdownMenuFactory())
}

 

🚨정리해보기

  • 디자인 패턴 자체가 기본적으로 유지보수에 초점을 맞추기 때문에 어떤 구조로 패턴을 구현하느냐도 중요하지만, 구조 자체보다 패턴의 의도에 집중해서 의도를 잘 표현하는 것이 중요하다.
  • 팩토리 매서드 적용으로 인해, 새로운 카테고리가 추가된다고 해도, Concrete 팩토리와 새로 추가된 카테고리를 추가하는 로직만 작성해주면 되므로 유연한 확장이 가능하다.
  • 디자인 패턴은 혼합해서 사용할 수 있고, 보완할 수 있다. 다음에는 위 예제 커맨드 패턴과 싱글톤 패턴을 적용해서 사용단계에서 addCategory() 메서드를 실행할 때 마다 새로운 팩토리 객체를 생성하지 않도록 하고, 카테고리를 추가하는 로직을 커맨드 패턴으로 분리해보도록 할 것이다. 

 

 

 

 

 


 

[ A. 오늘 복습한 내용 / B. 다음에 학습할 내용 ]

A. 디자인 패턴들은 독립적으로 존재하는 것이 아니다. 서로 연결해서 사용할 수 있다.

 

B. 커맨드 패턴, 싱글톤 패턴 학습하고 이번 예제에 적용해보기

 

B. 추상 팩토리 패턴

 

 


[Reference]

 

 

JAVA 언어로 배우는 디자인 패턴 입문: 쉽게 배우는 GoF의 23가지 디자인 패턴 | 유키 히로시 | 영진

▶ 3판에서 달라진 점 ㆍ예제 프로그램을 현대 Java 언어(람다식, 확장 for문, enum형 등)로 업데이트 ㆍ이해하기 쉽게 설명하되 현대의 관점에서 예제와 설명(의존성 주입, 보안 관련 설명 등) 수정

ebook-product.kyobobook.co.kr

 

 

팩토리 메서드 패턴

/ 디자인 패턴들 / 생성 패턴 팩토리 메서드 패턴 다음 이름으로도 불립니다: 가상 생성자, Factory Method 의도 팩토리 메서드는 부모 클래스에서 객체들을 생성할 수 있는 인터페이스를 제공하지

refactoring.guru