diff --git a/DesignPatterns/AppleStoreBuilder/README.md b/DesignPatterns/AppleStoreBuilder/README.md index 9978923..687ace0 100644 --- a/DesignPatterns/AppleStoreBuilder/README.md +++ b/DesignPatterns/AppleStoreBuilder/README.md @@ -10,95 +10,187 @@ ## Pattern overview -- The Builder pattern allows for the construction of a complex object step by step. -- The pattern allows for the construction of different representations of the object using the same construction process. -- For example, consider a car. The car can be built with different features, such as the color, the interior, the wheels, and so on. -- The Builder pattern allows for the construction of a car with different features using the same construction process. +- The Builder pattern is used to construct objects piece by piece. + +- Each part of the object is configured via a method on a builder object. + +- We are able to provide different representations of the object using the same construction process. ## Problem statement -- Before placing an order on the Apple Store, various information comes together to create an order -- Products, product quantities, shipping information, and payment information. The order is created by combining all this information. -- The Builder pattern can be used to create an order by combining all this information. +- The Apple Store has a feature called the Apple Watch Studio, which allows customers to choose the size, material, and band for their Apple Watch. + +- There are different collections of Apple Watches, such as Series 10 and Hèrmes Series 10. + +- In the future, collections may be added or removed, and new sizes, materials, and bands may be introduced. + +- No matter which collection and options are chosen, the API where we submit configured Apple Watches remains the same. + +- We are faced with the challenges of constructing Apple Watches with various configurations and maintaining a consistent output. -## Domain application +- The Builder pattern gives us the needed flexibility in the construction process and allows us to conform to the requirements of the API. -Builder: +## Definitions -Specifies an abstract interface for creating parts of a Product object. +#### Product: + +The object that is being constructed. ```swift -protocol OrderBuilder { - func setProduct(product: Product) - func setQuantity(quantity: Int) - func setShippingAddress(address: String) - func setPaymentMethod(paymentMethod: String) - func build() -> Order +struct AppleWatch { + var collection: String + var size: String + var material: String + var band: String } ``` -ConcreteBuilder: +#### Builder: + +- The protocol that declares the options for constructing the product. + +- Associated types are used here so that each concrete builder can define its own enum types for size, material, and band. -- Constructs and assembles parts of the product by implementing the Builder interface. -- Defines and keeps track of the representation it creates. -- Provides an interface for retrieving the product. +- Compare the `Series10Builder` and `HèrmesSeries10Builder` enum types to see this in action. + +- The protocol makes use of a fluent interface, allowing for the chaining of builder methods. ```swift -class CheckoutOrderBuilder: OrderBuilder { - private var order = Order() +protocol AppleWatchBuilder { + associatedtype SizeType + associatedtype MaterialType + associatedtype BandType + + func setSize(_ size: SizeType) -> Self + func setMaterial(_ material: MaterialType) -> Self + func setBand(_ band: BandType) -> Self + func build() -> AppleWatch +} +``` + +#### Concrete builders: + +- Conforms to the builder protocol and provides an implementation for building the product. + +- Two concrete builders are defined here: `Series10Builder` and `HèrmesSeries10Builder`. + +- Each one is tailored to a specific collection of Apple Watch. - func setProduct(product: Product) { - order.product = product +- Type aliases are used here to pair the associated types for each concrete builder's enum type implementation. + +```swift +class Series10Builder: AppleWatchBuilder { + private var appleWatch = AppleWatch( + collection: "Series 10", + size: Size.fortyTwo.rawValue, + material: Material.aluminum.rawValue, + band: Band.sportBand.rawValue + ) + + enum Size: String { + case fortyTwo = "42mm" + case fortySix = "46mm" } - func setQuantity(quantity: Int) { - order.quantity = quantity + enum Material: String { + case aluminum = "Aluminum" + case titanium = "Titanium" } - func setShippingAddress(address: String) { - order.shippingAddress = address + enum Band: String { + case sportBand = "Sport Band" + case milaneseLoop = "Milanese Loop" + } + + typealias SizeType = Size + typealias MaterialType = Material + typealias BandType = Band + + func setSize(_ size: Size) -> Self { + appleWatch.size = size.rawValue + return self } - func setPaymentMethod(paymentMethod: String) { - order.paymentMethod = paymentMethod + func setMaterial(_ material: Material) -> Self { + appleWatch.material = material.rawValue + return self } - func build() -> Order { - return order + func setBand(_ band: Band) -> Self { + appleWatch.band = band.rawValue + return self + } + + func build() -> AppleWatch { + return appleWatch } } -``` -Director: +class HèrmesSeries10Builder: AppleWatchBuilder { + private var appleWatch = AppleWatch( + collection: "Hèrmes Series 10", + size: Size.fortyTwo.rawValue, + material: Material.titanium.rawValue, + band: Band.torsade.rawValue + ) + + enum Size: String { + case fortyTwo = "42mm" + case fortySix = "46mm" + } + + enum Material: String { + case titanium = "Titanium" + } -Constructs an object using the Builder interface. + enum Band: String { + case torsade = "Torsade Single Tour" + case grand = "Grand H" + } -```swift -class OrderDirector { - let builder: OrderBuilder + typealias SizeType = Size + typealias MaterialType = Material + typealias BandType = Band + + func setSize(_ size: Size) -> Self { + appleWatch.size = size.rawValue + return self + } - func construct() -> Order { - builder.setProduct(product: Product(name: "iPhone", price: 999.00)) - builder.setQuantity(quantity: 1) - builder.setShippingAddress(address: "123 7th Ave, New York, NY 10001") - builder.setPaymentMethod(paymentMethod: "Credit Card") + func setMaterial(_ material: Material) -> Self { + appleWatch.material = material.rawValue + return self + } + + func setBand(_ band: Band) -> Self { + appleWatch.band = band.rawValue + return self + } - return builder.build() + func build() -> AppleWatch { + return appleWatch } } ``` -Product: - -- Represents the complex object under construction. -- ConcreteBuilder builds the product's internal representation and defines the process by which it's assembled. -- Includes classes that define the constituent parts, including interfaces for assembling the parts into the final result. +## Example ```swift -struct Order { - var product: Product? - var quantity: Int? - var shippingAddress: String? - var paymentMethod: String? -} +let series10 = Series10Builder() + +print(series10.build()) // Default Series 10 Apple Watch +// Output: AppleWatch(collection: "Series 10", size: "42mm", material: "Aluminum", band: "Sport Band") + +series10.setSize(.fortySix).setMaterial(.titanium).setBand(.milaneseLoop) // Update Series 10 Apple Watch +print(series10.build()) +// Output: AppleWatch(collection: "Series 10", size: "46mm", material: "Titanium", band: "Milanese Loop") + +let hermesSeries10 = HèrmesSeries10Builder() + +print(hermesSeries10.build()) // Default Hèrmes Series 10 Apple Watch +// Output: AppleWatch(collection: "Hèrmes Series 10", size: "42mm", material: "Titanium", band: "Torsade Single Tour") + +hermesSeries10.setSize(.fortySix).setMaterial(.titanium).setBand(.grand) // Update Hèrmes Series 10 Apple Watch +print(hermesSeries10.build()) +// Output: AppleWatch(collection: "Hèrmes Series 10", size: "46mm", material: "Titanium", band: "Grand H") ```