From adb1de9d81dc111a79cb4bd8935c418c706b3c49 Mon Sep 17 00:00:00 2001 From: Shaun Culver Date: Thu, 21 Nov 2024 07:09:51 +0200 Subject: [PATCH 1/6] Builder --- DesignPatterns/AppleStoreBuilder/README.md | 195 +++++++++++++++------ 1 file changed, 142 insertions(+), 53 deletions(-) diff --git a/DesignPatterns/AppleStoreBuilder/README.md b/DesignPatterns/AppleStoreBuilder/README.md index 9978923..cf620de 100644 --- a/DesignPatterns/AppleStoreBuilder/README.md +++ b/DesignPatterns/AppleStoreBuilder/README.md @@ -10,95 +10,184 @@ ## 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 builder methods. + +- 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. + +- To avoid the problem of steering away from this requirement, we can use the Builder pattern, which allows for flexibility in the construction process, while still maintaining a consistent output. -## Domain application +## Definitions -Builder: +#### Product: -Specifies an abstract interface for creating parts of a Product object. +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: -- 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. +- 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. + +- Compare the `AppleWatchSeries10` and `AppleWatchHèrmesSeries10` builder enum types to see this in action. + +- The protocol makes use of a fluent interface, allowing for chaining of builder methods. ```swift -class CheckoutOrderBuilder: OrderBuilder { - private var order = Order() +protocol AppleWatchBuilder { + var appleWatch: AppleWatch { get } - func setProduct(product: Product) { - order.product = product + associatedtype SizeType + associatedtype MaterialType + associatedtype BandType + + func setSize(_ size: SizeType) -> Self + func setMaterial(_ material: MaterialType) -> Self + func setBand(_ band: BandType) -> Self +} +``` + +#### Concrete builders: + +- Conforms to the builder protocol and provides an implementation for building the product. + +- Two concrete builders are defined here: `AppleWatchSeries10` and `AppleWatchHèrmesSeries10`. + +- Type aliases are used here to pair the associated types for each concrete builder's enum type implementation. + +```swift +class AppleWatchSeries10: AppleWatchBuilder { + 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" } - func setPaymentMethod(paymentMethod: String) { - order.paymentMethod = paymentMethod + typealias SizeType = Size + typealias MaterialType = Material + typealias BandType = Band + + func setSize(_ size: Size) -> Self { + appleWatch.size = size.rawValue + return self } - func build() -> Order { - return order + func setMaterial(_ material: Material) -> Self { + appleWatch.material = material.rawValue + return self + } + + func setBand(_ band: Band) -> Self { + appleWatch.band = band.rawValue + return self } } -``` -Director: +class AppleWatchHèrmesSeries10: AppleWatchBuilder { + var appleWatch = AppleWatch(collection: "Hèrmes Series 10", size: Size.fortyTwo.rawValue, material: Material.titanium.rawValue, band: Band.torsade.rawValue) -Constructs an object using the Builder interface. + enum Size: String { + case fortyTwo = "42mm" + case fortySix = "46mm" + } -```swift -class OrderDirector { - let builder: OrderBuilder + enum Material: String { + case titanium = "Titanium" + } + + enum Band: String { + case torsade = "Torsade Single Tour" + case grand = "Grand H" + } + + 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 + } - return builder.build() + func setBand(_ band: Band) -> Self { + appleWatch.band = band.rawValue + return self } } ``` -Product: +#### Director: + +- Maintains references to the builder objects. -- 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. +- Can be used to construct predefined product variations. ```swift -struct Order { - var product: Product? - var quantity: Int? - var shippingAddress: String? - var paymentMethod: String? +class AppleWatchStudio { + lazy var series10: AppleWatchSeries10 = { + let builder = AppleWatchSeries10() + return builder + }() + + lazy var hermesSeries10: AppleWatchHèrmesSeries10 = { + let builder = AppleWatchHèrmesSeries10() + return builder + }() } ``` + +## Example + +```swift +let appleWatchStudio = AppleWatchStudio() + +print(appleWatchStudio.series10.appleWatch) // Default Series 10 Apple Watch +// Output: AppleWatch(collection: "Series 10", size: "42mm", material: "Aluminum", band: "Sport Band") + +appleWatchStudio.series10.setSize(.fortySix).setMaterial(.titanium).setBand(.milaneseLoop) // Update Series 10 Apple Watch +print(appleWatchStudio.series10.appleWatch) +// Output: AppleWatch(collection: "Series 10", size: "46mm", material: "Titanium", band: "Milanese Loop") + +print(appleWatchStudio.hermesSeries10.appleWatch) // Default Hèrmes Series 10 Apple Watch +// Output: AppleWatch(collection: "Hèrmes Series 10", size: "42mm", material: "Titanium", band: "Torsade Single Tour") + +appleWatchStudio.hermesSeries10.setSize(.fortySix).setMaterial(.titanium).setBand(.grand) // Update Hèrmes Series 10 Apple Watch +print(appleWatchStudio.hermesSeries10.appleWatch) +// Output: AppleWatch(collection: "Hèrmes Series 10", size: "46mm", material: "Titanium", band: "Grand H") +``` From 4bd4ecf9ea22c9d4ece6509e3e83bdd6e666cce4 Mon Sep 17 00:00:00 2001 From: Shaun Culver Date: Thu, 21 Nov 2024 11:07:12 +0200 Subject: [PATCH 2/6] Updates --- DesignPatterns/AppleStoreBuilder/README.md | 36 ++++++---------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/DesignPatterns/AppleStoreBuilder/README.md b/DesignPatterns/AppleStoreBuilder/README.md index cf620de..3a38019 100644 --- a/DesignPatterns/AppleStoreBuilder/README.md +++ b/DesignPatterns/AppleStoreBuilder/README.md @@ -152,42 +152,24 @@ class AppleWatchHèrmesSeries10: AppleWatchBuilder { } ``` -#### Director: - -- Maintains references to the builder objects. - -- Can be used to construct predefined product variations. - -```swift -class AppleWatchStudio { - lazy var series10: AppleWatchSeries10 = { - let builder = AppleWatchSeries10() - return builder - }() - - lazy var hermesSeries10: AppleWatchHèrmesSeries10 = { - let builder = AppleWatchHèrmesSeries10() - return builder - }() -} -``` - ## Example ```swift -let appleWatchStudio = AppleWatchStudio() +let series10 = AppleWatchSeries10() -print(appleWatchStudio.series10.appleWatch) // Default Series 10 Apple Watch +print(series10.appleWatch) // Default Series 10 Apple Watch // Output: AppleWatch(collection: "Series 10", size: "42mm", material: "Aluminum", band: "Sport Band") -appleWatchStudio.series10.setSize(.fortySix).setMaterial(.titanium).setBand(.milaneseLoop) // Update Series 10 Apple Watch -print(appleWatchStudio.series10.appleWatch) +series10.setSize(.fortySix).setMaterial(.titanium).setBand(.milaneseLoop) // Update Series 10 Apple Watch +print(series10.appleWatch) // Output: AppleWatch(collection: "Series 10", size: "46mm", material: "Titanium", band: "Milanese Loop") -print(appleWatchStudio.hermesSeries10.appleWatch) // Default Hèrmes Series 10 Apple Watch +let hermesSeries10 = AppleWatchHèrmesSeries10() + +print(hermesSeries10.appleWatch) // Default Hèrmes Series 10 Apple Watch // Output: AppleWatch(collection: "Hèrmes Series 10", size: "42mm", material: "Titanium", band: "Torsade Single Tour") -appleWatchStudio.hermesSeries10.setSize(.fortySix).setMaterial(.titanium).setBand(.grand) // Update Hèrmes Series 10 Apple Watch -print(appleWatchStudio.hermesSeries10.appleWatch) +hermesSeries10.setSize(.fortySix).setMaterial(.titanium).setBand(.grand) // Update Hèrmes Series 10 Apple Watch +print(hermesSeries10.appleWatch) // Output: AppleWatch(collection: "Hèrmes Series 10", size: "46mm", material: "Titanium", band: "Grand H") ``` From 7ab5cd0ca051cde92904d0dcff539eed2f35e96f Mon Sep 17 00:00:00 2001 From: Shaun Culver Date: Thu, 21 Nov 2024 11:32:42 +0200 Subject: [PATCH 3/6] Update --- DesignPatterns/AppleStoreBuilder/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DesignPatterns/AppleStoreBuilder/README.md b/DesignPatterns/AppleStoreBuilder/README.md index 3a38019..e8019d2 100644 --- a/DesignPatterns/AppleStoreBuilder/README.md +++ b/DesignPatterns/AppleStoreBuilder/README.md @@ -51,7 +51,7 @@ struct AppleWatch { - Compare the `AppleWatchSeries10` and `AppleWatchHèrmesSeries10` builder enum types to see this in action. -- The protocol makes use of a fluent interface, allowing for chaining of builder methods. +- The protocol makes use of a fluent interface, allowing for the chaining of builder methods. ```swift protocol AppleWatchBuilder { From f4f61a0542739566e02296ebebfcb5b9e0a007e9 Mon Sep 17 00:00:00 2001 From: Shaun Culver Date: Thu, 21 Nov 2024 11:33:40 +0200 Subject: [PATCH 4/6] Update --- DesignPatterns/AppleStoreBuilder/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DesignPatterns/AppleStoreBuilder/README.md b/DesignPatterns/AppleStoreBuilder/README.md index e8019d2..6dc5e27 100644 --- a/DesignPatterns/AppleStoreBuilder/README.md +++ b/DesignPatterns/AppleStoreBuilder/README.md @@ -73,6 +73,8 @@ protocol AppleWatchBuilder { - Two concrete builders are defined here: `AppleWatchSeries10` and `AppleWatchHèrmesSeries10`. +- Each one is tailored to a specific collection of Apple Watch. + - Type aliases are used here to pair the associated types for each concrete builder's enum type implementation. ```swift From dc599c0112f5f7c364c4b34003695d8c406403e2 Mon Sep 17 00:00:00 2001 From: Shaun Culver Date: Mon, 25 Nov 2024 09:16:48 +0200 Subject: [PATCH 5/6] Updates --- DesignPatterns/AppleStoreBuilder/README.md | 49 +++++++++++++++------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/DesignPatterns/AppleStoreBuilder/README.md b/DesignPatterns/AppleStoreBuilder/README.md index 6dc5e27..cf7d51a 100644 --- a/DesignPatterns/AppleStoreBuilder/README.md +++ b/DesignPatterns/AppleStoreBuilder/README.md @@ -26,7 +26,9 @@ - No matter which collection and options are chosen, the API where we submit configured Apple Watches remains the same. -- To avoid the problem of steering away from this requirement, we can use the Builder pattern, which allows for flexibility in the construction process, while still maintaining a consistent output. +- We are faced with the challenges of constructing Apple Watches with various configurations and maintaining a consistent output. + +- The Builder pattern gives us the needed flexibility in the construction process and allows us to conform to the requirements of the API. ## Definitions @@ -49,14 +51,12 @@ struct AppleWatch { - Associated types are used here so that each concrete builder can define its own enum types for size, material, and band. -- Compare the `AppleWatchSeries10` and `AppleWatchHèrmesSeries10` builder enum types to see this in action. +- Compare the `Series10Builder` and `HèrmesSeries10Builder` builder enum types to see this in action. - The protocol makes use of a fluent interface, allowing for the chaining of builder methods. ```swift protocol AppleWatchBuilder { - var appleWatch: AppleWatch { get } - associatedtype SizeType associatedtype MaterialType associatedtype BandType @@ -64,6 +64,7 @@ protocol AppleWatchBuilder { func setSize(_ size: SizeType) -> Self func setMaterial(_ material: MaterialType) -> Self func setBand(_ band: BandType) -> Self + func build() -> AppleWatch } ``` @@ -71,15 +72,20 @@ protocol AppleWatchBuilder { - Conforms to the builder protocol and provides an implementation for building the product. -- Two concrete builders are defined here: `AppleWatchSeries10` and `AppleWatchHèrmesSeries10`. +- Two concrete builders are defined here: `Series10Builder` and `HèrmesSeries10Builder`. - Each one is tailored to a specific collection of Apple Watch. - Type aliases are used here to pair the associated types for each concrete builder's enum type implementation. ```swift -class AppleWatchSeries10: AppleWatchBuilder { - var appleWatch = AppleWatch(collection: "Series 10", size: Size.fortyTwo.rawValue, material: Material.aluminum.rawValue, band: Band.sportBand.rawValue) +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" @@ -114,10 +120,19 @@ class AppleWatchSeries10: AppleWatchBuilder { appleWatch.band = band.rawValue return self } + + func build() -> AppleWatch { + return appleWatch + } } -class AppleWatchHèrmesSeries10: AppleWatchBuilder { - var appleWatch = AppleWatch(collection: "Hèrmes Series 10", size: Size.fortyTwo.rawValue, material: Material.titanium.rawValue, band: Band.torsade.rawValue) +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" @@ -151,27 +166,31 @@ class AppleWatchHèrmesSeries10: AppleWatchBuilder { appleWatch.band = band.rawValue return self } + + func build() -> AppleWatch { + return appleWatch + } } ``` ## Example ```swift -let series10 = AppleWatchSeries10() +let series10 = Series10Builder() -print(series10.appleWatch) // Default Series 10 Apple Watch +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.appleWatch) +print(series10.build()) // Output: AppleWatch(collection: "Series 10", size: "46mm", material: "Titanium", band: "Milanese Loop") -let hermesSeries10 = AppleWatchHèrmesSeries10() +let hermesSeries10 = HèrmesSeries10Builder() -print(hermesSeries10.appleWatch) // Default Hèrmes Series 10 Apple Watch +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.appleWatch) +print(hermesSeries10.build()) // Output: AppleWatch(collection: "Hèrmes Series 10", size: "46mm", material: "Titanium", band: "Grand H") ``` From 5138df811349af18b6f5b8196287ff6eb0f2f921 Mon Sep 17 00:00:00 2001 From: Shaun Culver <45091059+xsdc@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:43:23 +0200 Subject: [PATCH 6/6] Update README.md --- DesignPatterns/AppleStoreBuilder/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DesignPatterns/AppleStoreBuilder/README.md b/DesignPatterns/AppleStoreBuilder/README.md index cf7d51a..687ace0 100644 --- a/DesignPatterns/AppleStoreBuilder/README.md +++ b/DesignPatterns/AppleStoreBuilder/README.md @@ -12,7 +12,7 @@ - The Builder pattern is used to construct objects piece by piece. -- Each part of the object is configured via builder methods. +- 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. @@ -51,7 +51,7 @@ struct AppleWatch { - Associated types are used here so that each concrete builder can define its own enum types for size, material, and band. -- Compare the `Series10Builder` and `HèrmesSeries10Builder` builder enum types to see this in action. +- 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.