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