Software design patterns have been around for more than two decades. Should you use them with Swift, a programming language that’s only a couple of years old? Do you know which one to pick to solve a particular problem? Does Swift offer better alternatives? Perhaps it even renders some of these patterns obsolete? This course, along with the other two parts of the series, attempts to answer these questions. In this course, Design Patterns in Swift: Behavioral, you’ll learn about patterns that increase the efficiency and flexibility of how objects interact with each other. First, you'll see a demonstration of each pattern through practical demos implemented in Swift 4. Next, to emphasize the benefits of using reusable best practices, you'll also see comparisons of brute-force approaches with pattern-based solutions. Finally, you’ll see situations where Swift language features provide a more straightforward solution than the traditional design pattern. When you’re finished with this course, you'll have the skills and knowledge of applying design patterns needed to develop flexible, easy to enhance and maintain Swift projects.
Course Overview Hi everyone. My name is Karoly Nyisztor, and I'm happy to welcome you to my course, Design Patterns in Swift: Behavioral. I'm a tech entrepreneur, software engineer, and book author. Having been a professional iOS developer since 2009, I started using Swift right after its launch back in 2014. This course is an overview of the behavioral design patterns and the way they are implemented using Swift. It is the final part of a three-part series of courses on design patterns in Swift. The first two courses cover the creational and the structural design patterns. In this course, we're going to talk about the 11 behavioral design patterns. We'll start with the chain of responsibility, which stands at the core of the event handling in iOS and macOS. We're going to talk about the command pattern, which lets us package a method invocation as an object. We'll rely on the interpreter pattern to build a demo capable of processing navigation instructions and a calculator which uses complex expressions. We'll also talk about the frequently used iterator, observer, and the strategy pattern. These are just a few of the 11 patterns that we'll cover. By the end of this course, you should become very comfortable using the behavioral design patterns and applying them in your Swift projects. You will also gather a lot of Swift-related knowhow. Before beginning the course, you should be familiar with the basics of Swift programming. I hope you will join me on this journey to learn design patterns, with the Design Patterns in Swift: Behavioral course, here at Pluralsight.
Observer Let's talk about another behavioral design pattern called observer. If you've implemented an iOS or a Mac app, you've probably used the observer design pattern already. I'll start by introducing the concepts surrounding this pattern. Then, I'm going to implement two demos that are based on the observer. First, we'll create a virtual auction application where bidders get notified when the bid changes. Then, I'm going to refactor the demo to use the built-in NotificationCenter. Alright, so what's the observer pattern about? Basically, it's a way of notifying a group of objects about state changes in another object. The observers subscribe to receive notifications and the subject updates them when its state changes. The observer achieves this while keeping the subscribers independent from the subject's implementation. All of these objects remain loosely coupled. Here's the class diagram of the observer pattern. We've got an object referred to as the subject. This subject sends the notifications; it maintains a list of subscribed objects otherwise know as observers and notifies them of changes in its state. The observers need to subscribe to receive notifications from the subject. Here's a brief definition of the observer pattern. The observer allows subscribers to get notified about changes in another object without being tightly coupled to the sender. In the next video, we're going to implement an observer-based demo in Swift.
State The state design pattern is the object-oriented solution to monolithic conditional statements. First, I'm going to talk about the theory behind this pattern. Then, I'll refactor a demo application using the state design pattern. We'll end up having a better, easier to maintain version. Alright, so let's take a look at the state design pattern. The core idea is to create an object-oriented state machine. The state design pattern can be applied successfully to remove complex conditional logic from a software system. The conditional behavior gets encapsulated into separate types. Having dedicated types results in a more flexible system and makes our code easier to understand. Besides, adding new states or updating existing ones will require less effort. This UML diagram describes the players involved in the state design pattern. The context exposes the public interface to the callers; it delegates requests to its state. The state protocol defines the common interface for all concrete states. Finally, the concrete state types implement the behavior associated with the state of the context. Each state type provides its own implementation for a request, thus the context behavior will be different whenever its state changes. So, here's the formal definition. The state design pattern allows an object to behave differently when its internal state changes. In the next video, I'll show you an example of applying the state pattern to an existing code base.
Strategy The strategy pattern lets you change the behavior of an object without modifying its implementation. I'll show you in a moment how it works, but first let's take a closer look at the pattern. After discussing the details and the reasons behind using the strategy, I'm going to show you a real world application of this pattern. We're going to implement the versatile logger. The logger will support console and in-memory logging, and it will be easy to extend with new destinations. The strategy pattern allows us to change the behavior of a type by switching algorithms. The pattern works by defining a common interface for a family of algorithms. Each algorithm gets encapsulated in its own strategy type. The client only depends on the interface, so it can vary the algorithm by simply switching the concrete strategy object. To make it easier to understand, let's take a look at the following UML diagram. We've got the context, which delegates the client request to its strategy. The strategy is a protocol; it defines a common interface for all concrete algorithms. The concrete strategy types implement a specific algorithm. Clients instantiate the context with a concrete strategy object. The algorithm can then be updated by applying a different strategy instance to the context. The strategy pattern is about decoupling the algorithm implementation details from the type that uses it. This allows changing the object's behavior at runtime by switching algorithms.
Template Method In this module, we'll take a look at the template method design pattern. The template method defines the steps of an algorithm and allows some of the required steps to be modified. As usual, we'll talk first about the concepts surrounding this pattern. Then, I'm going to walk you through a demo, which implements the template design pattern using a protocol-oriented approach. So, when should you use the template method? The template method pattern is useful when you need to allow third parties to provide or replace some steps of an algorithm while other parts remain fixed. The fixed functionality is provided by the base type and can be overwritten. For the steps that can vary, the base type can provide so-called hook methods. The hook methods may have default or empty implementations in the base type. Clients can extend or override the behavior provided by the hook methods. The hook operations get called at specific points. The template method pattern allows extensions only at these predefined points; thus, clients can override certain steps of an algorithm without modifying its original structure. We can briefly summarize the pattern as follows: The template method allows specific steps in an algorithm to be replaced by clients without modifying its original structure.
Visitor In this module, I'm going to talk about the visitor design pattern. After discussing the details of this pattern, we'll implement a shopping demo in Swift that will let us calculate the total price of unrelated products. So, let's talk first about the motivation behind using the visitor pattern. The visitor allows adding common behavior to unrelated types. It should be used if we cannot or don't want to change the source code of the given types. The visitor works without subclassing. Instead, it separates the new behavior into a dedicated visitor. The visitor must visit each object and performs required operations on that object's state. If a new functionality is required, you must only extend the visitor, or create a new visitor for that matter. That being said, I'd add that Swift type extensions make it easy for us to define new behavior without modifying the original type. In other words, type extensions render the visitor pattern obsolete. And finally, here's the definition. The visitor lets us add new common behavior to unrelated types without changing their implementation. In the next lecture, I'm going to implement the classical visitor design pattern first. Then, I'll show you the type extension base solution.