Template Method Pattern은 특정 알고리즘을 반복적으로 사용하고 있을 때 중복을 제거하기 위하여 적용해볼 수 있습니다. 공통 알고리즘을 묶을 템플릿 메서드를 포함할 추상 클래스를 정의하고, 하위 클래스에서 구체적인 내용을 작성하는 패턴입니다. 해당 패턴을 통해 코드 중복을 제거하고 공통된 알고리즘을 재사용할 수 있습니다. 템플릿 메서드의 변경이 필요하다면 하위 클래스에서 재정의를 통해 인터페이스의 변경 없이 세부 구현을 변경할 수도 있습니다.
Template Method Pattern
은 GoF의 분류 체계에서 행위(Behavioral) 패턴에 속합니다.
Template Method Pattern을 구현하기 위해 필요한 역할은 다음과 같습니다.
- AbstractClass(추상 클래스): 템플릿 메서드를 정의하고 하위 클래스에서 구현할 메서드를 정의합니다.
- ConcreteClass(구현 클래스): 추상 클래스에서 정의한 추상 메서드를 구현합니다.
Java 언어로 배우는 디자인 패턴 입문의 예제를 Swift 코드로 작성해봅시다. AbstractClass 역할을 수행하는 AbstractDisplay
클래스를 정의합니다. AbstractDisplay
클래스는 템플릿 메서드인 display
메서드를 정의하고 있습니다.
protocol AbstractDisplay {
func open()
func write()
func close()
}
extension AbstractDisplay {
func display() {
open()
for _ in 0..<5 {
write()
}
close()
}
}
AbstractClass
는 추상 클래스이므로 구현되어 있는 메서드가 존재하고 인스턴스를 생성할 수 없어야 합니다. protocol
과 extension
키워드를 활용하여 추상 클래스를 정의하였습니다. display
템플릿 메서드를 사용하고 싶다면, AbstractClass
를 채택하고 open
, write
, close
메서드를 구현해야 합니다.
final class CharDisplay: AbstractDisplay {
private let char: Character
init(char: Character) {
self.char = char
}
func open() {
print("<<", terminator: "")
}
func write() {
print(char, terminator: "")
}
func close() {
print(">>")
}
}
let display: AbstractDisplay = CharDisplay(char: "H")
display.display()
// output:
// <<HHHHH>>
CharDisplay
클래스는 AbstractDisplay
를 채택하고 있습니다. CharDisplay
클래스는 open
, write
, close
메서드를 구현하고 있습니다. CharDisplay
클래스는 display
메서드를 구현하지 않았지만, AbstractDisplay
의 display
메서드를 사용할 수 있습니다.
또 다른 클래스를 정의해봅니다.
final class StringDisplay: AbstractDisplay {
private let string: String
private let width: Int
init(string: String) {
self.string = string
self.width = string.count
}
func open() {
printLine()
}
func write() {
print("|\(string)|")
}
func close() {
printLine()
}
private func printLine() {
print("+\(String(repeating: "-", count: width))+")
}
}
let display: AbstractDisplay = StringDisplay(string: "Hello, World")
display.display()
// output:
// +-------------+
// |Hello, World|
// |Hello, World|
// |Hello, World|
// |Hello, World|
// |Hello, World|
// +-------------+
이렇듯 하나의 공통적인 로직을 템플릿화하여 코드의 중복을 없앨 수 있고, 추상 클래스를 상속(채택)하는 방식으로 인하여 상위 클래스 형식의 변수에 하위 클래스 인스턴스 중 어느 것을 대입해도 제대로 동작해야 한다는 원칙인 리스코프 치환 원칙(LSP, Liskov Substitution Principle)
을 만족합니다.
Swift에서는 extension
키워드의 존재로 인터페이스를 변경하지 않고도 쉽게 새로운 공통 로직을 언제든 추가할 수 있습니다.