Menu
Coddy logo textTech

빌더 패턴

Coddy Swift 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 48번째.

빌더 패턴은 복잡한 객체를 단계별로 조립합니다. 각 단계는 한 부분을 설정하고 빌더를 반환하므로 단계들을 연결할 수 있으며, 마지막으로 build()가 완성된 객체를 생성합니다:

struct Request {
    let url: String
    let method: String
    let headers: [String: String]
}

struct RequestBuilder {
    private var url = ""
    private var method = "GET"
    private var headers: [String: String] = [:]

    func url(_ u: String) -> RequestBuilder { var b = self; b.url = u; return b }
    func method(_ m: String) -> RequestBuilder { var b = self; b.method = m; return b }
    func header(_ k: String, _ v: String) -> RequestBuilder { var b = self; b.headers[k] = v; return b }
    func build() -> Request { Request(url: url, method: method, headers: headers) }
}

let req = RequestBuilder().url("/users").method("POST").header("Auth", "token").build()
print("\(req.method) \(req.url) \(req.headers.count) header")

출력:

POST /users 1 header

빌더는 객체를 생성하기 전에 전체 구성을 확인할 수 있으므로, 유효하지 않은 객체가 생성되는 일이 없습니다. 여기서 필요한 부분이 누락되면 build()가 오류를 발생시킵니다:

enum BuildError: Error { case missingURL }

struct Request { let url: String; let method: String }

struct RequestBuilder {
    private var url: String?
    private var method = "GET"
    func url(_ u: String) -> RequestBuilder { var b = self; b.url = u; return b }
    func method(_ m: String) -> RequestBuilder { var b = self; b.method = m; return b }
    func build() throws -> Request {
        guard let url else { throw BuildError.missingURL }
        return Request(url: url, method: method)
    }
}

do {
    _ = try RequestBuilder().method("PUT").build()
} catch {
    print("cannot build: \(error)")
}

출력:

cannot build: missingURL

각 단계는 구조체를 복사하고 복사본을 변경하므로, 일부만 구성된 빌더를 여러 객체의 템플릿으로 재사용할 수 있습니다:

struct PizzaBuilder {
    private var size = "medium"
    private var toppings: [String] = []
    func size(_ s: String) -> PizzaBuilder { var b = self; b.size = s; return b }
    func add(_ t: String) -> PizzaBuilder { var b = self; b.toppings.append(t); return b }
    func build() -> String { "\(size) pizza with " + (toppings.isEmpty ? "nothing" : toppings.joined(separator: ", ")) }
}

let base = PizzaBuilder().size("large").add("cheese")
print(base.add("ham").build())
print(base.add("olives").build())

출력:

large pizza with cheese, ham
large pizza with cheese, olives

Swift의 기본 매개변수 값은 이미 간단한 경우를 처리합니다(Request(url: "/", method: "GET")). 객체에 선택적 구성 요소가 많거나, 서로 의존하는 구성 요소가 있거나, 객체가 생성되기 전에 확인해야 할 규칙이 있을 때 빌더가 유용합니다.

// 단순: 기본 인자로 충분하다
struct Point { var x = 0, y = 0 }
// 복잡: 많은 선택적 부분과 검증 -> builder
let report = ReportBuilder().title("Q3").add(section: s1).add(section: s2).footer("draft").build()
challenge icon

챌린지

쉬움

이메일 메시지를 위한 빌더를 작성하세요. EmailBuilder에는 연결해서 사용할 수 있는 단계인 to(_:)(여러 번 호출하여 수신자를 추가할 수 있음), subject(_:), body(_:) 및 urgent()가 있습니다. build()는 Email을 반환하거나 EmailError.noRecipients 또는 EmailError.noSubject를 발생시킵니다. 긴급 이메일의 제목 앞에는 [URGENT] 가 붙습니다.

제공된 코드는 이메일마다 명령을 읽습니다. 명령은 --- 줄로 구분됩니다(to ada@x.io, subject Hello, body text, urgent를 입력한 다음 send 입력). 그리고 To: ada@x.io, bo@x.io | [URGENT] Hello | text, error: no recipients 또는 error: no subject를 출력합니다.

코드는 Email.swift, EmailError.swift 및 EmailBuilder.swift에 작성하세요. main.swift에는 제공된 입력/출력 코드가 들어 있으며 편집할 수 없습니다.

직접 해보기

// 제공된 입력/출력 코드: 그대로 유지하세요
var input: [String] = []
while let line = readLine() { input.append(line) }
var builder = EmailBuilder()
for line in input {
    let p = line.split(separator: " ", maxSplits: 1).map(String.init)
    switch p[0] {
    case "to": builder = builder.to(p[1])
    case "subject": builder = builder.subject(p[1])
    case "body": builder = builder.body(p[1])
    case "urgent": builder = builder.urgent()
    case "send":
        do {
            let e = try builder.build()
            print("To: \(e.recipients.joined(separator: ", ")) | \(e.subject) | \(e.body)")
        } catch EmailError.noRecipients {
            print("error: no recipients")
        } catch {
            print("error: no subject")
        }
    default: builder = EmailBuilder()
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: Swift 플레이그라운드