Chapter 1. Introduction to Design Patterns
Good code is like a love letter to the next developer who will maintain it!
Design patterns provide a common vocabulary to structure code, making it easier to understand. They help enhance the quality of this connection to other developers. Knowledge of design patterns helps us identify recurring themes in requirements and map them to definitive solutions. We can rely on the experience of others who have encountered a similar problem and devised an optimized method to address it. This knowledge is invaluable as it paves the way for writing or refactoring code to make it maintainable.
Whether on the server or client, JavaScript is a cornerstone of modern web application development. The previous edition of this book focused on several popular design patterns in the JavaScript context. Over the years, JavaScript has significantly evolved as a language in terms of features and syntax. It now supports modules, classes, arrow functions, and template literals that it did not previously. We also have advanced JavaScript libraries and frameworks that have made life easy for many web developers. How relevant, then, are design patterns in the modern JavaScript context?
It’s important to note that traditionally, design patterns are neither prescriptive nor language-specific. You can apply them when you think they fit, but you don’t have to. Like data structures or algorithms, you can still apply classic design patterns using modern programming languages, including JavaScript. You may not need some of these design patterns in modern frameworks or libraries where they are already abstracted. Conversely, the use of specific patterns may even be encouraged by some frameworks.
In this edition, we are taking a pragmatic approach to patterns. We will explore why specific patterns may be the right fit for implementing certain features and if a pattern is still recommended in the modern JavaScript context.
As applications got more interactive, requiring a large amount of JavaScript, the language came under constant criticism for its negative impact on performance. Developers are continuously looking for new patterns that can optimize JavaScript performance. This edition highlights such improvements wherever relevant. We will also discuss framework-specific patterns such as React Hooks and Higher-Order Components that have become increasingly popular in the age of React.js.
Going back a step, let us start by exploring the history and importance of design patterns. If you’re already familiar with this history, feel free to skip to “What Is a Pattern?” to continue reading.
History of Design Patterns
Design patterns can be traced back to the early work of an architect named Christopher Alexander. He often wrote about his experiences in solving design issues and how they related to buildings and towns. One day, it occurred to Alexander that certain design constructs lead to a desired optimal effect when used repeatedly.
Alexander produced a pattern language in collaboration with two other architects, Sara Ishikawa and Murray Silverstein. This language would help empower anyone wishing to design and build at any scale. They published it in 1977 in a paper titled “A Pattern Language,” later released as a complete hardcover book.
Around 1990, software engineers began to incorporate the principles Alexander had written about into the first documentation about design patterns to guide novice developers looking to improve their coding skills. It’s important to note that the concepts behind design patterns have been around in the programming industry since its inception, albeit in a less formalized form.
One of the first and arguably the most iconic formal works published on design patterns in software engineering was a book in 1995 called Design Patterns: Elements of Reusable Object-Oriented Software—written by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Most engineers today recognize this group as the Gang of Four (GoF).
The GoF publication was particularly instrumental in pushing the concept of design patterns further in our field. It describes several development techniques and pitfalls and provides 23 core object-oriented design patterns frequently used worldwide today. We will cover these patterns in more detail in Chapter 6, and they also form the basis for our discussion in Chapter 7.
What Is a Pattern?
A pattern is a reusable solution template that you can apply to recurring problems and themes in software design. Similar to other programming languages, when building a JavaScript web application, you can use the template to structure your JavaScript code in different situations where you think it will help.
Learning and using design patterns is mainly advantageous for developers because of the following:
- Patterns are proven solutions.
-
They are the result of the combined experience and insights of developers who helped define them. They are time-tested approaches known to work when solving specific issues in software development.
- Patterns can be easily reused.
-
A pattern usually delivers an out-of-the-box solution you can adopt and adapt to suit your needs. This feature makes them quite robust.
- Patterns can be expressive.
-
Patterns can help express elegant solutions to extensive problems using a set structure and a shared vocabulary.
Additional advantages that patterns offer include the following:
- Patterns assist in preventing minor issues that can cause significant problems in the application development process.
-
When you use established patterns to build code, you can relax about getting the structure wrong and focus on the quality of the overall solution. The pattern encourages you to write more structured and organized code naturally, avoiding the need to refactor it for cleanliness in the future.
- Patterns provide generalized solutions, documented in a fashion that doesn’t require them to be tied to a specific problem.
-
This generalized approach means you can apply design patterns to improve code structure regardless of the application (and, in many cases, the programming language).
- Some patterns can decrease the overall code file-size footprint by avoiding repetition.
-
Design patterns encourage developers to look more closely at their solutions for areas where they can achieve instant reductions in duplication. For example, you can reduce the number of functions performing similar processes in favor of a single generalized function to decrease the size of your codebase. This is also known as making code more dry.
- Patterns add to a developer’s vocabulary, which makes communication faster.
-
Developers can reference the pattern when communicating with their team, when discussing it in the design patterns community, or indirectly when another developer later maintains the code.
- Popular design patterns can be improvised further by harnessing the collective experiences of developers using those patterns and contributing back to the community.
-
In some cases, this leads to the creation of entirely new design patterns, while in others, it can lead to improved guidelines on the usage of specific patterns. This can ensure that pattern-based solutions continue to become more robust than ad hoc ones.
An Everyday Use Case for Design Patterns
If you have used React.js, you have probably come across the Provider pattern. If not, you may have experienced the following situation.
The component tree in web applications often needs access to shared data such as user information or user access permissions. The traditional way to do this in JavaScript is to set these properties for the root level component and then pass them down from parent to child components. As the component hierarchy deepens and becomes more nested, you drill down it with your data, resulting in the practice of prop drilling. This leads to unmaintainable code where the property setting and passing will get repeated in every child component, which relies on that data.
React and a few other frameworks address this problem using the Provider pattern. With the Provider pattern, the React Context API can broadcast the state/data to multiple components via a context provider. Child components needing the shared data can tap into this provider as a context consumer or use the useContext
Hook.
This is an excellent example of a design pattern used to optimize the solution to a common problem. We will cover this and many such patterns in a lot of detail in this book.
Summary
With that introduction to the importance of design patterns and their relevance to modern JavaScript, we can now deep dive into learning JavaScript design patterns. The first few chapters in this book cover structuring and classifying patterns and identifying anti-patterns before we go into the specifics of design patterns for JavaScript. But first, let’s see what it takes for a proposed “proto-pattern” to be recognized as a pattern in the next chapter.
Get Learning JavaScript Design Patterns, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.