팩토리 패턴에 대한 오해
많은 사람들이 팩토리 패턴을 객체 생성을 위한 클래스를 사용하는 것으로 알고 있다. 실제로 많은 글들을 보면 팩토리 패턴을 공장에 input을 넣으면면 결과물이 나오는 그림으로 설명해놓는 것을 볼 수 있다.
하지만 위 패턴은 팩토리 '패턴'이 아니다. 어떻게 보면 Input에 따라 결과물을 다르게 만드는 전략 패턴의 일종으로 볼 수 있다. 보통 많은 사람들이 위와 같이 팩토리 패턴을 생각하는데, 위와 같은 팩토리를 "헤드퍼스트 디자인패턴"에서는 "심플 팩토리 관용구"라고 부르기도 한다.
그렇다면 팩토리 패턴이란 무엇일까?
팩토리 패턴이란?
팩토리 패턴은 객체를 생성하기 위해 필요한 인터페이스를 만든 후, 인터페이스를 구현하는 클래스에서 어떤 객체를 만들지 결정하는 패턴이다. 즉, 그림1의 팩토리 객체는 Concrete 클래스 안에 팩토리 메서드가 있는데, 팩토리 패턴의 Factory는 Concrete 클래스가 아닌 인터페이스를 구현해서 만들어지는 클래스라는 뜻이다.
그렇다면 그림1의 Factory를 어떻게 바꾸면 팩토리 패턴이 될까? 그림1 대신 그림2와 같이 Factory가 interface로 선언되면 특정 구현체(Concrete Class)에 의존하지 않도록 만들 수 있다. 이에 따라 Input에 따라 생성되는 Output에 유연성을 만들어낼 수 있다. 즉, interface로 만들어진 Factory를 상속받아 구현하면 바로 팩토리 패턴이 된다.
자 그러면 개념을 알았으니 한 번 팩토리 패턴을 직접 만들어보도록 하자.
팩토리 패턴 설계해보기
우리가 안드로이드 휴대폰에서 사용자들이 메모를 하기 위한 보드(Board)를 만든다고 해보자. 보드에는 붙일 아이템(BoardItem)들이 필요하고 BoardItem들은 간단한 메모장에서부터 캘린더, 디데이 등이 될 수 있다. 또한 이 보드 아이템들은 휴대폰, 태플릿 혹은 기타 안드로이드 디바이스 별로 다른 방식으로 View를 그려줘야 한다.
요구사항을 정리해보자
- 보드에 붙일 보드 아이템들은 다양해야 한다. 메모장, 캘린더, 디데이 등이 될 수 있다.
- 보드에 붙일 아이템들은 디바이스 종류별로 다르게 그려줘야 한다. 휴대폰, 태블릿 등에서 다르게 그려져야 한다.
정리된 요구사항에 따르면 생성되는 아이템들이 생성이 유연해야 한다. 이제 위에서 알아본 팩토리 패턴을 적용시켜 보도록 하자. 먼저 BoardItem들을 만들어내는 BoardItemFactory를 인터페이스로 선언해야 한다.
이후 이 BoardItemFactory를 구현하는 PhoneBoardItemFactory와 Tablet
BoardItemFactory를 만들면 된다. 만약 기기가 바뀌면 구현된 Factory만 교체하면 되는 것이다.
마지막으로 ViewType에 따라 다르게 View들을 생성하도록 만들면 완성된다.
위 내용을 코드로 만들면 다음과 같다. ViewType과 View를 선언하고 해당 ViewType 별로 생성하는 View를 다르게 만들면 된다.
sealed class ViewType {
data class Memo(val string: String) : ViewType()
data class MemoWithImage(val string: String, val imageUri: String) : ViewType()
object Calendar : ViewType()
}
abstract class View() {
//View 그리기 위한 로직들 추가
}
interface BoardItemFactory {
fun createView(view: ViewType): View
}
class PhoneBoardItemFactory : BoardItemFactory {
override fun createView(view: ViewType): View {
when (view) {
is ViewType.Memo -> {
return // Memo View
}
is ViewType.MemoWithImage -> {
return // MemoWithImage View
}
is ViewType.Calendar -> {
return // Calendar View
}
}
}
}
class TabletBoardItemFactory : BoardItemFactory {
override fun createView(view: ViewType): View {
when (view) {
is ViewType.Memo -> {
return // Memo View
}
is ViewType.MemoWithImage -> {
return // MemoWithImage View
}
is ViewType.Calendar -> {
return // Calendar View
}
}
}
}
정리
위의 내용들을 정리하면 다음과 같다.
'팩토리 패턴'은 필요한 객체를 만들기 위한 interface를 선언해서 유연하게 구현해서 사용할 수 있도록 하는 패턴이다. 팩토리 패턴에서 종류별로 객체가 생성되는 것은 부가적인 것이고, 이는 '심플 팩토리' 라고 부른다.