Builder Pattern

Eddy📱·2022년 7월 23일
0

Design-Pattern

목록 보기
3/4
post-thumbnail

빌터 패턴은 객체의 생성과 객체를 표현하는 속성들과 분리하여 생성 절차는 동일하지만 결과는 다르게 만드는 패턴이다

비슷한 역할을 하는 객체를 생성하기 위해서 매번 코드를 작성하는 것보다 속성만 바꿀 수 있고 동일한 생성을 할 수 있는 Builder를 만든다면 재사용성 높은 코드를 사용할 수 있다.

BuilderPattern 에서 중요한 3가지 역할은 아래와 같다

  • Director: 객체 생성 방식에 대한 책임
  • Builder: 객체를 생성하는 추상 인터페이스
  • Product: Builder를 이용해서 Director가 만들어낸 최종 과제
  • ConCreateBuilder: Builder의 구현 클래스, 객체 생성 결과에 대한 구체적인 구현 책임

먼저 위에처럼 재사용성 있는 코드를 작성하기 위해서는 당연히 프로토콜이 필요하다!

재사용하려는 프로퍼티와 메서드를 아래와 같이 넣는다.

// Builder protocol
protocol Builder {
    var label: UILabel { get }
    func setText(with text: String) -> Builder
    func setTextColor(with textColor: UIColor) -> Builder
    func setFontSize(with textFontSize: CGFloat) -> Builder
}

다음으로 이 프로토콜을 채택해서 실제 어떤 속성을 지정해줘야하는지 정해야하는 클래스가 필요하다

// Builder 채택 후 상세 속성 지정하는 역할
class ConCreateBuilder: Builder {
    var label: UILabel = UILabel()
    
    func setText(with text: String) -> Builder {
        label.text = text
        return self
    }
    
    func setTextColor(with textColor: UIColor) -> Builder {
        label.textColor = textColor
        return self
    }
    
    func setFontSize(with textFontSize: CGFloat) -> Builder {
        label.font = .systemFont(ofSize: textFontSize)
        return self
    }
}

이렇게 아직 존재하지 않는 객체지만, 만약 생성한다면 위에처럼 만들것 이라고 속성을 미리 정해둔다. 물론 값들은 실제 초기화시 받아야한다!

이제 이를 실제 사용하는 객체를 만들 것이다!

방법은 두가지가 있고 첫 번째는 메서드의 파라미터의 인자로 받아서 이를 활용한다.


class Director {
    func makeLabel(builder: Builder) -> UILabel {
        let build = builder
        build.setText(with: "Eddy")
        build.setFontSize(with: 40)
        build.setTextColor(with: .red)
        return build.label
    }
}

그리고 이를 실제 사용할 때에는 ViewController에서 인스턴스 생성 후 viewDidLoad()에서 아래와 같이 적어줌으로써 뷰에서 보여지도록 한다.

class ViewController: UIViewController {
    private let director: Director = Director()
    
    override func viewDidLoad() {
            super.viewDidLoad()
            
            let label = director.makeLabel(builder: ConCreateBuilder())
            self.view.addSubview(label)
            label.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
    }
}

그리고 또 다른 방식으로는 chain를 활용해서도 생성할 수 있다

그 코드는 아래와 같다

private let tmpLabel: UILabel = ConCreateBuilder()
    .setText(with: "eddy2")
    .setTextColor(with: .blue)
    .setFontSize(with: 30)
    .label

이처럼 chain으로 값을 정해주고 마지막에 label이라는 표시를 해준다! 그럼 어떤 타입인지 알려주게 되고

실제 이는 위에 label처럼 addSubView하고 위치를 잡아주면 된다.

 self.view.addSubview(tmpLabel)
        tmpLabel.frame = CGRect(x: 0, y: 200, width: 100, height: 100)
    }

그럼 아래와 같이 시뮬레이터에서 들어가는 것을 볼 수 있다.

이것을 더 업그레이드 시켜서 실제 프로젝트에서 Alert에 활용하는 것을 보여주려고한다!

기존의 빌더패턴 방식과 조금 다르지만, 사용해보며 차이점을 익혀보면 좋을 듯싶다.

제일 먼저 AlertAction이라면 필요한 것들을 선언해준다.

이곳에서는 title, alert style, completion 3가지가 필요하다!

import UIKit

struct AlertAction {
    let title: String
    let style: UIAlertAction.Style
    let completion: (() -> Void)?
}

그 다음에는 Builder Pattern를 활용해서 Alert의 템플릿?!을 만들면 된다.

먼저 ViewController를 가지고 있어야 하며 alertAction들을 담을 수 있는 배열을 생성한다.

그리고 Action를 넣는 메서드를 통해 AlertAction의 값들을 파라미터로 넣을 수 있도록 구현하고 이를 배열에 넣어준다.

그리고 show 메서드에서는 실제 alert를 보여주는 방식에 대해 작성하면 된다.

UIAlertController를 통해 alertController를 만들어 준다.

그리고 아까 만든 alertActions에 대한 배열을 반복문을 통해 alertAction를 만들어준다!

그리고 이것을 alertController 의 액션으로 추가해서 넣은 다음 ViewController 에서 실행시켜주면 된다.

원래였다면 호출부에서 선언하던 것들을 이곳에서 템플릿처럼 만들어 둔것뿐이다!

import UIKit

final class AlertBuilder {
    private weak var viewController: UIViewController?
    private var alertActions: [AlertAction] = []
    
    init(target: UIViewController) {
        self.viewController = target
    }
    
    func addAction(_ title: String, style: UIAlertAction.Style, action: (() -> Void)? = nil) -> Self {
        let alertAction = AlertAction(title: title, style: style, completion: action)
        alertActions.append(alertAction)
        
        return self
    }
    
    func show(_ title: String? = nil, message: String? = nil, style: UIAlertController.Style) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: style)
        alertActions.forEach { action in
            let alertAction = UIAlertAction(title: action.title, style: action.style) { _ in
                action.completion?()
            }
            alertController.addAction(alertAction)
        }
        viewController?.present(alertController, animated: true)
    }
}

실제 사용처에서는 아래와 같이 사용하면 된다.

builder를 호출하고, target에는 ViewController가 들어가므로 위치에 있는 self를 넣어준다.

그리고 필요한 액션을 추가해주면 된다.

그다음으로 이곳에서는 diary라는 모델을 사용하고 있는데 이는 이 행동을 통해 모델의 값들을 지우기 위해 만든 로직이다!

그래서 이 부분은 다르게 사용하고 싶다면 자신만의 방식으로 변경해도된다.

마지막에 보면 show를 통해 유저에게 alert 창을 보여줄 것이다.

이렇게 간단하게 사용이 가능해지고 여러 곳에서도 공통으로 쓸 수 있는 템플릿이 만들어진 것이다.

func showDeleteAlert() {
    AlertBuilder(target: self).addAction("취소", style: .default)
        .addAction("삭제", style: .destructive) { [weak self] in
        guard let diary = self?.diary else { return }
        self?.delegate?.delete(diary)
        self?.navigationController?.popViewController(animated: true)
    }.show("진짜요?", message: "정말로 삭제하시겠어요?", style: .alert)
}

결론적으로, 재사용성을 높일 수 있는 패턴같다!

만약 비슷한 작업이 이루어지지만 내부 속성만 바꾸어야 한다면 빌더패턴을 활용해서 반복작업을 줄일 수 있을 것이다.

참고 사이트

https://linsaeng.tistory.com/7

profile
Make a better world

0개의 댓글