빌더 패턴
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, olivesSwift의 기본 매개변수 값은 이미 간단한 경우를 처리합니다(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()챌린지
쉬움이메일 메시지를 위한 빌더를 작성하세요. 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()
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: Swift 플레이그라운드