Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 120 additions & 59 deletions DesignPatterns/AppleStoreTemplateMethod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,96 +10,157 @@

## Pattern overview

- The Template Method pattern is a pattern that defines the skeleton of an algorithm in an operation, deferring some steps to subclasses.
- For example, consider an onboarding process that consists of multiple steps, such as creating an account, setting up a profile, and selecting preferences.
- The onboarding process can be implemented using the Template Method pattern by defining a template method that calls a series of steps to complete the onboarding process.
- The steps can be overridden by subclasses to customize the onboarding process based on the user's preferences.
- The Template Method pattern starts with defining a protocol listing steps in a sequence of operations we would like to perform.

- We then provide a default implementation for each step in the sequence, as well as define the method that orchestrates the sequence.

- Subclasses are then created to override the default implementation as needed.
Comment on lines +13 to +17
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clear, but do you understand the difference between subclassing and conforming to a protocol? Above you say "Subclasses are then created", but you don't subclass anything.


## Problem statement

- We would like to define a set of steps to rebuild an Apple Watch Studio configuration based on user input.
- The Template Method pattern allows us to define a template method that calls a series of steps to configure an Apple Watch Studio.
- The steps can be overridden by subclasses to customize the configuration based on the Apple Watch series.
- Apple Store customers are able to configure their Apple Watch using the Apple Watch Studio.

- Let's assume the configuration is stored in memory in a way that is convenient for the frontend to display, but not in the expected format needed to send to the backend.

- Each watch series also has a different data structure we need to convert from.

- We'll use the Template Method pattern to define the steps needed to convert the configuration to the expected format.

- The pattern will allow us to cater to different watch series by providing a custom implementation for each series.

- Each series would have its own implementation of the steps needed to convert the configuration to the expected format.

## Definitions

#### Abstract class:

## Domain application
- Defines the steps in the sequence needed to map the configuration to the expected format.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't define any sequence; you just define a set of methods to call. Remove the word sequence.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scrap that. We discussed this. Your code is missing the critical part which preserves the sequence.


AbstractClass:
- Provides a default implementation for each step in the sequence.

- Defines abstract primitive operations that concrete subclasses define to implement steps of an algorithm.
- Implements a template method defining the skeleton of an algorithm.
- The template method calls primitive operations as well as operations defined in AbstractClass or those of other objects.
- Defines the default implementation of `process`, which orchestrates the sequence.

- We provide a default implementation of the `AppleWatchConfiguration` called `DefaultAppleWatchConfiguration`, which marks the `process()` method as `final` to protect the process defined.

```swift
protocol AppleWatchConfiguration {
func templateMethod()

func selectWatchCaseSize()
func selectWatchCaseMaterial()
func selectWatchBand()
func selectWatchBandSize()
func selectWatchEngraving()
func process()
func mapDataForWatchCaseSize()
func mapDataForWatchCaseMaterial()
func mapDataForWatchBand()
func mapDataForWatchBandSize()
func mapDataForWatchEngraving()
}

extension AppleWatchConfiguration {
func templateMethod() {
selectWatchCaseSize()
selectWatchCaseMaterial()
selectWatchBand()
selectWatchBandSize()
selectWatchEngraving()
class DefaultAppleWatchConfiguration: AppleWatchConfiguration {
final func process() {
mapDataForWatchCaseSize()
mapDataForWatchCaseMaterial()
mapDataForWatchBand()
mapDataForWatchBandSize()
mapDataForWatchEngraving()
}

func selectWatchCaseMaterial() {
print("Configure default Apple Watch case material.")
func mapDataForWatchCaseSize() {
print("Default implementation for case size.")
}

func selectWatchBand() {
print("Configure default Apple Watch band.")
func mapDataForWatchCaseMaterial() {
print("Default implementation for case material.")
}

func selectWatchCaseSize() {
print("Configure default Apple Watch case size.")
func mapDataForWatchBand() {
print("Default implementation for watch band.")
}

func selectWatchBandSize() {
print("Configure Apple Watch band size.")
func mapDataForWatchBandSize() {
print("Default implementation for band size.")
}

func selectWatchEngraving() {
print("No engraving by default.")
func mapDataForWatchEngraving() {
print("Default implementation for engraving.")
}
}
```

