[Swift 디자인 패턴] Template Method Pattern (템플릿 메서드) - 디자인 패턴 공부 23
안녕하세요 Pingu입니다!🐧
지난 글에서는 행동 패턴 중 하나인 Strategy Pattern(전략)에 대해 알아봤었는데요, 이번 글에서는 또 다른 행동 패턴 중 하나인 Template Method Pattern에 대해 알아보도록 하겠습니다.
템플릿 메서드 패턴이란?
Template Method(템플릿 메서드)는 부모 클래스에서 여러 메서드로 이루어진 알고리즘의 틀을 정의합니다. 이러한 알고리즘 틀을 Template Method라고 하며, 하위 클래스는 Template Method에서 단계별로 이루어진 메서드들을 override 할 수 있도록 만들어 구조를 변경하지 않고 알고리즘의 특정 단계를 재정의 할 수 있도록 하는 디자인 패턴입니다.
- Abstract Class (Application)
- Abstract Class는 알고리즘을 단계적으로 작동하는 메서드들과 이들을 실제로 호출하는 Template Method를 정의합니다.
- Concrete Class (My Application)
- Abstract Class에서 정의한 단계적으로 작동하는 메서드들은 override 할 수 있지만 Template Method는 override 할 수 없습니다.
템플릿 메서드는 언제 쓰나요?
클라이언트가 알고리즘의 특정 단계만 제어하고 전체 알고리즘이나 구조를 변경할 수 없도록 하고 싶을 때 템플릿 메서드 패턴을 사용하면 좋습니다. 혹은 특정 단계에서의 구현만 다르고 다른 부분은 대부분 동일한 동작을 하는 알고리즘을 사용하는 경우에도 사용하면 좋습니다.
예를 들어 여러 확장자의 파일들에서 원하는 데이터를 추출하는 작업을 한다고 가정해볼게요. PDF, Word, Excel 파일에서 원하는 데이터를 얻고 싶은 경우 어떤 과정으로 데이터를 얻을 수 있을까요? 일단 간단하게 생각해보면 아래와 같은 단계가 필요할 거 같습니다.
- 특정 파일에서 데이터를 가지고 옵니다.
- 가지고 온 데이터를 원하는 데이터로 처리합니다.
- 처리된 데이터를 분석합니다.
즉 1번 과정만 다르고 2,3번 과정은 모두 동일하게 작동되어도 문제가 없어보입니다. 하지만 만약 3개의 파일을 처리하기 위한 코드를 모두 따로 구현하면 비효율적이겠죠? 또한 다른 확장자를 분석하는 코드도 추가하려면 더 많은 작업이 필요하게 됩니다. 따라서 동일한 코드를 3번 쓰기보다는 템플릿 메서드 패턴을 사용해서 1번 과정만 3개 만들고 나머지 2,3번 과정은 동일한 코드로 구현하면 이와 같은 문제를 해결할 수 있습니다.
템플릿 메서드의 결과
장점
- 클라이언트가 알고리즘의 특정 부분을 구현해도 알고리즘의 다른 부분은 영향을 덜 받도록 할 수 있습니다.
- 중복된 코드를 슈퍼 클래스에서 한 번만 정의해도 되기 때문에 효율적입니다.
단점
- 일부 클라이언트는 이미 정의된 알고리즘만 사용할 수 있기 때문에 제한받는 상황이 올 수 있습니다.
- Liskov Subsititution Priciple을 위반 할 수 있습니다.
- 템플릿 메서드에 필요한 단계가 많다면 유지하기 어려울 수 있습니다.
예제
그럼 이제 Template Method Pattern을 Swift로 구현해보겠습니다.
아까 본 다양한 확장자 파일에서 데이터를 가져와서 분석하는 상황을 만들어보도록 할게요.
먼저 아까 정의해본 과정을 한 번 다시 보겠습니다.
- 특정 파일에서 데이터를 가지고 옵니다.
- 가지고 온 데이터를 원하는 데이터로 처리합니다.
- 처리된 데이터를 분석합니다.
여기서 각각의 Concrete Class에서 override 할 메서드는 1번에 해당하는 메서드입니다.
그럼 먼저 Template Method 및 다른 메서드들을 구현한 Abstract Class를 먼저 구현해볼게요.
// Abstract Class
class DataMining {
// Template Method
final func dataMining() {
getData()
dataProcess()
dataAnalysis()
}
func getData() {
print("데이터를 불러옵니다.")
}
func dataProcess() {
print("데이터 처리완료")
}
func dataAnalysis() {
print("데이터 분석완료\n")
}
}
Template Method 역할을 하는 dataMining()메서드는 더 이상 override 할 수 없도록 만들어 줬습니다.
그럼 이제 Concrete Class를 구현해야하는데요, 각각의 Concrete Class에서 override 할 메서드는 getData()뿐입니다.
// Concrete Class
class PDFFileDataMining: DataMining {
override func getData() {
print("PDF File 데이터를 불러옵니다.")
}
}
// Concrete Class
class WordFileDataMining: DataMining {
override func getData() {
print("Word File 데이터를 불러옵니다.")
}
}
// Concrete Class
class ExcelFileDataMining: DataMining {
override func getData() {
print("Excel File 데이터를 불러옵니다.")
}
}
이렇게 3개의 Concrete Class를 구현했지만 실제로 각각의 class에서 구현한 메서드는 getData() 하나뿐인 것을 볼 수 있습니다.
이젠 실제로 이를 사용할 Client를 구현해볼게요.
enum FileType {
case pdf
case word
case excel
}
class Client {
static func dataMining(fileType: FileType) {
switch fileType {
case .pdf:
PDFFileDataMining().dataMining()
case .word:
WordFileDataMining().dataMining()
case .excel:
ExcelFileDataMining().dataMining()
}
}
}
3개의 파일 타입이 있기 때문에 열거형도 하나 만들어줬습니다.
그럼 이제 한 번 사용해보겠습니다.
이렇게 3개의 파일 타입을 처리 할 수 있는 코드를 만들었지만 데이터 처리와 분석 코드는 모두 Abstract Class에서 구현한 메서드를 그대로 사용하고 getData()만 각각의 서브 클래스에서 구현해준 것을 볼 수 있습니다.
이렇게 Template Method Pattern을 알아보고 간단하게 구현도 해봤습니다.
혹시라도 틀린 부분이 있다면 알려주시면 감사하겠습니다~
전체 코드는 여기에서 볼 수 있습니다.
감사합니다.