This example will demonstrate how to use Dependency Injection (DI) design pattern in Swift using these methods:
protocol Engine {
func startEngine()
func stopEngine()
}
class TrainEngine: Engine {
func startEngine() {
print("Engine started")
}
func stopEngine() {
print("Engine stopped")
}
}
protocol TrainCar {
var numberOfSeats: Int { get }
func attachCar(attach: Bool)
}
class RestaurantCar: TrainCar {
var numberOfSeats: Int {
get {
return 30
}
}
func attachCar(attach: Bool) {
print("Attach car")
}
}
class PassengerCar: TrainCar {
var numberOfSeats: Int {
get {
return 50
}
}
func attachCar(attach: Bool) {
print("Attach car")
}
}
class Train {
let engine: Engine?
var mainCar: TrainCar?
}
As the name says, all dependencies are injected through the class initializer.
To inject dependencies through the initializer, we'll add the initializer to the Train
class.
Train class now looks like this:
class Train {
let engine: Engine?
var mainCar: TrainCar?
init(engine: Engine) {
self.engine = engine
}
}
When we want to create an instance of the Train class we'll use initializer to inject a specific Engine implementation:
let train = Train(engine: TrainEngine())
NOTE: The main advantage of the initializer injection versus the property injection is that we can set the variable as private variable or even make it a constant with the let
keyword (as we did in our example). This way we can make sure that no one can access it or change it.
DI using properties is even simpler that using an initializer. Let's inject a PassengerCar dependency to the train object we already created using the properties DI:
train.mainCar = PassengerCar()
That's it. Our train's mainCar
is now a PassengerCar
instance.
This type of dependency injection is a little different that the previous two because it won't affect the whole object, but it will only inject a dependency to be used in the scope of one specific method. When a dependency is only used in a single method, it's usually not good to make the whole object dependent on it. Let's add a new method to the Train class:
func reparkCar(trainCar: TrainCar) {
trainCar.attachCar(attach: true)
engine?.startEngine()
engine?.stopEngine()
trainCar.attachCar(attach: false)
}
Now, if we call the new Train's class method, we'll inject the TrainCar
using the method dependency injection.
train.reparkCar(trainCar: RestaurantCar())