* 예제 코드 및 설명에 필요한 개념들이 Swift를 기준으로 작성된 글입니다.
GoF 디자인 패턴 첫번째 키워드는 Creational Patterns 중 Factory Method 입니다.
Creational Patterns으로는 Factory Method, Abstract Factory, Builder, Prototype, Singleton 이 있습니다.
Creational Patterns은 코드의 유연성과 재사용성을 증가시키기 위한 객체 생성 패턴입니다.
1. 설계 목적
부모 클래스에서 객체를 생성하기 위한 인터페이스를 제공하지만, 자식 클래스에서 생성될 객체의 유형을 변경할 수 있도록 패턴입니다.
역시 한 번에 이해가 되지 않습니다.
2. 문제 상황
이해를 위해 사이트에서 제공하는 문제 상황을 살펴봅시다.
당신이 물류 관리 앱을 개발하고 있다고 가정합시다. 앱의 첫 번째 버전은 트럭 운송만 처리할 수 있어서 대부분의 코드가 Truck
(트럭) 클래스에 있습니다.

그런데 만약 앱에 Ship
을 추가하게 되었다면 어떻게 해야할까요?
이미 Truck
클래스로 결합된 코드를 전부 수정해야할지도 모릅니다. 또한 이후에 어떤 교통수단이 다시 추가될지도 알 수 없습니다.
3. 해결책
이러한 상황에서 Factory Method를 활용하여 문제를 해결할 수 있습니다.
필요한 클래스의 생성자를 Protocol을 통해 호출합니다.

예를 들어 Truck
클래스는 RoadLogistics
에서 Ship
클래스는 SeaLogisitics
에서 생성되어 반환됩니다.
protocol Logistics {
// Transport를 생성할 함수
func createTransport() -> Transport
// Logistics에서 Transport의 로직을 담은 함수
func planDelivery()
}
extension Logistics {
func planDelivery() {
let transport = createTransport()
transport.deliver()
}
}
class RoadLogistics: Logistics {
func createTransport() -> Transport {
return Truck()
}
}
class SeaLogistics: Logistics {
func createTransport() -> Transport {
return Ship()
}
}
먼저 공통되는 Logistics
프로토콜을 정의합니다.
RoadLogistic
와 SeaLogistics
클래스는 Logistics
프로토콜을 체택하여 각 클래스에 맞는 Transport
생성할 수 있도록 합니다.

protocol Transport {
func deliver()
}
class Truck: Transport {
func deliver() {
print("Deliver by land in a box.")
}
}
class Ship: Transport {
func deliver() {
print("Deliver by sea in a container.")
}
}
Transport
프로토콜을 정의하고, Truck
과 Ship
클래스가 채택합니다.
각 클래스에 맞게 deliver()
함수를 정의합니다.
4. 구조

구조에 대해 그림으로 표시하면 다음과 같습니다.
Creator = Logistics
, ConcreteCreatorA = RoadLogistics
, ConcreteCreatorB = SeaLogistics
Product = Transport
, ConcreteProductA = Truck
, ConcreteProductB = Ship
이 됩니다.
5. 예제 코드의 사용
원활한 사용을 위해 enum을 추가했습니다.
enum LogisticsType {
case road
case sea
}
class LogisticsCompany {
private var logistics: Logistics
init(logisticsType: LogisticsType) {
self.logistics = {
switch logisticsType {
case .road: return RoadLogistics()
case .sea: return SeaLogistics()
}
}()
}
func deliverState() {
logistics.planDelivery()
}
}
let roadLogisticsCompany = LogisticsCompany(logisticsType: .road)
roadLogisticsCompany.deliverState()
// Deliver by land in a box.
let seaLogisticsCompany = LogisticsCompany(logisticsType: .sea)
seaLogisticsCompany.deliverState()
// Deliver by sea in a container.
위 코드에서 LogisticsCompany
처럼 해당 코드를 사용하는 클래스를 클라이언트 코드라고 합니다.
6. 장단점
Creator
와 Product
의 결합도가 낮아집니다.
또한 SOLID 원칙 중 Single Responsibility Principle(SRP)과 Open/Closed Principle(OCP) 원칙을 준수하게 됩니다.
하지만 오히려 너무 많은 자식 클래스 종류가 필요할 경우 코드가 복잡해질 수 있습니다.
7. 실제 사용 사례
만약 다크모드, 라이트모드에 따라서 버튼의 모양을 달리하고 싶다고 어떻게 해야할까요?
다크모드에서는 좀더 샤프하게 직각 모서리를, 라이트모드에서는 둥근 모서리를 적용해야한다고 가정해봅시다.
// Product Protocol
protocol CustomButton: UIButton {
func configure()
}
// Product A
class LightModeButton: UIButton, CustomButton {
func configure() {
self.backgroundColor = .white
self.layer.cornerRadius = 10
self.setTitleColor(.black, for: .normal)
self.setTitle("Light Mode Button", for: .normal)
}
}
// Product B
class DarkModeButton: UIButton, CustomButton {
func configure() {
self.backgroundColor = .black
self.setTitleColor(.white, for: .normal)
self.setTitle("Dark Mode Button", for: .normal)
}
}
먼저 각 버튼에 사용할 프로토콜과 버튼 클래스를 정의합니다.
// Creator Protocol
protocol ButtonFactory {
func createButton() -> CustomButton
}
// Product A Creator
class LightButtonFactory: ButtonFactory {
func createButton() -> CustomButton {
return LightModeButton()
}
}
// Product B Creator
class DarkButtonFactory: ButtonFactory {
func createButton() -> CustomButton {
return DarkModeButton()
}
}
Factory Method 패턴으로 생성자 클래스를 정의합니다.
class SettingsViewController: UIViewController {
var buttonFactory: ButtonFactory?
override func viewDidLoad() {
super.viewDidLoad()
buttonFactory = {
switch traitCollection.userInterfaceStyle {
case .dark: return DarkButtonFactory()
default: return LightButtonFactory()
}
}()
if let button = buttonFactory?.createButton() {
button.configure()
view.addSubview(button)
button.frame = CGRect(x: 100, y: 100, width: 200, height: 50)
}
}
}
클라이언트 코드에서 상황에 맞는 버튼를 생성하고 사용합니다.
이 과정에서 버튼의 생성 로직과 클라이언트 코드의 결합도가 낮아집니다.
8. Conclusion
Factory Method 패턴은 객체 생성을 위임하는 패턴이라고도 볼 수 있겠습니다.
확실히 코드간의 결합도를 낮춰 유연성을 증가시킬 수 있겠습니다.
Reference
'Design Pattern > GoF - Creational Patterns' 카테고리의 다른 글
[GoF Design Patterns] Singleton (0) | 2024.10.18 |
---|---|
[GoF Design Patterns] Prototype (0) | 2024.10.01 |
[GoF Design Patterns] Builder (0) | 2024.09.28 |
[GoF Design Patterns] Abstract Factory - 실제 사용 예제 (1) | 2024.09.26 |
[GoF Design Patterns] Abstract Factory (0) | 2024.09.26 |