Allergies and Dependency Inversion

A couple months ago, I posted about the Law of Demeter and how to keep your classes from requiring knowledge of its dependencies’ dependencies. The code for that post was contrived and I only refactored it far enough to demonstrate the Law of Demeter. However, after posting the article on LinkedIn, someone correctly pointed out that I neglected the Dependency Inversion Principle.

The Dependency Inversion Principle tells us that our higher-level modules should not depend on our lower-level modules. Let’s give dependency injection a try to demonstrate the principle.

Consider the following code:

class LawnService {
    func serviceLawn() {
        Employee().mowGrass()
    }
}

class Employee {
    func mowGrass(){
        LawnMower().cut()
    }
}

class LawnMower {
    func cut(){
        println("Hello allergies!")
    }
}

LawnService().serviceLawn()

 

So, what problems does the example code present?

  1. Isolating the different classes for testing purposes is not possible because of dependency on concrete classes.
  2. The code is not very flexible. What if I wanted to have multiple kinds of lawn mowers?

Let’s start with making it possible to isolate individual classes for testing purposes and see if that technique helps us with our second problem, too.

I need to be able to inject a stub or mock into LawnService, so that I can test it without testing the concrete Employee class. If I make a protocol for the employee and provide a mechanism for injecting an instance into the LawnService class, I should be able to do just that.

// Added a field for an instance of employee 
// that will use whatever concrete is passed 
// in on construction
class LawnService {
    var employee: LawnServiceEmployee
    
    init(_ employee: LawnServiceEmployee) {
        self.employee = employee
    }
    
    func serviceLawn() {
        self.employee.mowGrass()
    }
}

// Extract protocol for lawn service employees
protocol LawnServiceEmployee {
    func mowGrass()
}

// Employee now implemented the new protocol
class Employee: LawnServiceEmployee {
    func mowGrass(){
        LawnMower().cut()
    }
}

// A stub class used for testing
class EmployeeStub: LawnServiceEmployee {
    func mowGrass(){
        // do nothing, we're a stub
    }
}

// Nothing new here
class LawnMower {
    func cut(){
        println("Hello allergies!")
    }
}

// Injecting our real Employee 
// implementation
LawnService(Employee()).serviceLawn()

// Injecting our fake Employee
// implementation for tests
LawnService(EmployeeStub()).serviceLawn()

 

Also, I find it convenient to provide a default implementation if I know I’ll be using a particular concrete most of the time.

class LawnService {
    var employee: LawnServiceEmployee
    
    // Notice that we default to an instance of 
    // Employee
    init(employee: LawnServiceEmployee = Employee()) {
        self.employee = employee
    }
    
    func serviceLawn() {
        self.employee.mowGrass()
    }
}

// This call instantiates Employee and uses it
// rather that requiring it to be passed into
// the init
LawnService().serviceLawn()

 

Let’s do the same thing for lawn mowers, but this time we’ll provide real types instead of a testing fake.

class LawnService {
    var employee: LawnServiceEmployee
    
    init(employee: LawnServiceEmployee = Employee()) {
        self.employee = employee
    }
    
    func serviceLawn() {
        self.employee.mowGrass()
    }
}

protocol LawnServiceEmployee {
    func mowGrass()
}

// Added a field for an instance of lawn mower
// that will use whatever concrete is passed
// in on init
class Employee: LawnServiceEmployee {
    var lawnMower: LawnMower
    
    // defaulting to RidingMower may not be 
    // a good idea...
    init(lawnMower: LawnMower = RidingMower()){
        self.lawnMower = lawnMower
    }
    
    func mowGrass(){
        RidingMower().cut()
    }
}

// Extract protocol for lawn mowers
protocol LawnMower {
    func cut()
}

// One type of lawn mower
class RidingMower: LawnMower {
    func cut(){
        println("Riding along on my gas hog")
    }
}

// Another type of lawn mower
class ReelMower: LawnMower {
    func cut(){
        println("Getting some exercise")
    }
}

var mower = ReelMower()
var employee = Employee(lawnMower: mower)
LawnService(employee: employee).serviceLawn()

 

That’s all well and good, but I think I can clean things up a little more by creating a different kinds of lawn services: an eco-friendly lawn service and a standard lawn service.

class LawnService {
    private var employee: LawnServiceEmployee
    
    init(employee: LawnServiceEmployee) {
        self.employee = employee
    }
    
    func serviceLawn() {
        self.employee.mowGrass()
    }
}

// Standard lawn service has employees
// using riding lawn mowers
class StandardLawnService: LawnService {
    init() {
        var employee = Employee(lawnMower: RidingMower())
        super.init(employee: employee)
    }
}

// Eco-friendly lawn service has employees 
// using reel mowers
class EcoFriendlyLawnService: LawnService {
    init() {
        var employee = Employee(lawnMower: ReelMower())
        super.init(employee: employee)
    }
}

protocol LawnServiceEmployee {
    func mowGrass()
}

class Employee: LawnServiceEmployee {
    var lawnMower: LawnMower
    
    init(lawnMower: LawnMower){
        self.lawnMower = lawnMower
    }
    
    func mowGrass(){
        self.lawnMower.cut()
    }
}

protocol LawnMower {
    func cut()
}

class RidingMower: LawnMower {
    func cut(){
        println("Riding along on my gas hog")
    }
}

class ReelMower: LawnMower {
    func cut(){
        println("Getting some exercise")
    }
}

StandardLawnService().serviceLawn()
EcoFriendlyLawnService().serviceLawn()