슈퍼클래스에서 객체를 생성하기 위한 인터페이스를 제공하고 서브클래스가 생성될 객체의 유형을 변경할 수 있도록 하는 디자인 패턴이다. 일반적으로 슈퍼클래스에서 어떠한 비즈니스 로직이 존재하고, 그 로직을 수행하는 객체를 생성하는 것을 서브클래스에서 구현하는 형태이다.
물류 관리 응용 프로그램을 만들었다. 앱의 첫 번째 버전은 트럭 운송만 처리할 수 있으므로 대부분의 코드는 Truck클래스 내에 있다. 나중에 사업을 확장하며 해상 물류에 대해서도 서비스를 확장해야할 시기가 왔다. 하지만 현재 대부분의 코드는 다 Truck 클래스와 연결되어있다. 지금 전체적으로 Ship클래스를 추가하여 변경한다고 하더라도 나중에 또 다른 운송 수단에 대해서 확장된다면? 문제가 복잡해지고 사이드 이펙트가 커질 수 있다.
직접적인 객체 생성 호출을 팩토리 메서드 내에서 해결한다. 이때 반환되는 객체를 제품이라고 한다. 팩토리 메서드를 가지고 있는 객체를 상속하여 자신이 원하는 제품을 만들도록 할 수 있다. 이 때 제품은 해당 도메인 내에서 정의된 동작을 하기위해 인터페이스를 구현해야한다.
클라이언트는 모든 제품을 추상적으로 취급할 수 있고, 그저 인터페이스를 사용할 뿐이다. 클라이언트는 내부적으로 어떤 일이 일어나는지 알 필요가 없다. 그저 필요한 기능을 호출할 뿐이다. 이렇게 팩토리 클래스, 제품 클래스를 추상화함으로써 더 유연한 서비스를 만들 수 있다. 운송 수단이 추가되면 기존 코드를 변경하는 것이 아닌 해당하는 클래스를 추가만 하면 된다. 이것은 "Append Only", 즉 OCP를 잘 지켰다.
package factory_method
abstract class Logistics {
fun planDelivery() {
val transport = createTransport()
println("---Start Delivery---")
transport.deliver()
println("---End Delivery---")
}
abstract fun createTransport(): Transport
}
package factory_method
class RoadLogistics : Logistics() {
override fun createTransport(): Transport = Truck()
}
package factory_method
class SeaLogistics : Logistics() {
override fun createTransport(): Transport = Ship()
}
package factory_method
interface Transport {
fun deliver()
}
package factory_method
class Truck : Transport {
override fun deliver() {
println("Go Go Truck!")
}
}
package factory_method
class Ship : Transport {
override fun deliver() {
println("Go Go Ship!")
}
}
package factory_method
fun main() {
println("What mode of transport would you like to use?")
println("1. Truck")
println("2. Ship")
val logistics: Logistics = when(readLine()) {
"1" -> RoadLogistics()
"2" -> SeaLogistics()
else -> {
println("Error!")
return
}
}
logistics.planDelivery()
}
- 코드에서 작업해야 하는 객체의 정확한 유형과 종속성을 미리 모르는 경우
- 라이브러리나 프레임워크의 사용자에게 내부 구성 요소를 확장하는 방법을 제공하려는 경우
- 매번 재구축하는 대신 기존 개체를 재사용하여 시스템 리소스를 절약하려는 경우
- 제작자와 구체적인 제품 간의 긴밀한 결합을 피할 수 있다.
- SRP => 제품 생성 책임을 프로그램의 한 위치로 이동
- OCP => 기존 클라이언트 코드를 손상시키지 않고 새로운 유형의 제품을 프로그램에 도입할 수 있다.
- 패턴을 구현하기 위해 많은 새 하위 클래스를 도입해야 하므로 코드가 더 복잡해질 수 있다. 가장 좋은 시나리오는 생성자 클래스의 기존 계층 구조에 패턴을 도입하는 경우이다.
https://github.com/oh980225/DesignPattern/tree/main/src/main/kotlin/factory_method