빌더 패턴은 Creational Pettern 중 하나로
Director/Builder/Product 세 가지 요소로 구성되어있다.
빌더 패턴은
"특정 객체를 생성하는 과정에서 각 프로퍼티를 Set할 수 있는 인터페이스를 제공하여
동일한 프로덕트에 다른 프로퍼티 값을 상황에 따라 얼마던지 커스터마이징하여 쓸 수 있는"
패턴이다.
세 가지 요소로 구성된 빌더 패턴을 그림으로 그리자면 다음과 같다.
[1] Builder
빌더는 프로덕트를 생성하는 인터페이스를 제공한다.
프로덕트를 생성하는 빌더를 생성하여 비즈니스 로직에서 마지막에 build 메서드를 통해 프로덕트를 얻는다.
빌더는 상태의 보존을 유지하면서 특정 프로퍼티 변경에 따른 프로덕트 재생산일 때만 전역의 빌더 인스턴스 객체로 남는 것이 좋다.
그리고 주의해야할 것은
빌더 프로퍼티와 메서드 등은 실제 프로덕트의 프로퍼티와는 관계가 없다는 것이다.
빌더의 초기화 함수, 프로퍼티, 메서드는 결국 프로덕트를 만들어내는 인터페이스일 뿐이지
종속성 자체가 있는 것은 아니다.
아래 예제코드에서
빌더 내 대부분의 메서드는 프로덕트 프로퍼티의 setter 메서드이지만,
특정 메서드는 프로퍼티의 setter가 아닌, 모든 Bool형의 프로퍼티를 일괄처리하는 메서드가 있다.
(참조: setAllToggleProperty)
[2] Director
Builder로 동일한 비즈니스 로직 및 순서에 의해 빈번히 생성되는 객체 인스턴스가 존재한다면
디렉터에 해당 로직을 추가함으로써
디렉터의 접근으로만 동일한 객체 인스턴스를 적재적소에서 사용할 수 있다.
즉, 빌더가 요리에서 레시피의 한 줄이 될 수 있는 요소들을 담은 객체라면
디렉터는 일종에 프로덕트라는 음식을 만드는 레시피를 가지고 있는 객체이다.
[3] Product
빌더를 통해 생성될 인스턴스의 객체이다.
대게로 빌드 패턴을 통해 객체의 인스턴스를 생성하는 건
여튼 Product 내부의 세터 메서드를 통해 값을 변경하는 조작이 아닌
인터페이스가 추가되야하므로 소스량은 결국에 늘어날 수 있다.
그러므로, 프로퍼티의 개수가 많으면 빌더를 통해 인스턴스 조작을 손쉽게할 수 있으나
프로퍼티의 개수가 적다면 오히려 불필요한 접근자 두개가 생기는 것이니
절대 적거나 단순한 객체형태를 빌더로 구현하지는 말자.
(단순한 객체형태라면 오히려 객체의 생성을 서브 클래스에 맡기는 팩토리 메서드 패턴이 더 참조에 유리할 것으로 생각한다.)
import AVFoundation
// 1. 카메라 설정 값 정보를 가지는 클래스
final class CameraConfiguration: CustomStringConvertible {
/// Type to manage media data.
private let mediaType: AVMediaType
/// Indicates physical positionof an AVCaptureDevice's hardware on the system.
private let position: AVCaptureDevice.Position
/// Type of a list of devices matching certain search criteria.
private let captureDevice: AVCaptureDevice.DeviceType
/// Indicates the session preset currently in use by the receiver.
private let sessionPreset: AVCaptureSession.Preset
/// The mode of the torch on the receiver's device, if it has one.
private let torchMode: AVCaptureDevice.TorchMode
/// Current status whether a user grants permission specific input device.
private var authorizationStatus: AVAuthorizationStatus
// MARK: - Variables
private var makeupEnabled: Bool
private var distortEnabled: Bool
private var skinSmoothEnabled: Bool
private var stickerEnabled: Bool
private var availableMaxStickerCount: Int
// MARK: - Initializer
init(
mediaType: AVMediaType,
position: AVCaptureDevice.Position,
captureDevice: AVCaptureDevice.DeviceType,
sessionPreset: AVCaptureSession.Preset,
torchMode: AVCaptureDevice.TorchMode,
authorizationStatus: AVAuthorizationStatus,
makeupEnabled: Bool,
distortEnabled: Bool,
skinSmoothEnabled: Bool,
stickerEnabled: Bool,
availableMaxStickerCount: Int
) {
self.mediaType = mediaType
self.position = position
self.captureDevice = captureDevice
self.sessionPreset = sessionPreset
self.torchMode = torchMode
self.authorizationStatus = authorizationStatus
self.makeupEnabled = makeupEnabled
self.distortEnabled = distortEnabled
self.skinSmoothEnabled = skinSmoothEnabled
self.stickerEnabled = stickerEnabled
self.availableMaxStickerCount = availableMaxStickerCount
}
// MARK: - Internal Methods
func isMakeupEnabled() -> Bool {
makeupEnabled
}
func isDistortEnabled() -> Bool {
distortEnabled
}
func isSkinSmoothEnabled() -> Bool {
skinSmoothEnabled
}
func isStickerEnabled() -> Bool {
stickerEnabled
}
func getAvailableMaxStickerCount() -> Int {
availableMaxStickerCount
}
// MARK: - CustomStringConvertible Variable
var description: String {
"""
MediaType: \(mediaType)
position: \(position)
captureDevice: \(captureDevice)
sessionPreset: \(sessionPreset)
torchMode: \(torchMode)
authorizationStatus: \(authorizationStatus)
makeupEnabled: \(makeupEnabled)
distortEnabled: \(distortEnabled)
skinSmoothEnabled: \(skinSmoothEnabled)
stickerEnabled: \(stickerEnabled)
availableMaxStickerCount: \(availableMaxStickerCount)
"""
}
}
// 2. Product(여기서는 CameraConfiguration) 객체를 생성하는 빌더
final class CameraConfigurationBuilder {
private let mediaType: AVMediaType
private let position: AVCaptureDevice.Position
private let captureDevice: AVCaptureDevice.DeviceType
private let sessionPreset: AVCaptureSession.Preset
private let torchMode: AVCaptureDevice.TorchMode = .auto
private var authorizationStatus: AVAuthorizationStatus = .notDetermined
private var makeupEnabled: Bool = false
private var distortEnabled: Bool = false
private var skinSmoothEnabled: Bool = false
private var stickerEnabled: Bool = false
private var availableMaxStickerCount = 5
// 빌더에 initializer를 넣는다는 건 빌더로 생성되는 객체에 반드시 셋을 해야하는 프로퍼티가 존재할 때 강제할 수 있다.
// MARK: - Initializer
init(
mediaType: AVMediaType,
position: AVCaptureDevice.Position,
captureDevice: AVCaptureDevice.DeviceType,
sessionPreset: AVCaptureSession.Preset
) {
self.mediaType = mediaType
self.position = position
self.captureDevice = captureDevice
self.sessionPreset = sessionPreset
}
// MARK: - Internal Methods
/// Product를 생성하는 빌드 메서드
func build() -> CameraConfiguration {
CameraConfiguration(
mediaType: mediaType,
position: position,
captureDevice: captureDevice,
sessionPreset: sessionPreset,
torchMode: torchMode,
authorizationStatus: authorizationStatus,
makeupEnabled: makeupEnabled,
distortEnabled: distortEnabled,
skinSmoothEnabled: skinSmoothEnabled,
stickerEnabled: stickerEnabled,
availableMaxStickerCount: availableMaxStickerCount
)
}
func setMakeupEnabled(_ enabled: Bool) {
makeupEnabled = enabled
}
func setDistortEnabled(_ enabled: Bool) {
distortEnabled = enabled
}
func setSkinSmoothEnabled(_ enabled: Bool) {
skinSmoothEnabled = enabled
}
func setStickerEnabled(_ enabled: Bool) {
stickerEnabled = enabled
}
func setAvailableMaxStickerCount(_ count: Int) {
availableMaxStickerCount = count
}
// 이렇게 각 프로퍼티를 Set하는 메서드가 아닌, 특정 타입을 받거나 비즈니스 로직에 의해 프로덕트 프로퍼티가 변경될 수 있다.
func setAllToggleProperty(_ enabled: Bool) {
makeupEnabled = enabled
distortEnabled = enabled
skinSmoothEnabled = enabled
stickerEnabled = enabled
}
}
// 3. 빌더를 통해 Product를 생성하는 단순 예제
let builder = CameraConfigurationBuilder(
mediaType: .video,
position: .unspecified,
captureDevice: .builtInDualCamera,
sessionPreset: .high
)
let cameraConfig = builder.build()
print("===첫 번째 Config가 빌드된 시점의 빌더 상태===")
print(cameraConfig)
builder.setAllToggleProperty(true)
builder.setAvailableMaxStickerCount(10)
let anotherCameraConfig = builder.build()
print("\n===두 번째 Config가 빌드된 시점의 빌더 상태===")
print(anotherCameraConfig)
// 4. Director : 동일한 로직으로 같은 값을 가지는 객체를 빌드할 때 쓰는 편의 객체이다.
struct CameraConfigurationDirector {
func createImageCameraConfiguration() -> CameraConfiguration {
let builder = CameraConfigurationBuilder(
mediaType: .video,
position: .back,
captureDevice: .builtInTripleCamera,
sessionPreset: .hd1920x1080
)
builder.setAllToggleProperty(true)
let config = builder.build()
return config
}
func createVideoCameraConfiguration() -> CameraConfiguration {
let builder = CameraConfigurationBuilder(
mediaType: .video,
position: .back,
captureDevice: .builtInDualCamera,
sessionPreset: .hd4K3840x2160
)
builder.setSkinSmoothEnabled(true)
builder.setDistortEnabled(true)
let config = builder.build()
return config
}
}
// 5. Director를 통한 Product 반환 예
let imageConfig = CameraConfigurationDirector().createImageCameraConfiguration()
let videoConfig = CameraConfigurationDirector().createVideoCameraConfiguration()
print("\n\n==============================================================\n\n")
print("===Director로 생성된 Image Configuration, 디렉터로 접근하여 언제든 객체 생성이 가능하다===")
print(imageConfig)
print("\n===Director로 생성된 Video Configuration, 디렉터로 접근하여 언제든 객체 생성이 가능하다===")
print(videoConfig)
===첫 번째 Config가 빌드된 시점의 빌더 상태===
MediaType: AVMediaType(_rawValue: vide)
position: AVCaptureDevicePosition
captureDevice: AVCaptureDeviceType(_rawValue: AVCaptureDeviceTypeBuiltInDualCamera)
sessionPreset: AVCaptureSessionPreset(_rawValue: AVCaptureSessionPresetHigh)
torchMode: AVCaptureTorchMode
authorizationStatus: AVAuthorizationStatus
makeupEnabled: false
distortEnabled: false
skinSmoothEnabled: false
stickerEnabled: false
availableMaxStickerCount: 5
===두 번째 Config가 빌드된 시점의 빌더 상태===
MediaType: AVMediaType(_rawValue: vide)
position: AVCaptureDevicePosition
captureDevice: AVCaptureDeviceType(_rawValue: AVCaptureDeviceTypeBuiltInDualCamera)
sessionPreset: AVCaptureSessionPreset(_rawValue: AVCaptureSessionPresetHigh)
torchMode: AVCaptureTorchMode
authorizationStatus: AVAuthorizationStatus
makeupEnabled: true
distortEnabled: true
skinSmoothEnabled: true
stickerEnabled: true
availableMaxStickerCount: 10
==============================================================
===Director로 생성된 Image Configuration, 디렉터로 접근하여 언제든 객체 생성이 가능하다===
MediaType: AVMediaType(_rawValue: vide)
position: AVCaptureDevicePosition
captureDevice: AVCaptureDeviceType(_rawValue: AVCaptureDeviceTypeBuiltInTripleCamera)
sessionPreset: AVCaptureSessionPreset(_rawValue: AVCaptureSessionPreset1920x1080)
torchMode: AVCaptureTorchMode
authorizationStatus: AVAuthorizationStatus
makeupEnabled: true
distortEnabled: true
skinSmoothEnabled: true
stickerEnabled: true
availableMaxStickerCount: 5
===Director로 생성된 Video Configuration, 디렉터로 접근하여 언제든 객체 생성이 가능하다===
MediaType: AVMediaType(_rawValue: vide)
position: AVCaptureDevicePosition
captureDevice: AVCaptureDeviceType(_rawValue: AVCaptureDeviceTypeBuiltInDualCamera)
sessionPreset: AVCaptureSessionPreset(_rawValue: AVCaptureSessionPreset3840x2160)
torchMode: AVCaptureTorchMode
authorizationStatus: AVAuthorizationStatus
makeupEnabled: false
distortEnabled: true
skinSmoothEnabled: true
stickerEnabled: false
availableMaxStickerCount: 5
'스위프트' 카테고리의 다른 글
[Swift/디자인패턴] Strategy 패턴 (0) | 2021.12.20 |
---|---|
[Swift/아키텍쳐 패턴] VIPER 패턴 (0) | 2021.11.10 |
[Swift] TableView의 DataSource와 Delegate들 분석 (0) | 2021.11.02 |
[Swift] UITableView & UITableViewCell 고찰 (0) | 2021.10.27 |
[Network] NWConnection (0) | 2021.10.22 |