Wzorce projektowe: Budowniczy (Builder) w Swift

Budowniczy (ang. Builder) to wzorzec pomagający nam w tworzeniu skomplikowanych obiektów. Dziś zapraszam na zapoznanie się z implementacją w Swift.

Opis wzorcahttp://pl.wikipedia.org/wiki/Budowniczy_(wzorzec_projektowy)

Wzorzec budowniczego ma ułatwić nam życie w przypadku tworzenia skomplikowanych obiektów, które składają się z wielu elementów. Zamiast tworzyć wiele konstruktorów, możemy mieć tylko jeden, a proces tworzenia podzielić na etapy konfiguracji w budowniczym a potem w jednym kroku stworzyć co trzeba.

Rozważmy przykład z tworzeniem produktu jakim jest MacBook. Ten komputer można kupić w 2 wersjach: Air i Pro. Każda z tych wersji posiada inne parametry (wielkość ekranu, rozdzielczość ekranu, rodzaj procesora, rodzaj dysku twardego). Poniższe przykłady kodu próbują właśnie zamodelować opisany problem konstrukcji MacBooka.

Zdefiniujmy więc interfejs takiego buildera.

// MacBuilder.swift
// Builder
 
import Foundation
 
protocol MacBuilder {
 
 func setStandardDisplay()
 func setRetinaDisplay()
 
 func set11Inches()
 func set13Inches()
 func set15Inches()
 
 func setSSD()
 func setHDD()
 func setSSDandHDD()
 
 func seti5()
 func seti7()
 
 func getResult() -> MacBook
 
}

Jak widać, metoda getResult zwraca obiekt klasy MacBook. Poniżej prezentuję definicję klasy MacBook modelującej nasz komputer.

class MacBook {
 let storageType : StorageType
 let displayType : DisplayType
 let cpuType : CPUType
 let screenSize : ScreenSize
 
 init(storage: StorageType, display: DisplayType, cpu: CPUType, screen : ScreenSize) {
 storageType = storage
 displayType = display
 cpuType = cpu
 screenSize = screen
 }
}

Atrubuty tej klasy, trzymające informację o konfiguracji danego MacBooka zostały zdefiniowane jako enumy. Poniżej ich definicje:

enum StorageType : Int {
 case SSD
 case HDD
 case SSD_HDD
}
 
enum DisplayType : Int {
 case Standard
 case Retina
}
 
enum CPUType : Int {
 case i5
 case i7
}
 
enum ScreenSize : Int {
 case s11inch = 11
 case s13inch = 13
 case s15inch = 15
}

Dzięki takiemu zabiegowi, mamy pewność, że konfiguracje będzie bazować na pewnym ograniczonym zbiorze możliwości i na przykład nie będzie można ustawić innej wielkości ekranu niż te 3 wymienione.

Przyszła kolej na kod naszego Budowniczego. Oto on poniżej:

// MacBookBuilder.swift
// Builder
 
import Foundation
 
class MacBookBuilder : MacBuilder {
 var display : DisplayType = DisplayType.Standard
 var screen : ScreenSize = ScreenSize.s13inch
 var storage : StorageType = StorageType.HDD
 var cpu : CPUType = CPUType.i5
 
 func setStandardDisplay() {
   display = DisplayType.Standard
 }
 func setRetinaDisplay() {
   display = DisplayType.Retina
 }
 
 func set11Inches() {
   screen = ScreenSize.s11inch
 }
 func set13Inches() {
   screen = ScreenSize.s13inch
 }
 func set15Inches() {
   screen = ScreenSize.s15inch
 }
 
 func setSSD() {
   storage = StorageType.SSD
 }
 func setHDD() {
   storage = StorageType.HDD
 }
 func setSSDandHDD() {
   storage = StorageType.SSD_HDD
 }
 
 func seti5() {
   cpu = CPUType.i5
 }
 func seti7() {
   cpu = CPUType.i7
 }
 
 func getResult() -> MacBook {
   let mb : MacBook = MacBook(storage: storage, display: display, cpu: cpu, screen: screen);
   return mb
 }
 
}

Kod prosty, czytelny i powiedziałbym oczywisty. Chyba nie ma się gdzie z nim pogubić. Jedyną uwagę jaką mam do niego to fakt, że Budowniczy trzyma domyślną konfigurację MacBooka. Tak więc jeśli chcemy dostać MacBooka z domyślnymi parametrami, nie musimy w ogóle używać metod konfiguracyjnych.

Przyszła pora na weryfikację powyższego kodu. Poniższy listing prezentuje kod 3 funkcji testowych

func testBuildMacBookAir() {
 let mbb = MacBookBuilder();
 mbb.set11Inches();
 mbb.setStandardDisplay();
 mbb.setSSD();
 mbb.seti5();
 let mb : MacBook = mbb.getResult();
 
 XCTAssertEqual(mb.cpuType, CPUType.i5, "CPU Incorrect");
 XCTAssertEqual(mb.storageType, StorageType.SSD, "Storage Incorrect");
 XCTAssertEqual(mb.displayType, DisplayType.Standard, "Display Incorrect");
 XCTAssertEqual(mb.screenSize, ScreenSize.s11inch, "Screen Size Incorrect");
 }
 
 func testBuildMacBookProRetina() {
 let mbb = MacBookBuilder()
 mbb.set15Inches()
 mbb.setRetinaDisplay()
 mbb.setSSD()
 mbb.seti7()
 
 let mb : MacBook = mbb.getResult()
 
 XCTAssertEqual(mb.cpuType, CPUType.i7, "CPU Incorrect");
 XCTAssertEqual(mb.storageType, StorageType.SSD, "Storage Incorrect");
 XCTAssertEqual(mb.displayType, DisplayType.Retina, "Display Incorrect");
 XCTAssertEqual(mb.screenSize, ScreenSize.s15inch, "Screen Size Incorrect");
 }
 
 func testBuildMacBookPro() {
 let mbb = MacBookBuilder()
 mbb.set13Inches()
 mbb.setStandardDisplay()
 mbb.setHDD()
 mbb.seti5()
 
 let mb : MacBook = mbb.getResult();
 
 XCTAssertEqual(mb.cpuType, CPUType.i5, "CPU Incorrect");
 XCTAssertEqual(mb.storageType, StorageType.HDD, "Storage Incorrect");
 XCTAssertEqual(mb.displayType, DisplayType.Standard, "Display Incorrect");
 XCTAssertEqual(mb.screenSize, ScreenSize.s13inch, "Screen Size Incorrect");
 }

Testy poprawnie się uruchamiają i kończą sukcesem. Nasz Budowniczy działa prawidłowo.

Builder Tests


Projekt Xcode i pliki źródłowe z przykładowym kodem można znaleźć tutaj.


Ten post jest częścią cyklu o wzorcach projektowych w Objective-C i Swift. Strona Wzorce projektowe w Objective-C i Swift – Kompendium  zbiera w jednym miejscu wzorce i przypisane do nich artykuły. Zapraszam do lektury.

Dodaj komentarz