Budowniczy (ang. Builder) to wzorzec pomagający nam w tworzeniu skomplikowanych obiektów. Poprzednio pokazałem implementację w Swift. Dziś jest kolaj na kod w Objective-C.
Opis wzorca: http://pl.wikipedia.org/wiki/Budowniczy_(wzorzec_projektowy)
Ponownie jak w przypadku Budowniczego w Swift, użyję przykładu z tworzeniem MacBooka. Zdefiniujmy sobie protokół, który pokażę nam zakres konfiguracji.
@protocol MacBuilder -(void) setStandardDisplay; -(void) setRetinaDisplay; -(void) set11Inches; -(void) set13Inches; -(void) set15Inches; -(void) setSSD; -(void) setHDD; -(void) setSSDandHDD; -(void) seti5; -(void) seti7; -(MacBook*) getResult; @end |
Skoro wiemy już jakie mamy opcje, to wypadałoby stworzyć klasę MacBook, która zamodeluje nam rzeczywisty obiekt.
@interface MacBook : NSObject @property (nonatomic) ScreenSize screenSize; @property (nonatomic) DisplayType displayType; @property (nonatomic) StorageType storageType; @property (nonatomic) CPUType cpuType; -(MacBook*)initWithDisplayType:(DisplayType)displayType withScreenSize:(ScreenSize)screenSize withStorageType:(StorageType)storageType withCpuType:(CPUType)cpuType; @end |
Opcje konfiguracji warto jest wydzielić tak jak ja powyżej, do odpowiednich, dedykowanych typów danych. W ten sposób zamkniemy drogę do potencjalnych błędów w bezsensownych konfiguracjach MacBooka, np ekran o wielkości 100 cali lub możliwość wsadzenia procesora AMD w ten komputer.
typedef enum : NSUInteger { SSD, HDD, SSD_HDD } StorageType; typedef enum : NSUInteger { Standard, Retina } DisplayType; typedef enum : NSUInteger { i5, i7, } CPUType; typedef enum : NSUInteger { s11inch, s13inch, s15inch, } ScreenSize; |
Teraz pora na Budowniczego:
#import <Foundation/Foundation.h> #import "MacBuilder.h" @interface MacBookBuilder : NSObject <MacBuilder> @end |
Czyli tworzymy sobie klasę, która implementuje zdefiniowany poprzednio protokół. Implementacja tej klasy może wyglądać na przykład tak
#import "MacBookBuilder.h" @implementation MacBookBuilder { StorageType storageType; DisplayType displayType; CPUType cpuType; ScreenSize screenSize; } -(void) setStandardDisplay { displayType = Standard; } -(void) setRetinaDisplay { displayType = Retina; } -(void) set11Inches { screenSize = s11inch; } -(void) set13Inches { screenSize = s13inch; } -(void) set15Inches { screenSize = s15inch; } -(void) setSSD { storageType = SSD; } -(void) setHDD { storageType = HDD; } -(void) setSSDandHDD { storageType = SSD_HDD; } -(void) seti5 { cpuType = i5; } -(void) seti7 { cpuType = i7; } -(MacBook*) getResult { MacBook* mb = [[MacBook alloc] initWithDisplayType: displayType withScreenSize: screenSize withStorageType: storageType withCpuType: cpuType]; return mb; } @end |
Następnie kolej na sprawdzenie, czy to rzeczywiście jest poprawne. W pliku BuilderTests.m dodajemy 3 przypadki testowe:
- (void)testBuildMacBookAir { MacBookBuilder* mbb = [[MacBookBuilder alloc] init]; [mbb set11Inches]; [mbb setStandardDisplay]; [mbb setSSD]; [mbb seti5]; MacBook* mb = [mbb getResult]; XCTAssertEqual(mb.cpuType, i5, @"CPU Incorrect"); XCTAssertEqual(mb.storageType, SSD, @"Storage Incorrect"); XCTAssertEqual(mb.displayType, Standard, @"Display Incorrect"); XCTAssertEqual(mb.screenSize, s11inch, @"Screen Size Incorrect"); } - (void)testBuildMacBookProRetina { MacBookBuilder* mbb = [[MacBookBuilder alloc] init]; [mbb set15Inches]; [mbb setRetinaDisplay]; [mbb setSSD]; [mbb seti7]; MacBook* mb = [mbb getResult]; XCTAssertEqual(mb.cpuType, i7, @"CPU Incorrect"); XCTAssertEqual(mb.storageType, SSD, @"Storage Incorrect"); XCTAssertEqual(mb.displayType, Retina, @"Display Incorrect"); XCTAssertEqual(mb.screenSize, s15inch, @"Screen Size Incorrect"); } - (void)testBuildMacBookPro { MacBookBuilder* mbb = [[MacBookBuilder alloc] init]; [mbb set13Inches]; [mbb setStandardDisplay]; [mbb setHDD]; [mbb seti5]; MacBook* mb = [mbb getResult]; XCTAssertEqual(mb.cpuType, i5, @"CPU Incorrect"); XCTAssertEqual(mb.storageType, HDD, @"Storage Incorrect"); XCTAssertEqual(mb.displayType, Standard, @"Display Incorrect"); XCTAssertEqual(mb.screenSize, s13inch, @"Screen Size Incorrect"); } |
Rezultat testów będzie jak na obrazku poniżej:
Budowniczy w Objective-C działa jak widać poprawnie.
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.