Design patterns in software engineering are reusable, named solutions to recurring problems in software design. They are not finished code or libraries, but rather general templates—descriptions of a problem, the shape of a solution, and the trade-offs involved—that a programmer can adapt to their specific context. The study of design patterns is the study of these recurring structures: how to recognize a problem as an instance of a known type, how to choose an appropriate solution shape, and how to implement that shape without forcing it where it does not fit.
Software design is the activity of structuring code so that it is correct, readable, and adaptable to change. A central difficulty is that many design decisions involve competing pressures. A solution that is fastest to write may be hard to test; one that is flexible may be slower; one that is simple may not scale. Over time, experienced developers notice that certain combinations of classes and objects reappear in well-structured systems, and that certain mistakes recur in poorly structured ones. Design patterns are an attempt to capture the successful structures and the reasoning behind them.
The core question of the field is: What recurring structures of objects and classes solve common design problems, and under what conditions should each be used? This is distinct from algorithms, which solve computational problems with a specific sequence of steps. A pattern is more about the static and dynamic relationships between components—who knows about whom, who creates what, how responsibilities are divided—than about the logic inside any single component.
The idea of capturing design knowledge as named, reusable patterns did not originate in software. It came from architecture, specifically from the work of Christopher Alexander, who in the 1970s proposed that buildings and towns could be improved by following a "pattern language"—a structured set of problems and solutions, each with a name, a statement of the problem, and a resolution. Alexander's patterns were meant to be used by ordinary people, not just architects, and were tied to a philosophy of design that valued human experience over abstract formalism.
In the late 1980s, a small community of object-oriented programmers, many associated with the "Hillside Group" and the first conferences on object-oriented programming, began adapting Alexander's idea to software. They noticed that good object-oriented designs had recurring shapes: a way to let an object change its behavior at runtime, a way to add responsibilities to an object without modifying its class, a way to decouple a sender of a request from its receiver. The landmark publication was Design Patterns: Elements of Reusable Object-Oriented Software (1994), written by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides—commonly called the "Gang of Four." That book catalogued 23 patterns, organized into three categories: creational (how objects are created), structural (how classes and objects are composed), and behavioral (how objects interact and distribute responsibility).
The Gang of Four book was not the first to describe these ideas, but it was the first to present them in a consistent, widely accessible format. Each pattern was given a name, a motivation, a structure diagram, a set of participants, and a discussion of consequences and trade-offs. The names—Factory, Singleton, Observer, Strategy, Decorator, and others—became a shared vocabulary. A developer could say "use a Strategy here" and other developers would understand the intended shape of the solution, the alternatives, and the likely costs.
The 23 patterns of the Gang of Four remain the core of the field, though they are not the whole of it. Understanding them requires understanding the principles they embody. The most important principle is program to an interface, not an implementation. This means that code should depend on abstract types (interfaces or abstract classes) rather than on concrete classes, so that the concrete implementation can be changed without affecting the code that uses it. A second principle is favor object composition over class inheritance. Inheritance binds a subclass to its parent's implementation, which can be brittle; composition, where an object holds references to other objects and delegates to them, is more flexible at runtime.
The creational patterns address the question of how objects come into being. A naive approach—using new directly in client code—ties the client to a specific class. The Factory Method lets a subclass decide which class to instantiate. The Abstract Factory provides an interface for creating families of related objects without specifying their concrete classes. The Builder separates the construction of a complex object from its representation, so the same construction process can create different representations. The Prototype creates new objects by copying an existing one. The Singleton ensures a class has exactly one instance and provides a global point of access to it. The Singleton is often criticized for introducing global state and hiding dependencies, and many modern practitioners treat it as an anti-pattern to be used sparingly, if at all.
The structural patterns concern how classes and objects are combined to form larger structures. The Adapter converts the interface of one class into another interface that clients expect. The Bridge decouples an abstraction from its implementation so that both can vary independently. The Composite lets clients treat individual objects and compositions of objects uniformly, as in a tree of UI components where a single widget and a panel containing many widgets both respond to the same operations. The Decorator attaches additional responsibilities to an object dynamically, by wrapping it in another object that adds behavior. The Facade provides a simplified interface to a complex subsystem. The Flyweight shares common state among many fine-grained objects to save memory. The Proxy provides a stand-in for another object to control access, defer creation, or add logging.
The behavioral patterns focus on communication and responsibility. The Observer defines a one-to-many dependency so that when one object changes state, all its dependents are notified automatically. The Strategy defines a family of algorithms, encapsulates each one, and makes them interchangeable; the client can select an algorithm at runtime. The Command encapsulates a request as an object, allowing it to be parameterized, queued, or logged. The State lets an object alter its behavior when its internal state changes, so that the object appears to change its class. The Template Method defines the skeleton of an algorithm in a method, deferring some steps to subclasses. The Iterator provides a way to access elements of a collection sequentially without exposing its underlying representation. The Mediator centralizes communication between objects to reduce coupling. The Chain of Responsibility passes a request along a chain of handlers until one handles it. The Interpreter defines a grammar and a way to evaluate sentences in that grammar. The Memento captures and externalizes an object's internal state so it can be restored later. The Visitor lets you add operations to a set of classes without changing those classes, by placing the operation in a separate object that "visits" each class.
This catalog is not a checklist to be applied mechanically. The Gang of Four were explicit that patterns are solutions to specific problems in specific contexts, and that using a pattern when a simpler solution suffices is a mistake. A pattern's value lies in the trade-offs it makes explicit: the Observer decouples subjects from observers but can lead to subtle update-order bugs; the Visitor makes adding operations easy but makes adding new element classes hard; the Singleton is simple but makes testing difficult because it introduces hidden global state.
The classical catalog was written for a particular style of object-oriented programming, primarily in languages like C++ and Smalltalk. As software development evolved, so did the pattern concept. Several directions of development are important.
One direction is the expansion of the catalog itself. Later authors proposed additional patterns for specific domains: enterprise application architecture (patterns like Repository, Unit of Work, and Data Mapper, catalogued by Martin Fowler), concurrency (patterns for locking, thread pools, and message passing), and distributed systems (patterns for service discovery, circuit breakers, and event sourcing). These are not replacements for the original patterns but extensions into new problem areas.
Another direction is the critique and refinement of the original patterns. Some patterns have been re-evaluated as languages and practices changed. The Singleton, as noted, is widely considered problematic. The Model-View-Controller (MVC) pattern, which predates the Gang of Four and is often described as a pattern, is more of an architectural pattern—a pattern for the overall structure of an application rather than for a small cluster of classes. The distinction between design patterns (small, local structures) and architectural patterns (large-scale structures like layered architecture, pipes and filters, or microservices) became standard.
A third direction is the rise of anti-patterns: named, recurring bad solutions that look attractive but cause problems. Examples include the "God Object" (a class that knows too much and does too much), "Spaghetti Code" (unstructured, tangled control flow), and "Copy-and-Paste Programming." Anti-patterns are the negative image of design patterns; they give a name to common mistakes so that they can be recognized and avoided.
A fourth direction is the influence of new programming paradigms. Functional programming, for example, solves many of the same problems that object-oriented patterns solve, but with different tools. The Strategy pattern, which in an object-oriented language requires an interface and multiple implementing classes, in a functional language is often just a higher-order function passed as an argument. The Command pattern, which in Java requires a class with an execute method, in a language with first-class functions is simply a function value. This does not make the patterns obsolete; it shows that the underlying intent—decoupling, flexibility, separation of concerns—is more durable than any particular implementation. Many modern languages, such as Python, Ruby, and JavaScript, support both object-oriented and functional styles, and practitioners often mix them, using a pattern's intent rather than its literal class diagram.
Patterns are not independent atoms. They combine and overlap. A typical large system uses many patterns at once, and the patterns interact. For example, an Abstract Factory might create Strategy objects; a Decorator might wrap a Proxy; a Composite might contain Observers. The Gang of Four included a section on how patterns relate, and later authors have drawn "pattern languages" that show which patterns tend to be used together and in what order.
Some patterns are variations on a common theme. The Adapter, Proxy, Decorator, and Facade all involve wrapping one object with another, but they differ in intent: the Adapter changes an interface, the Proxy controls access, the Decorator adds behavior, and the Facade simplifies a subsystem. Recognizing these similarities and differences is part of pattern literacy. A developer who understands the underlying principle—delegation through wrapping—can see when a pattern is being used and can choose among the variants based on the goal.
There is also a relationship between patterns and principles. The SOLID principles (Single responsibility, Open-closed, Liskov substitution, Interface segregation, Dependency inversion) are often taught alongside patterns. Patterns are concrete realizations of these principles. The Strategy pattern, for instance, is a direct application of the Open-Closed Principle (open for extension, closed for modification): you can add a new algorithm without changing the code that uses it. The Dependency Inversion Principle—depend on abstractions, not concretions—is the foundation of most patterns. Understanding the principles helps a developer know when to use a pattern; understanding the patterns helps a developer know how to apply the principles.
In contemporary software engineering, the term "design pattern" is used broadly, sometimes loosely. The original Gang of Four patterns are still taught in university courses and appear in technical interviews, but their practical role has changed. In many modern codebases, the patterns are so deeply embedded in frameworks and languages that developers use them without naming them. A web framework's request pipeline is a Chain of Responsibility; a UI library's event system is an Observer; a dependency injection container is a kind of Abstract Factory. The patterns have become part of the infrastructure.
At the same time, the pattern concept has been extended and, in some quarters, criticized. Some practitioners argue that the emphasis on patterns can lead to over-engineering—applying a named solution where a simple conditional or a straightforward function would do. This critique is not new; the Gang of Four themselves warned against it. The durable lesson is that a pattern is a tool, not a goal. The goal is a design that is clear, correct, and maintainable; a pattern is useful only insofar as it serves that goal.
The field today is less a unified discipline with a single canon and more a shared vocabulary and a body of accumulated experience. Different communities emphasize different parts of it. Enterprise developers rely on patterns for transaction management and data access; game developers use patterns for game loops, component systems, and event handling; distributed-systems engineers use patterns for resilience and messaging. The common thread is the habit of naming and reusing structural solutions, and the recognition that design knowledge can be codified, taught, and improved.
The most important skill in this subfield is not memorizing the 23 patterns or any later catalog. It is the ability to see the problem behind the surface: to recognize that a piece of code is tangled because it mixes two responsibilities that should be separated, or that a change is hard because the code depends on a concrete class instead of an interface. Design patterns are a map of these recurring problems and their known solutions. The map is not the territory, but it helps a developer navigate.