디자인패턴 - Builder Pattern

어흥·2024년 5월 3일

Computer Science

목록 보기
12/28

정의

복잡하게 생성되어야 할 객체를 구현할 때 구성을 분리하여 다른 표현으로 만들 수 있게 하는 디자인 패턴

Swift 사용 예

  • Director input을 받고 builder와 이를 조정한다. iOS 개발에서는 viewcontrolller이나 viewcontroller에서 사용하는 도우미 클래스
  • Builder 단계별 입력을 받고 객체를 만든다. Builder는 클래스인 경우도 있어 참조하여 재사용이 가능하다.
  • Product 만들어지는 복잡한 객체이다. (구조체, 클래스 객체)

언제 사용하면 될까?

  • 단계를 거쳐서 객체를 생성하고 싶을 때
  • 객체를 구성하는 코드를 분리하고 싶을 때

장단점

장점

  • 엄청나게 많은 프로퍼티를 갖는 객체가 존재할 때 builder를 사용하면 캡슐화 효과를 얻어 모듈성 향상
  • 객체가 만들어지는 과정을 세밀하게 제어 가능 (단계별로 만들 수 있으므로)

단점

  • 복잡하지 않는 객체에 적용할 때 오히려 복잡성만 증가시킴

구현

  1. Product 정의
    햄버거 구조체 정의
public struct Hamburger {
    public let meat: Meat
    public let sauce: Set<Sauce>
    public let vegetable: Set<Vegetable>
    public let bread: Bread
    
    func getAllProperty() {
        print("Meat = \(meat)")
        print("Sauce = \(sauce)")
        print("Vegetable = \(vegetable)")
        print("Bread = \(bread)\n")
    }
}

public enum Meat: String {
    case beef
    case chicken
    case pork
}

public enum Sauce: String {
    case mayonnaise
    case mustard
    case ketchup
    case secret
}

public enum Vegetable: String {
    case cabbage
    case lettuce
    case pickels
    case tomatoes
}

public enum Bread: String {
    case brownBread
    case hotDogBun
    case mealBread
    case ryeBread
}
출처: https://icksw.tistory.com/236 [PinguiOS:티스토리]
  1. Builder 정의

    public class HamburgerBuilder {
        public private(set) var meat: Meat = .beef
        public private(set) var sauces: Set<Sauce> = []
        public private(set) var vegetables: Set<Vegetable> = []
        public private(set) var bread: Bread = .brownBread
        
        public func addSauce(_ sauce: Sauce) {
            sauces.insert(sauce)
        }
        public func removeSauce(_ sauce: Sauce) {
            sauces.remove(sauce)
        }
        public func addVegetable(_ vegetable: Vegetable) {
            vegetables.insert(vegetable)
        }
        public func removeVegetable(_ vegetable: Vegetable) {
            vegetables.remove(vegetable)
        }
        public func setMeat(_ meat: Meat) {
            self.meat = meat
        }
        public func setBread(_ bread: Bread) {
            self.bread = bread
        }
        
        public func build() -> Hamburger {
            return Hamburger(meat: meat, sauce: sauces, vegetable: vegetables, bread: bread)
        }
    }
  2. Director 정의
    고정된 모양의 햄버거를 만들 수 있다.

    ```swift
    class HamburgerDirector {
        public func createBeefBurger() -> Hamburger {
            let builder = HamburgerBuilder()
            builder.setMeat(.beef)
            builder.addSauce(.ketchup)
            builder.addSauce(.mustard)
            builder.addVegetable(.pickels)
            builder.addVegetable(.lettuce)
            builder.setBread(.ryeBread)
            return builder.build()
        }
        public func createChickenBurger() -> Hamburger {
            let builder = HamburgerBuilder()
            builder.setMeat(.chicken)
            builder.addSauce(.secret)
            builder.addSauce(.mayonnaise)
            builder.addVegetable(.cabbage)
            builder.addVegetable(.tomatoes)
            builder.setBread(.brownBread)
            return builder.build()
        }
    }
    ```

iOS에 적용하기!

커스텀 뷰를 만들거나 UIStackView를 사용할 때 적용할 수 있다.

  • 필요한 부분들을 서브 뷰로 만들어 addArrangedSubview(_:)로 만들어 추가
  1. Product 정의
class CustomView: UIView {

    private let containerView = CustomContainerView()

    private func configureContainerView() {
        // configure basic settings
    }

    func addTitle(_ title: String, spacing: CGFloat = 10) {
        let label = UILabel()
        ...
        containerView.stackView.addArrangedSubview(label)
    }

    func withSubTitle(_ subTitle: String) {
        let label = UILabel()
        ...
        containerView.stackView.addArrangedSubview(label)
    }

    func addImage(_ image: UIImage, size: CGSize) {
        let imageView = UIImageView(image: image)
        ...
        containerView.stackView.addArrangedSubview(imageView)
    }

    func addButtons(_ buttons: [CustomButton]) {
        containerView.addButtonStackView()
        buttons.forEach {
            containerView.buttonStackView.addArrangedSubview($0)
        }
    }

}
let customView1 = CustomViewBuilder()
	.withTitle("Notice")
	.withSubTitle("Are you sure to delete this list?")
	.withButtons([.cancelButton, .okButton])
	.build()

let customView2 = CustomViewBuilder()
	.withImage(defaultImage)
	.withButtons([.cancelButton, .okButton])
	.withTitle("Notice")
	.withSubTitle("Are you sure to delete this list?")
	.build()

0개의 댓글