
Template Method 패턴
Template Method 패턴은 상위 클래스에서 알고리즘의 전체 구조(템플릿)을 정의하고, 하위 클래스에서 세부 단계를 구현하는 패턴이다.
즉, "큰 틀은 정해져 있지만 세부 동작은 상황에 따라 달라질 수 있는 알고리즘"을 만들 때 적합하다.
구조
- AbstractClass
- templateMethod() : 알고리즘 뼈대를 정의 (불변 공통 흐름)
- primitiveOperation() : 하위 클래스가 반드시 구현해야 하는 메서드
- ConcreteClass
- primitiveOperation()을 구현해서 실제 동작을 정의
Template Method
package org.example.templateMethod
// Abstract class : Define the algorithm's skeleton
abstract class DataProcessor {
// Template Method : Define the algorithm's total flow
fun process() {
loadData()
processData()
saveData()
}
// The process to be established in subclasses
protected abstract fun loadData()
protected abstract fun processData()
protected abstract fun saveData()
}
// ConcreteClass 1
class CsvDataProcessor : DataProcessor() {
override fun loadData() = println("[CsvDataProcessor] loadData")
override fun processData() = println("[CsvDataProcessor] processData")
override fun saveData() = println("[CsvDataProcessor] saveData")
}
// ConcreteClass 2
class JsonDataProcessor : DataProcessor() {
override fun loadData() = println("[JsonDataProcessor] loadData")
override fun processData() = println("[JsonDataProcessor] processData")
override fun saveData() = println("[JsonDataProcessor] saveData")
}
// Client
fun main() {
val csvProcessor: DataProcessor = CsvDataProcessor()
csvProcessor.process()
println("------")
val jsonProcessor: DataProcessor = JsonDataProcessor()
jsonProcessor.process()
}
결과
[CsvDataProcessor] loadData
[CsvDataProcessor] processData
[CsvDataProcessor] saveData
------
[JsonDataProcessor] loadData
[JsonDataProcessor] processData
[JsonDataProcessor] saveData
장점
- 일관성 유지 : 상위 클래스가 전체 구조를 통제하므로, 흐름이 틀어지지 않고 일관성을 보장한다.
- 확장성 향상 : 새로운 하위 클래스를 추가하면 다른 방식으로 동작을 쉽게 정의할 수 있다.
단점
- 하위 클래스 증가 : 세부 동작을 다양하게 정의하다 보면 하위 클래스가 많아져 관리가 복잡해질 수 있다.
- 유연성 한계 : 알고리즘의 큰 틀이 상위 클래스에 고정되어 있어, 그 흐름을 바꾸기 어렵다.
Q1. Template Method vs. Factory?
| 구분 | Template Method | Factory |
| 목적 | 알고리즘의 전체 흐름을 정의하고, 세부 단계는 하위 클래스에서 구현한다. | 객체 생성 방법을 추상화하여, 구체 클래스에 의존하지 않고, 객체를 생성한다. |
| 상위 클래스 역할 | 알고리즘 뼈대를 정의 (templateMethod()) | 객체 생성 인터페이스 제공 (createProduct()) |
| 하위 클래스 역할 | 세부 단계 구현 (primitiveOperation) | 실제 객체 생성 구현 (ConcreteProdut) |
| 주 사용 상황 | 공통 흐름은 고정, 세부 동작은 다양할 때 | 어떤 클래스의 객체를 동적으로 생성할 때 |
| 출력 예시 | CSV / JSON 데이터 처리 흐름 | Car, Bike 객체를 만드는 Factory |
정리하면,
- Template Method는 "무엇을 처리할지 전체 흐름을 정하고, 세부 내용만 바꾸는 패턴"이고,
- Factory는 "무엇을 생성할지 추상화하고, 실제 객체 생성 책임만 위임하는 패턴"이다.
즉, Template Method는 알고리즘 중심이고, Factory는 객체 생성 중심이다.
Q2. abstract class vs. interface
| 구분 | abstract class | interface |
| 상속 | 구체 클래스는 단 하나의 추상 클래스만 상속 가능 | 구체 클래스는 여러 인터페이스 상속 가능 |
| 코드 재사용 | 일반 메서드 재사용 가능 | default 메서드만 가능 |
| 생성자 | 가능 (e.g. Vehicle(name: String)) | 불가능 (e.g. 하위 생성자에서 override로 초기화) |
| 상태 | 가질 수 있음 | val만 가능 |
| 목적 | 부분 구현 + 공통 상태 공유 | 계약/규약 정의, 여러 클래스 구현 가능 |
// ------------------------------
// 1️⃣ Abstract Class Example
// ------------------------------
abstract class Vehicle(val name: String) { // Can have properties and constructor
// Abstract method: must be implemented by subclass
abstract fun startEngine()
// Normal method: reusable in subclasses
fun describe() {
println("This vehicle's name is $name.")
}
// Open method: can be optionally overridden
open fun honk() {
println("Beep beep!")
}
}
// Subclass: implements abstract method and optionally overrides open method
class Car(name: String, val model: String) : Vehicle(name) {
override fun startEngine() {
println("Starting engine of $name ($model)")
}
override fun honk() {
println("$name ($model) says: Beep Beep! (customized)")
}
}
// ------------------------------
// 2️⃣ Interface Example
// ------------------------------
// Interface: cannot have constructor, can define val
interface Flyable {
val maxAltitude: Int // must be implemented in subclass
fun fly() // abstract method
fun land() { // default implementation allowed
println("Default landing")
}
}
interface Swimmable {
fun swim() // abstract method
}
// Implement multiple interfaces
class Duck(override val maxAltitude: Int) : Flyable, Swimmable {
override fun fly() {
println("Duck flies up to $maxAltitude meters!")
}
override fun swim() {
println("Duck is swimming on water.")
}
// land() uses default implementation
}
// ------------------------------
// 3️⃣ Main function for testing
// ------------------------------
fun main() {
println("=== Abstract Class Test ===")
val myCar = Car("Hyundai", "Avante")
myCar.describe() // Reuse superclass method
myCar.startEngine() // Implemented abstract method
myCar.honk() // Overridden open method
println("\n=== Interface Test ===")
val daffy = Duck(maxAltitude = 500)
daffy.fly() // Implemented abstract method
daffy.land() // Default method from interface
daffy.swim() // Another interface method
println("\n=== Multiple Interface Reference ===")
val duckAsFlyable: Flyable = daffy
val duckAsSwimmable: Swimmable = daffy
duckAsFlyable.fly()
duckAsSwimmable.swim()
}
결과
=== Abstract Class Test ===
This vehicle's name is Hyundai.
Starting engine of Hyundai (Avante)
Hyundai (Avante) says: Beep Beep! (customized)
=== Interface Test ===
Duck flies up to 500 meters!
Default landing
Duck is swimming on water.
=== Multiple Interface Reference ===
Duck flies up to 500 meters!
Duck is swimming on water.
Powered By. ChatGPT
'Backend' 카테고리의 다른 글
| [Backend] 데이터 구조 (List) (0) | 2025.09.11 |
|---|---|
| [Backend] Generic Tutorial (Kotlin) (1) | 2025.09.11 |
| [Backend] Observer 패턴 (1) | 2025.09.03 |
| [Backend] Singleton 패턴 (1) | 2025.09.03 |
| [Backend] Bulider 패턴 (1) | 2025.09.03 |