You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: Common Design Patterns/Creational/AbstractFactory/AbstractFactory.md
+74-47
Original file line number
Diff line number
Diff line change
@@ -1,10 +1,16 @@
1
1
# Abstract Factory Design Pattern
2
-
`Abstract Factory` is a creational design pattern that is aimed to simplify creation of objects at run-time without specifying their concrete types. The pattern has some similarities with another creational pattern called `Factory Method`. Don't be confused by the common nature and similar names of the patterns - they are different and have different restrictions.
2
+
`Abstract Factory` is a creational design pattern that is aimed to simplify creation of objects at run-time without specifying their concrete types. That is accomplished by incapsulating a group of related factories that operate the same domain-specific types.
3
3
4
-
`Abstract Factory` is implemented using a type rather than a method and can be `inherited` and `extended`. In order to implement the pattern we need to define a type that will act as an `abstract factory`and a `protocol` that will be used as the resulting type of the factory. Which type exactly - will be determined at runtime.
4
+
The pattern has some similarities with another creational pattern called `Factory Method`. Don't be confused by the common nature and similar names of the patterns - they are different and have different restrictions and purposes.
5
5
6
-
Our example will be built around the following task: we need to create a land vehicle based on whether there are zombies walking on the Earth or not. If zombie apocalypse happened - then we need a post-apocalyptic car that can shoot rockets or flame. Otherwise, if zombie apocalypse hasn't happened yet 😅, then our factorywill produce a very powerful sport car.
6
+
`Abstract Factory` is implemented using a type rather than a method, can be `inherited` and `extended`. The pattern hides the related factories that specifically know how to create concrete types that belong to the factory. In order to implement the pattern we need to define a type that will incapsulate the related sub-factories and a `protocol`that will be used as the resulting type of the factory. In some cases there might be several protocols, each for a separate sub-factory. That will allow to produce non-dependent types.
7
7
8
+
<!--act as an `abstract factory` and a `protocol` that will be used as the resulting type of the factory. Which type exactly - will be determined at runtime.-->
9
+
10
+
We will create two factories: one for creating `postapocalyptic vehicles` and the other will be used to create `regular vehicles`. Then we will build *Abstract Factory* that will be producing various types of vehicles, depending on non type-related properties and without exposing the underlying theme-specific factories.
11
+
12
+
<!--Our example will be the following: we need to create a land vehicle based on whether there are zombies walking on the Earth or not. If zombie apocalypse happened - then we need a post-apocalyptic car that can shoot rockets or flame. Otherwise, if zombie apocalypse hasn't happened yet 😅, then our factory will produce a very powerful sport car.
13
+
-->
8
14
## Implementation
9
15
10
16
We start from defining `LandVehicle` protocol that will be used as a runtime type for our abstract factory:
@@ -25,7 +31,7 @@ The protocol defines a set of properties that describe main components for our l
25
31
Then we create a concrete type for `SportCar` and add conformance to `LandVehicle` protocol:
@@ -61,11 +67,16 @@ class SportCar: LandVehicle, MovableProtocol, RotatableProtocol {
61
67
print("turned to the:", direction)
62
68
}
63
69
70
+
// MARK: - Conformance to TurboAcceleratable protocol
71
+
72
+
funcaccelerate() {
73
+
print("accelerated!")
74
+
}
64
75
}
65
76
66
77
```
67
78
68
-
`MovableProtocol` and `RotatableProtocol` are protocols that add certain functions for our `SportCar` type - they indicate what capabilites our vehicles have.
79
+
`MovableProtocol` and `RotatableProtocol` are protocols that add certain functions for our `SportCar` type - they indicate what capabilities our vehicles have.
69
80
70
81
The next type that we are going to implement is `PostapocalypticCar`:
71
82
@@ -78,9 +89,15 @@ class PostapocalypticCar: LandVehicle, MovableProtocol, RotatableProtocol, Shoot
78
89
var wheels: [Wheel]
79
90
var body: CarBody
80
91
92
+
// MARK: - Conformance to ShootableProtocol
93
+
94
+
var missileType: MissileType
95
+
81
96
// MARK: - Initializers
82
97
83
-
init() {
98
+
init(missileType: MissileType) {
99
+
self.missileType= missileType
100
+
84
101
let engineOne =Engine(id: "V8", horsepower: 850)
85
102
let engineTwo =Engine(id: "V6", horsepower: 550)
86
103
@@ -112,68 +129,78 @@ class PostapocalypticCar: LandVehicle, MovableProtocol, RotatableProtocol, Shoot
112
129
113
130
// MARK: - Conformance to ShootableProtocol
114
131
115
-
funcshoot(missileOftype: MissileType) {
116
-
print("shoot missile of type: ", type)
132
+
funcshoot() {
133
+
print("shoot missile of type: ", missileType)
117
134
}
118
-
119
135
}
120
136
```
121
-
Additionally `PostapocalypticCar` conforms to `ShootableProtocol` which adds shooting capability using `MissileType` (rockets, bullets or flame). The conformance to this protocol adds some differentiative characteristic to anti-zombie car.
137
+
Additionally `PostapocalypticCar` conforms to `ShootableProtocol` which adds shooting capability using `MissileType` (rockets, bullets or flame). The conformance to this protocol adds some differentiate characteristic to anti-zombie car.
122
138
139
+
Each of the `LandVehicle` types incapsulate the functionality related to the instantiation of `self`, so the factory will be free from set up code. Please note that it's not required, your factories may be responsible for setting up the related types.
123
140
124
-
Each of the `LandVehicle` types incapsulate the functionality related to the instantiation of `self`, so the factory will be free from set up code. Please note that it's not required, your abstract factory may be responsible for setting up the related types.
`SteampunkTruck` is similar to `PostapocalypticCar` except some type specific details (the code for *SteampunkTruck* can be found in the corresponding `.playground` file). The two factories specifically operate on either `Shootable` or `Regular` vehicles. For instance we can add some other land vehicle without a weapon system and add it to our `RegularVehicleFactory`.
The factory is implemented using a basic boolean rule that is used to determine which type will be instantiated at runtime and produced.
185
+
The factory is implemented using boolean values that are used to determine which type will be instantiated at runtime and then produced by the theme-specific factory.
153
186
154
187
```swift
155
-
// Create the factory
156
-
var vehicleFactory =VehicleFactory()
157
-
// Set the state of the factory to produce postapocalyptic land vehicles
158
-
vehicleFactory.areThereZombies=true
159
-
160
-
// Cast the produces vehicle as PostapocalypticCar
161
-
let postapocalypticCar = vehicleFactory.produce() as? PostapocalypticCar
162
-
// Shoot a rocket! 🚀 Kill all the zombies! 😄
163
-
postapocalypticCar?.shoot(missleOf: .rocket)
164
-
165
-
// After killing all the zombies we set the state of the factory to produce SportCar
166
-
vehicleFactory.areThereZombies=false
167
-
let sportCar = vehicleFactory.produce()
168
-
// Now we have a normal 🚗
169
-
```
188
+
// Create the factory 🏭
189
+
let vehicleFactory =VehicleFactory()
190
+
191
+
// Produce a vehicle with the specified properties
192
+
let steampunkTruck = vehicleFactory.produce(areThereZombies: false, hasWeapSystem: true) as? SteampunkTruck
193
+
// Use car-specific functionality
194
+
steampunkTruck?.shoot()
170
195
171
-
The usage of the factory is pretty straightforward:
172
196
173
-
- We instantiate the `Abstract Factory`
174
-
- We change the factory state to produce `PostapocalypticCar` type
175
-
- We shoot the rocket and kill all the zombies 🚀
176
-
- Then we again change the state of the factory to produce sport car 🚗
197
+
// Produce another vehicle based on different initial requirements
198
+
let sportCar = vehicleFactory.produce(areThereZombies: true, hasWeapSystem: false) as? SportCar
199
+
// Use turbo acceleration available for Sport Car 🚗:
200
+
sportCar?.accelerate()
201
+
```
202
+
203
+
The usage of the factory is pretty straightforward. We have created the factory for vehicles and then produced our first vehicle that is a regular vehicle but has weapon system. Our factory will delegate this work to `ShootableVehicleFactory` that will produce for us `SteampunkLandVehicle` instance. Next, we produce another vehicle but adopted for zombie apocalypse and without weapon system.
177
204
178
205
## Conclusion
179
-
`Abstract Factory`is a great and very easy to implement creational pattern! It provides means for creating objects based on external parameters, configure them differently and provide specific type capabilities based on run-time requirements. Since, the implementation is built around concrete type, we can use `type extension` and `inheritance`in order to create families of factories or domain specific factories. Use this pattern when you have relatedtypes and you don't want to expose the way they are constructed and when you have run-time factors that affect the type that needs to be selected.
206
+
`Abstract Factory` provides means for creating objects based on external parameters, configure them differently and provide specific type capabilities based on run-time requirements, by delegating the work to sub-factories that operate on theme-specific types. Since, the factory is built around concrete type, we can use `type extension` and `inheritance` to extend the capabilities. Use this pattern when you have related, unstructured types and you don't want to expose the way they are constructed. Also this pattern helps when you have run-time factors that affect the type that needs to be selected and constructed.
0 commit comments