The first publication of the Gang of Four's Design Patterns: Elements of Reusable Object- Oriented Software was released in October 1994. This book drew on the experience of four top object-oriented developers and presented a catalog of simple solutions to many commonly occurring design problems. These design patterns showed developers how to write flexible, easy to maintain and reusable code in an object-oriented way.
There are many sites and blog posts that talk about design patterns and show how to implement them, in various languages, in ways that follow the original designs from the Gang of Four’s book. While this works great for object-oriented languages such as Java, C# or C++, what about Swift? We could use Swift as an object-oriented language however if we follow Apple’s recommendations and give our preference to value types and think about the protocol first, how does that fit in with object-oriented design principles? How do protocol extensions, protocol inheritance or composition fit in? In my opinion, we shouldn’t use the same implementation strategies or design principles that we used in object-oriented languages such as Java or C#. Instead, we should take advantage of the features within the Swift language and implement them in a protocol-oriented way.
In this series of posts we have been looking at the problems that a number of the design patterns were design to solve but reimagining the solutions in a protocol oriented way with Swift. As we explain how we would reimagine these solutions it is our hope that it will helps you to begin to think in more of a protocol focused way.
The key is to think in a protocol focused way. The solutions that are presented in this series may not be the optimal way to solve the problem, but the solutions are presented in a protocol focused way to help us see how a protocol-oriented design would work.
There has been much discussion on Reddit where people make a comment like “You should use a function, protocol has too much boiler plate code”, or something to that effect. It is perfectly acceptable to use functions in the way people have mentioned but that does not demonstrate a protocol oriented approach which this series was designed to show. A solution that uses the least amount of code may not always be the best solution long term as your applications grows.
A perfect example of a protocol-oriented approach is the iterator design pattern <https://www.mastering-swift.com/post/iterator-pattern-protocol-oriented-design-pattern> that we presented last week. In order to traverse our custom data structure using the standard for…in… loop, all we needed to do was adopt the IteratorProtocol and Sequence protocols that is provided with Swift. By understanding how a protocol oriented design works, we can create similar functionality with our own APIs.
Before we start looking at the individual patterns lets give some background starting with what is a design pattern.
What are design patterns?
The concept of design patterns dates back to the mid-80s, however they did not gain popularity until the Gang of Four released their book Design Patterns: Elements of Reusable Object-Oriented Software. This book was published in 1994 and the authors, Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides discuss the pitfalls of object-oriented programming and describe 23 classic software design patterns. These patterns are broken up into three categories: creational, structural, and behavioral.
A design pattern identifies a common software development problem and provides an effective strategy in dealing with it. These strategies, identify by the design patterns, have been proven, over the years, to be an effective solution for the problems they are intended to solve.
The two main philosophies behind design patterns are flexibility and code reuse. As a software architect, it is essential that we build reusability and flexibility into our code. This allows us to easily maintain our code in the future and makes it easier for our applications to expand to meet future requirements, because we all know how quickly new requirements come in.
Using these patterns can also speed up the development process because they provide solutions that have already been proven to solve several common software development problems. Another advantage we get with design patterns is consistency within our code.
Design patterns should be considered the starting point for solving common programming problems and not the end-all solution. We can think of each design pattern as a recipe for a food dish. Just like a good recipe, we can tinker and adjust it to meet our particular tastes, but we usually do not want to stray too far from the original recipe because we may mess it up. There are also times when we do not have a recipe for a certain dish that we want to make, just like there are times when there isn't a design pattern to solve the problem we face. In cases such as these, we can use our knowledge of design patterns and their underlying philosophy to come up with an effective solution for our problem.
Design patterns can also be implemented in different ways depending on your code architecture. Some may implement them in the traditional object-oriented way, some may implement in a functional programming way, some may use prebuilt frameworks and others may use a protocol-oriented approach. The odds are, the idea behind the solution is very similar, just implemented in a different way which is why understanding design patterns and the idea behind them is so valuable.
Three Categories of Design Patterns
Creational patterns: Creational patterns support the creation of objects and how to effectively work with sometimes complex creation strategies. There are two basic ideas behind creational patterns. The first is the encapsulation of the knowledge of how instances of the types should be created, and the second involves hiding the complexity of how instances are created.
Structural patterns: Structural design patterns describe how types can be combined to form larger structures. These larger structures will generally be easier to work with and hide a lot of the complexity of the individual types. Most patterns in the structural pattern category involve the connection between these objects and how they work together.
Behavioral patterns: Behavioral design patterns explain how types interact with each other. These patterns describe how different instances of types send messages to each other in order to implement the business logic.
Posts on Protocol Oriented Design Patterns
We currently have the following posts that show how to implement certain design patterns in a protocol-oriented way. Follow us on Twitter to receive updates when new posts come out.
We will be updating this page as additional posts on design patterns are made but we may be taking a break from design patterns and we are (hopefully) starting another really cool Swift project which I will be sharing through our twitter account and writing about here. Follow us on twitter to find out first