티스토리 뷰
정의
- 공통된 업무 흐름은 부모 클래스에 두고, 해당 공통 업무 흐름 부분에서 객체 종류에 따라 달라지는 생성 부분은 자식 클래스가 부모의 팩토리 메서드를 오버라이하여 결정하는 디자인 패턴이다
필요한 이유
abstract class Logistics {
abstract fun deliverSequence()
}
class RoadLogistics : Logistics() {
override fun deliverSequence() {
checkOrder()
prepareCargo()
Truck().deliver()
sendNotification()
}
}
class SeaLogistics : Logistics() {
override fun deliverSequence() {
checkOrder()
prepareCargo()
Ship().deliver()
sendNotification()
}
}
- 일반 상속 방식은 자식마다 전체 작업을 오버라이드는 가능하나, 이 경우 공통 절차까지 중복 구현가능성 존재한다.
- 팩토리 메서드는 전체 작업 절차를 부모 클래스에 공통으로 유지하면서 절차 안에서 대상 객체마다 달라지는 객체 생성 부분만 오버라이드 가능한 메서드로 분리한다.
- 따라서 공통 절차의 중복을 줄이면서 자식클래스마다 필요한 객체를 커스텀하여 생성이 가능하며, 새로운 종류의 객체가 추가되어도 기존 공통 절차를 수정하지 않아도 된다.
코드
전체 코드
package DesignPattern
interface Transport {
fun deliver()
}
abstract class CommonLogistics {
fun deliverSequence() {
checkOrder()
prepareCargo()
val transport = createTransport()
transport.deliver()
sendDeliveryNotification()
}
fun checkOrder() {
println("checkOrder")
}
fun prepareCargo() {
println("prepareCargo")
}
fun sendDeliveryNotification() {
println("sendDeliveryNotification")
}
abstract fun createTransport(): Transport
}
class RoadLogistics : CommonLogistics() {
override fun createTransport(): Transport {
return Truck()
}
}
class SeaLogistics : CommonLogistics() {
override fun createTransport(): Transport{
return Ship()
}
}
class TrainLogistics : CommonLogistics() {
override fun createTransport(): Transport{
return Train()
}
}
class Train : Transport {
override fun deliver() {
println("기차로 철도 배송을 진행합니다.")
}
}
class Truck : Transport {
override fun deliver() {
println("트럭으로 육상 배송을 진행합니다.")
}
}
class Ship : Transport {
override fun deliver() {
println("배로 수상 배송을 진행합니다.")
}
}
부모 클래스 설명
interface Transport {
fun deliver()
}
abstract class CommonLogistics {
fun deliverSequence() {
checkOrder()
prepareCargo()
val transport = createTransport()
transport.deliver()
sendDeliveryNotification()
}
fun checkOrder() {
println("checkOrder")
}
fun prepareCargo() {
println("prepareCargo")
}
fun sendDeliveryNotification() {
println("sendDeliveryNotification")
}
abstract fun createTransport(): Transport
}
- fun deliverSequence() 라는 함수는 해당 부모 클래스를 상속 받은 자식 클래스 인스턴스들이 공통적으로 수행할 프로세스를 정의한 영역이다.
- 그래서 자식 클래스 객체가 상속 받은 클래스의 함수인 deliverSequence() 를 호출하면, checkOrder() 부터 실행하여 sendDeliveryNotification() 까지 실행한다.
- 그리고 중간에 핵심인 createTransport()를 호출하는데, 아래 정의된 abstract fun createTransport(): Transport 를 보면, 추상 메서드로 정의됨을 알 수 있고, 나아가 Transport 타입으로 반환하라고 명시되어있다.
- 즉, transport 라는 참조 변수는 creatTrasport() 라는 함수를 호출하여 Transport 라는 interface 타입 객체에 대한 참조값이 저장된다.
- 해당 객체 참조값은 heap 메모리(JVM 관리)에 저장된, Transport 타입의 객체를 추적할 수 있는 참조값을 반환해주어 해당 참조 변수에 저장된다.
- interface 라 함은 여러 클래스가 공통으로 제공해야하는 기능의 존재 및 호출 규약을 정의함으로써 클래스들의 해당 기능들에 대한 일관성을 제공하며, 객체, 변수, 매개변수, 반환값에 사용될 수 있는 추상 데이터타입이다
자식 클래스
class RoadLogistics : CommonLogistics() {
override fun createTransport(): Transport {
return Truck()
}
}
class SeaLogistics : CommonLogistics() {
override fun createTransport(): Transport{
return Ship()
}
}
class TrainLogistics : CommonLogistics() {
override fun createTransport(): Transport{
return Train()
}
}
최종 생성 목적 클래스
class Train : Transport {
override fun deliver() {
println("기차로 철도 배송을 진행합니다.")
}
}
class Truck : Transport {
override fun deliver() {
println("트럭으로 육상 배송을 진행합니다.")
}
}
class Ship : Transport {
override fun deliver() {
println("배로 수상 배송을 진행합니다.")
}
}
- RoadLogistics , SeaLogistics, TrainLogistics : 각자의 목적을 가진 자식클래스이다.
- 육상으로 운반하고자하는 객체는 RoadLogistics 클래스 생성자를 호출한 뒤, 최종적으로 Truck() 생성자를 호출하여 만들어내고
- 배로 수상 배송하고자 하는 객체는 SeaLogistics 클래스 생성자를 호출한 뒤, 최종적으로 Ship() 생성자를 호출하여 만들어낸다.
- 결국, RoadLogistics , SeaLogistics, TrainLogistics 클래스는 자연스럽게 부모 클래스안 deliverSequece() 라는 함수의 공통 프로세스를 따르게되면서 createFunction() 이라는 함수를 각자 본인마다 재정의하여 필요한 최종 목적 클래스를 호출한다. 그래서 createFunction() 호출하면 최종 산물인, Truck(), Ship(), Train() 생성자들로 호출된 객체들인 것이다.
Factory Method와 Interface의 관계
- 추상 메서드의 리턴 타입은 interface인 Transport로 설정함으로써 3개의 자식클래스가 모두 오버라이드한 팩토리 메서드의 return 타입을 일관되게 하였다.
- 해당 interface는 각각 Trunk, Ship, Train class를 통해 interface 기능들이 구현된다. 여기서는 deliver() 라는 함수를 각자 자신들에게 맞게 구체화(정의)하면 된다.
- 각 운송 수단마다 Tranport interface에 선언된 기능[deliver()] 구현에 차별점을 두면서 팩토리 메서드인 createTranport()의 return 되는 데이터 타입에 일관성을 가지기 위해 Interface 를 사용한 것이라 보면 된다.
- 추가로 interface()에 선언된 기능들은 해당 인터페이스를 쓰는 개념상 비슷한 객체들이 공통으로 가져야할 기능들을 추상적으로 선언되어있다고 보면된다. 그리고 그걸 구체화하는데 있어서는 차별점을 둔다는 것이다.
- Train() 생성자로 호출된 객체는 데이터 타입이 Train class 타입이면서 Transport 타입으로도 취급되기에 createTransport() 함수의 return 값으로 Train, Ship, Truck 생성자로 호출된 객체 참조 값이 와도 된다.
'Backend' 카테고리의 다른 글
| Observer Pattern (0) | 2026.08.03 |
|---|---|
| Singleton Pattern (0) | 2026.08.03 |
| Builder Pattern (0) | 2026.08.01 |
| UnitTest (0) | 2026.07.06 |
| java와 코틀린 차이 (0) | 2026.06.25 |
