Object-Oriented Programming: Beyond the Basics

Oct 17 2023 · Swift 5.9, iOS 17, Xcode 15

Lesson 05: Liskov Substitution, Interface Segregation & Dependency Inversion

Demo 3

Episode complete

Play next episode

Next
Transcript

In this demo, you’ll put the Dependency Inversion principle to work by applying it to the system you refactored in the first demo. I’ve moved the system initialzation underneath all the class initializations.

Open the starter playground to begin. It’s exactly like the final state from demo 1 in the previous lesson.

You have a system that does a math operation, localizes the result, shows it on the UI, then logs the whole operation.

LogManager handles multiple tasks to ensure that the logs are properly organized. The system gets the singleton instance directly to perform the logging operation.

The first step to create an abstraction between the system and LogManager is to create a protocol for LogManager.

To do this, add this new protocol before the declaration of LogManager:

protocol LoggingProtocol {
  func addLogEntry(_ entry: String)
}

Then, change the declaration of LogManager to conform to the new protocol:

class LogManager: LoggingProtocol {
 ...

The class already includes the addLogEntry(_ entry: String) method. You reverse-engineered a protocol containing the single function that any class responsible for logging should implement. This allows for loggers that upload entries to a server, save them in a database, or write them to various types of files.

Now, you need to update your system to depend on the new abstraction.

Start by updating System by adding a new property and a new initializer:

class System {
  var activeView = SimulatedView()
  var logger: LoggingProtocol // new code

  init(logger: LoggingProtocol) { // new code
     self.logger = logger
  }
...

Now, in doMainOperation(::), change the line of code that adds the log entry to the following:

logger.addLogEntry(logEntry)

You updated System to expect an instance that conforms to LoggingProtocol. The system doesn’t care how this instance works, whether it’s a singleton or not, how the instance is created or any details related to its construction. It only cares that you give it something it can use.

Finally, modify the last line in the playground — the one that creates a System instance — by passing it the LogManager singleton.

let system = System(logger: LogManager.singleton())

Build and run the playground. The printed message remains the same; nothing has changed in the functionality of the system. It’s still using the very same instance.

But your changes have made a key difference — you can now create new log managers and use them in your system without changing a single line of code!

See forum comments
Cinema mode Download course materials from Github
Previous: Instruction 3 Next: Conclusion