Factory method pattern

Exploring the Factory Method Pattern: A design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created

September 8, 2024


팩토리 메서드 패턴이란?

팩토리 메서드 패턴은 객체 생성을 서브클래스에 위임하는 디자인 패턴입니다. 이 패턴을 사용하면 객체 생성 로직을 캡슐화하고, 클라이언트 코드와 구체적인 클래스 구현을 분리할 수 있습니다.

팩토리 메서드 패턴의 주요 구성 요소

  • 추상 팩토리 클래스 (Abstract Factory)
  • 구체적인 팩토리 클래스 (Concrete Factory)
  • 추상 제품 클래스 (Abstract Product)
  • 구체적인 제품 클래스 (Concrete Product)

코드로 보는 팩토리 메서드 패턴

주어진 코드를 바탕으로 팩토리 메서드 패턴의 구현을 살펴보겠습니다.

추상 팩토리 클래스

abstract class PaymentFactory {
    abstract fun createPayment(method: PaymentMethod): Payment

    fun process(method: PaymentMethod) {
        val payment = createPayment(method)
        payment.paramValidation()
        payment.transfer()
        payment.sendMessage()
    }
}

PaymentFactory는 추상 팩토리 클래스로, createPayment 메서드를 추상 메서드로 선언하여 서브클래스에서 구현하도록 합니다.

구체적인 팩토리 클래스

class NicePaymentFactory : PaymentFactory() {
    override fun createPayment(method: PaymentMethod): Payment {
        return when (method) {
            PaymentMethod.ONLINE -> NiceOnlinePayment()
            PaymentMethod.OFFLINE -> NiceOfflinePayment()
        }
    }
}

class TossPaymentFactory : PaymentFactory() {
    override fun createPayment(method: PaymentMethod): Payment {
        return when (method) {
            PaymentMethod.ONLINE -> TossOnlinePayment()
            PaymentMethod.OFFLINE -> TossOfflinePayment()
        }
    }
}

NicePaymentFactory와 TossPaymentFactory는 구체적인 팩토리 클래스로, createPayment 메서드를 구현하여 적절한 Payment 객체를 생성합니다.

추상 제품 클래스

abstract class Payment {
    abstract val description: String
    abstract val method: PaymentMethod

    open fun paramValidation(): Boolean {
        return true
    }

    open fun transfer(): Boolean {
        return true
    }

    open fun sendMessage(): Boolean {
        return true
    }
}

Payment는 추상 제품 클래스로, 모든 결제 방식이 공통으로 가져야 할 속성과 메서드를 정의합니다.

구체적인 제품 클래스

class NiceOnlinePayment() : Payment() {
    override val description = "Nice online"
    override val method = PaymentMethod.ONLINE

    override fun transfer(): Boolean {
        println(this.description)
        return super.transfer()
    }

    override fun sendMessage(): Boolean {
        println("Nice - Success send message")
        return super.sendMessage()
    }
}

class NiceOfflinePayment() : Payment() {
    override val description = "Nice offline"
    override val method = PaymentMethod.OFFLINE

    override fun transfer(): Boolean {
        println(this.description)
        return super.transfer()
    }

    // Nice 인 경우에는 offline도 메세지를 보냄
    override fun sendMessage(): Boolean {
        println("Nice - Success send message")
        return super.sendMessage()
    }
}

class TossOnlinePayment() : Payment() {
    override val description = "Toss online"
    override val method = PaymentMethod.ONLINE

    override fun transfer(): Boolean {
        println(this.description)
        return super.transfer()
    }

    override fun sendMessage(): Boolean {
        println("Toss - Success send message")
        return super.sendMessage()
    }
}

class TossOfflinePayment() : Payment() {
    override val description = "Toss offline"
    override val method = PaymentMethod.OFFLINE

    override fun transfer(): Boolean {
        println(this.description)
        return super.transfer()
    }
}

이러한 클래스들은 구체적인 제품 클래스로, Payment 클래스를 상속받아 특정 결제 방식에 맞는 동작을 구현합니다.

테스트

    fun test() {
        val nicePaymentFactory = NicePaymentFactory()
        nicePaymentFactory.process(PaymentMethod.OFFLINE)

        val tossPaymentFactory = TossPaymentFactory()
        tossPaymentFactory.process(PaymentMethod.OFFLINE)
    }

구체적인 팩토리 클래스 인스턴스를 생성 후 메서드를 호출하면 전체 프로세스는 동일하지만 세부 구현은 다르게 실행 할 수 있습니다. 예시 코드에서는 Nice의 경우 online, offline 모두 메세지를 출력하게 합니다.

팩토리 메서드 패턴의 장점

  • 확장성: 새로운 결제 방식이나 제공업체를 추가할 때, 기존 코드를 수정하지 않고 새로운 팩토리와 제품 클래스를 추가하기만 하면 됩니다.
  • 캡슐화: 객체 생성 로직이 팩토리 클래스에 캡슐화되어 있어, 클라이언트 코드는 구체적인 클래스 구현을 알 필요가 없습니다.
  • 유연성: 런타임에 어떤 객체를 생성할지 결정할 수 있어 유연한 설계가 가능합니다.
  • 단일 책임 원칙: 객체 생성 책임을 팩토리 클래스로 분리하여 각 클래스의 책임을 명확히 합니다.