ConcreteClass:
#### Concrete classes:

- Provides the custom configuration for each series.

- The Hermès Series 10 Apple Watch has a different configuration format than the Series 10 Apple Watch.

- The Hermès series only offers the titanium case material, which is why we can set it manually in the `mapDataForWatchCaseMaterial` method.

Implements the primitive operations to carry out subclass-specific steps of the algorithm.
- The Hermès series does not offer the engraving option, so we can skip the `mapDataForWatchEngraving` method.

- For simplicity, the data mapping implementations are omitted.

```swift
class Series10AppleWatchConfiguration: AppleWatchConfiguration {
func templateMethod() {
selectWatchCaseSize()
selectWatchCaseMaterial()
selectWatchBand()
selectWatchBandSize()
selectWatchEngraving()
class Series10AppleWatchConfiguration: DefaultAppleWatchConfiguration {
override func mapDataForWatchCaseSize() {
print("Implementation for converting the data structure for the Apple Watch Series 10 case size.")
}

override func mapDataForWatchCaseMaterial() {
print("Implementation for converting the data structure for the Apple Watch Series 10 case material.")
}

override func mapDataForWatchBand() {
print("Implementation for converting the data structure for the Apple Watch Series 10 band.")
}

override func mapDataForWatchBandSize() {
print("Implementation for converting the data structure for the Apple Watch Series 10 band size.")
}

override func mapDataForWatchEngraving() {
print("Implementation for converting the data structure for the Apple Watch Series 10 engraving.")
}
}

class HermèsSeries10AppleWatchConfiguration: AppleWatchConfiguration {
func templateMethod() {
selectWatchCaseSize()
selectWatchCaseMaterial()
selectWatchBand()
selectWatchBandSize()
class HermèsSeries10AppleWatchConfiguration: DefaultAppleWatchConfiguration {
override func mapDataForWatchCaseSize() {
print("Implementation for converting the data structure for the Apple Watch Hermès Series case size.")
}

func selectWatchCaseMaterial() {
print("Configure titanium for Apple Watch Hermès Series case material.")
override func mapDataForWatchCaseMaterial() {
print("Implementation for converting the data structure for the Apple Watch Hermès Series case material.")
}

func selectWatchBand() {
print("Configure Apple Watch Hermès Series specific band.")
override func mapDataForWatchBand() {
print("Implementation for converting the data structure for the Apple Watch Hermès Series band.")
}

override func mapDataForWatchBandSize() {
print("Implementation for converting the data structure for the Apple Watch Hermès Series band size.")
}

override func mapDataForWatchEngraving() {
print("Implementation for converting the data structure for the Apple Watch Hermès Series 10 engraving.")
}
}
```

## Example

```swift
let series10AppleWatchConfiguration = Series10AppleWatchConfiguration()
series10AppleWatchConfiguration.process()

// Output:
// Implementation for converting the front data structure for the Apple Watch Series 10 case size.
// Implementation for converting the front data structure for the Apple Watch Series 10 case material.
// Implementation for converting the front data structure for the Apple Watch Series 10 band.
// Implementation for converting the front data structure for the Apple Watch Series 10 band size.
// Implementation for converting the front data structure for the Apple Watch Series 10 engraving.

let hermèsSeries10AppleWatchConfiguration = HermèsSeries10AppleWatchConfiguration()
hermèsSeries10AppleWatchConfiguration.process()

// Output:
// Implementation for converting the data structure for the Apple Watch Hermès Series case size.
// Implementation for converting the data structure for the Apple Watch Hermès Series case material.
// Implementation for converting the data structure for the Apple Watch Hermès Series band.
// Implementation for converting the data structure for the Apple Watch Hermès Series band size.
// Implementation for converting the data structure for the Apple Watch Hermès Series 10 engraving.
```