Recent from talks
Contribute something
Nothing was collected or created yet.
Software design pattern
View on WikipediaIn software engineering, a software design pattern or design pattern is a general, reusable solution to a commonly occurring problem in many contexts in software design.[1] A design pattern is not a rigid structure to be transplanted directly into source code. Rather, it is a description or a template for solving a particular type of problem that can be deployed in many different situations.[2] Design patterns can be viewed as formalized best practices that the programmer may use to solve common problems when designing a software application or system.
Object-oriented design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved.[citation needed] Patterns that imply mutable state may be unsuited for functional programming languages. Some patterns can be rendered unnecessary in languages that have built-in support for solving the problem they are trying to solve, and object-oriented patterns are not necessarily suitable for non-object-oriented languages.[citation needed]
Design patterns may be viewed as a structured approach to computer programming intermediate between the levels of a programming paradigm and a concrete algorithm.[citation needed]
History
[edit]Patterns originated as an architectural concept by Christopher Alexander as early as 1977 in A Pattern Language (cf. his article, "The Pattern of Streets," JOURNAL OF THE AIP, September, 1966, Vol. 32, No. 5, pp. 273–278). In 1987, Kent Beck and Ward Cunningham began experimenting with the idea of applying patterns to programming – specifically pattern languages – and presented their results at the OOPSLA conference that year.[3][4] In the following years, Beck, Cunningham and others followed up on this work.
Design patterns gained popularity in computer science after the book Design Patterns: Elements of Reusable Object-Oriented Software was published in 1994 by the so-called "Gang of Four" (Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides), which is frequently abbreviated as "GoF". That same year, the first Pattern Languages of Programming Conference was held, and the following year the Portland Pattern Repository was set up for documentation of design patterns. The scope of the term remains a matter of dispute. Notable books in the design pattern genre include:
- Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 978-0-201-63361-0.
- Brinch Hansen, Per (1995). Studies in Computational Science: Parallel Programming Paradigms. Prentice Hall. ISBN 978-0-13-439324-7.
- Buschmann, Frank; Meunier, Regine; Rohnert, Hans; Sommerlad, Peter (1996). Pattern-Oriented Software Architecture, Volume 1: A System of Patterns. John Wiley & Sons. ISBN 978-0-471-95869-7.
- Beck, Kent (1997). Smalltalk Best Practice Patterns. Prentice Hall. ISBN 978-0134769042.
- Schmidt, Douglas C.; Stal, Michael; Rohnert, Hans; Buschmann, Frank (2000). Pattern-Oriented Software Architecture, Volume 2: Patterns for Concurrent and Networked Objects. John Wiley & Sons. ISBN 978-0-471-60695-6.
- Fowler, Martin (2002). Patterns of Enterprise Application Architecture. Addison-Wesley. ISBN 978-0-321-12742-6.
- Hohpe, Gregor; Woolf, Bobby (2003). Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions. Addison-Wesley. ISBN 978-0-321-20068-6.
- Freeman, Eric T.; Robson, Elisabeth; Bates, Bert; Sierra, Kathy (2004). Head First Design Patterns. O'Reilly Media. ISBN 978-0-596-00712-6.
- Larman, Craig (2004). Applying UML and Patterns (3rd Ed, 1st Ed 1995). Pearson. ISBN 978-0131489066.
Although design patterns have been applied practically for a long time, formalization of the concept of design patterns languished for several years.[5]
Practice
[edit]Design patterns can speed up the development process by providing proven development paradigms.[6] Effective software design requires considering issues that may not become apparent until later in the implementation. Freshly written code can often have hidden, subtle issues that take time to be detected; issues that sometimes can cause major problems down the road. Reusing design patterns can help to prevent such issues,[7] and enhance code readability for those familiar with the patterns.
Software design techniques are difficult to apply to a broader range of problems.[citation needed] Design patterns provide general solutions, documented in a format that does not require specifics tied to a particular problem.
In 1996, Christopher Alexander was invited to give a Keynote Speech to the 1996 OOPSLA Convention. Here he reflected on how his work on Patterns in Architecture had developed and his hopes for how the Software Design community could help Architecture extend Patterns to create living structures that use generative schemes that are more like computer code.
Motif
[edit]A pattern describes a design motif, a.k.a. prototypical micro-architecture, as a set of program constituents (e.g., classes, methods...) and their relationships. A developer adapts the motif to their codebase to solve the problem described by the pattern. The resulting code has structure and organization similar to the chosen motif.
Domain-specific patterns
[edit]Efforts have also been made to codify design patterns in particular domains, including the use of existing design patterns as well as domain-specific design patterns. Examples include user interface design patterns,[8] information visualization,[9] secure design,[10] "secure usability",[11] Web design [12] and business model design.[13]
The annual Pattern Languages of Programming Conference proceedings [14] include many examples of domain-specific patterns.
Object-oriented programming
[edit]Object-oriented design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved. Patterns that imply mutable state may be unsuited for functional programming languages. Some patterns can be rendered unnecessary in languages that have built-in support for solving the problem they are trying to solve, and object-oriented patterns are not necessarily suitable for non-object-oriented languages.
Examples
[edit]Design patterns can be organized into groups based on what kind of problem they solve. Creational patterns create objects. Structural patterns organize classes and objects to form larger structures that provide new functionality. Behavioral patterns describe collaboration between objects.
| Name | Description | In Design Patterns | In Code Complete[15] | Other |
|---|---|---|---|---|
| Abstract factory | Provide an interface for creating families of related or dependent objects without specifying their concrete classes. | Yes | Yes | — |
| Builder | Separate the construction of a complex object from its representation, allowing the same construction process to create various representations. | Yes | Yes | — |
| Dependency Injection | A class accepts the objects it requires from an injector instead of creating the objects directly. | — | Yes | — |
| Factory method | Define an interface for creating a single object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. | Yes | Yes | — |
| Lazy initialization | Tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. This pattern appears in the GoF catalog as "virtual proxy", an implementation strategy for the Proxy pattern. | Yes | Yes | PoEAA[16] |
| Multiton | Ensure a class has only named instances, and provide a global point of access to them. | Yes | Yes | Yes |
| Object pool | Avoid expensive acquisition and release of resources by recycling objects that are no longer in use. Can be considered a generalisation of connection pool and thread pool patterns. | Yes | Yes | Yes |
| Prototype | Specify the kinds of objects to create using a prototypical instance, and create new objects from the 'skeleton' of an existing object, thus boosting performance and keeping memory footprints to a minimum. | Yes | Yes | Yes |
| Resource acquisition is initialization (RAII) | Ensure that resources are properly released by tying them to the lifespan of suitable objects. | Yes | Yes | Yes |
| Singleton | Ensure a class has only one instance, and provide a global point of access to it. | Yes | Yes | Yes |
| Name | Description | In Design Patterns | In Code Complete[15] | Other |
|---|---|---|---|---|
| Adapter, Wrapper, or Translator | Convert the interface of a class into another interface clients expect. An adapter lets classes work together that could not otherwise because of incompatible interfaces. The enterprise integration pattern equivalent is the translator. | Yes | Yes | Yes |
| Bridge | Decouple an abstraction from its implementation allowing the two to vary independently. | Yes | Yes | Yes |
| Composite | Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. | Yes | Yes | Yes |
| Decorator | Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality. | Yes | Yes | Yes |
| Delegation | Extend a class by composition instead of subclassing. The object handles a request by delegating to a second object (the delegate) | Yes | Yes | Yes |
| Extension object | Adding functionality to a hierarchy without changing the hierarchy. | Yes | Yes | Yes |
| Facade | Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use. | Yes | Yes | Yes |
| Flyweight | Use sharing to support large numbers of similar objects efficiently. | Yes | Yes | Yes |
| Front controller | The pattern relates to the design of Web applications. It provides a centralized entry point for handling requests. | Yes | Yes | |
| Marker | Empty interface to associate metadata with a class. | Yes | Yes | Effective Java[19] |
| Module | Group several related elements, such as classes, singletons, methods, globally used, into a single conceptual entity. | Yes | Yes | Yes |
| Proxy | Provide a surrogate or placeholder for another object to control access to it. | Yes | Yes | Yes |
| Twin[20] | Twin allows modeling of multiple inheritance in programming languages that do not support this feature. | Yes | Yes | Yes |
| Name | Description | In Design Patterns | In Code Complete[15] | Other |
|---|---|---|---|---|
| Blackboard | Artificial intelligence pattern for combining disparate sources of data (see blackboard system) | Yes | Yes | Yes |
| Chain of responsibility | Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. | Yes | Yes | Yes |
| Command | Encapsulate a request as an object, thereby allowing for the parameterization of clients with different requests, and the queuing or logging of requests. It also allows for the support of undoable operations. | Yes | Yes | Yes |
| Fluent interface | Design an API to be method chained so that it reads like a DSL. Each method call returns a context through which the next logical method call(s) are made available. | Yes | Yes | Yes |
| Interpreter | Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. | Yes | Yes | Yes |
| Iterator | Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. | Yes | Yes | Yes |
| Mediator | Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it allows their interaction to vary independently. | Yes | Yes | Yes |
| Memento | Without violating encapsulation, capture and externalize an object's internal state allowing the object to be restored to this state later. | Yes | Yes | Yes |
| Null object | Avoid null references by providing a default object. | Yes | Yes | Yes |
| Observer or Publish/subscribe | Define a one-to-many dependency between objects where a state change in one object results in all its dependents being notified and updated automatically. | Yes | Yes | Yes |
| Servant | Define common functionality for a group of classes. The servant pattern is also frequently called helper class or utility class implementation for a given set of classes. The helper classes generally have no objects hence they have all static methods that act upon different kinds of class objects. | Yes | Yes | Yes |
| Specification | Recombinable business logic in a Boolean fashion. | Yes | Yes | Yes |
| State | Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. | Yes | Yes | Yes |
| Strategy | Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. | Yes | Yes | Yes |
| Template method | Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. | Yes | Yes | Yes |
| Visitor | Represent an operation to be performed on instances of a set of classes. Visitor lets a new operation be defined without changing the classes of the elements on which it operates. | Yes | Yes | Yes |
| Name | Description | In POSA2[21] | Other |
|---|---|---|---|
| Active Object | Decouples method execution from method invocation that reside in their own thread of control. The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests. | Yes | — |
| Balking | Only execute an action on an object when the object is in a particular state. | No | — |
| Binding properties | Combining multiple observers to force properties in different objects to be synchronized or coordinated in some way.[22] | No | — |
| Compute kernel | The same calculation many times in parallel, differing by integer parameters used with non-branching pointer math into shared arrays, such as GPU-optimized Matrix multiplication or Convolutional neural network. | No | — |
| Double-checked locking | Reduce the overhead of acquiring a lock by first testing the locking criterion (the 'lock hint') in an unsafe manner; only if that succeeds does the actual locking logic proceed.
Can be unsafe when implemented in some language/hardware combinations. It can therefore sometimes be considered an anti-pattern. |
Yes | — |
| Event-based asynchronous | Addresses problems with the asynchronous pattern that occur in multithreaded programs.[23] | No | — |
| Guarded suspension | Manages operations that require both a lock to be acquired and a precondition to be satisfied before the operation can be executed. | No | — |
| Join | Join-pattern provides a way to write concurrent, parallel and distributed programs by message passing. Compared to the use of threads and locks, this is a high-level programming model. | No | — |
| Lock | One thread puts a "lock" on a resource, preventing other threads from accessing or modifying it.[24] | No | PoEAA[16] |
| Messaging design pattern (MDP) | Allows the interchange of information (i.e. messages) between components and applications. | No | — |
| Monitor object | An object whose methods are subject to mutual exclusion, thus preventing multiple objects from erroneously trying to use it at the same time. | Yes | — |
| Reactor | A reactor object provides an asynchronous interface to resources that must be handled synchronously. | Yes | — |
| Read-write lock | Allows concurrent read access to an object, but requires exclusive access for write operations. An underlying semaphore might be used for writing, and a Copy-on-write mechanism may or may not be used. | No | — |
| Scheduler | Explicitly control when threads may execute single-threaded code. | No | — |
| Service handler pattern | For each request, a server spawns a dedicated client handler to handle a request.[25] Also referred to as thread-per-session.[26] | No | — |
| Thread pool | A number of threads are created to perform a number of tasks, which are usually organized in a queue. Typically, there are many more tasks than threads. Can be considered a special case of the object pool pattern. | No | — |
| Thread-specific storage | Static or "global" memory local to a thread. | Yes | — |
| Safe Concurrency with Exclusive Ownership | Avoiding the need for runtime concurrent mechanisms, because exclusive ownership can be proven. This is a notable capability of the Rust language, but compile-time checking isn't the only means, a programmer will often manually design such patterns into code - omitting the use of locking mechanism because the programmer assesses that a given variable is never going to be concurrently accessed. | No | — |
| CPU atomic operation | x86 and other CPU architectures support a range of atomic instructions that guarantee memory safety for modifying and accessing primitive values (integers). For example, two threads may both increment a counter safely. These capabilities can also be used to implement the mechanisms for other concurrency patterns as above. The C# language uses the Interlocked class for these capabilities. | No | — |
Documentation
[edit]The documentation for a design pattern describes the context in which the pattern is used, the forces within the context that the pattern seeks to resolve, and the suggested solution.[27] There is no single, standard format for documenting design patterns. Rather, a variety of different formats have been used by different pattern authors. However, according to Martin Fowler, certain pattern forms have become more well-known than others, and consequently become common starting points for new pattern-writing efforts.[28] One example of a commonly used documentation format is the one used by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides in their book Design Patterns. It contains the following sections:
- Pattern Name and Classification: A descriptive and unique name that helps in identifying and referring to the pattern.
- Intent: A description of the goal behind the pattern and the reason for using it.
- Also Known As: Other names for the pattern.
- Motivation (Forces): A scenario consisting of a problem and a context in which this pattern can be used.
- Applicability: Situations in which this pattern is usable; the context for the pattern.
- Structure: A graphical representation of the pattern. Class diagrams and Interaction diagrams may be used for this purpose.
- Participants: A listing of the classes and objects used in the pattern and their roles in the design.
- Collaboration: A description of how classes and objects used in the pattern interact with each other.
- Consequences: A description of the results, side effects, and trade offs caused by using the pattern.
- Implementation: A description of an implementation of the pattern; the solution part of the pattern.
- Sample Code: An illustration of how the pattern can be used in a programming language.
- Known Uses: Examples of real usages of the pattern.
- Related Patterns: Other patterns that have some relationship with the pattern; discussion of the differences between the pattern and similar patterns.
Criticism
[edit]Some suggest that design patterns may be a sign that features are missing in a given programming language (Java or C++ for instance). Peter Norvig demonstrates that 16 out of the 23 patterns in the Design Patterns book (which is primarily focused on C++) are simplified or eliminated (via direct language support) in Lisp or Dylan.[29] Related observations were made by Hannemann and Kiczales who implemented several of the 23 design patterns using an aspect-oriented programming language (AspectJ) and showed that code-level dependencies were removed from the implementations of 17 of the 23 design patterns and that aspect-oriented programming could simplify the implementations of design patterns.[30] See also Paul Graham's essay "Revenge of the Nerds".[31]
Inappropriate use of patterns may unnecessarily increase complexity.[32] FizzBuzzEnterpriseEdition offers a humorous example of over-complexity introduced by design patterns.[33]
By definition, a pattern must be programmed anew into each application that uses it. Since some authors see this as a step backward from software reuse as provided by components, researchers have worked to turn patterns into components. Meyer and Arnout were able to provide full or partial componentization of two-thirds of the patterns they attempted.[34]
In order to achieve flexibility, design patterns may introduce additional levels of indirection, which may complicate the resulting design and decrease runtime performance.
Relationship to other topics
[edit]Software design patterns offer finer granularity compared to software architecture patterns and software architecture styles, as design patterns focus on solving detailed, low-level design problems within individual components or subsystems. Examples include Singleton, Factory Method, and Observer.[35][36][37]
Software Architecture Pattern refers to a reusable, proven solution to a recurring problem at the system level, addressing concerns related to the overall structure, component interactions, and quality attributes of the system.[citation needed] Software architecture patterns operate at a higher level of abstraction than design patterns, solving broader system-level challenges. While these patterns typically affect system-level concerns, the distinction between architectural patterns and architectural styles can sometimes be blurry. Examples include Circuit Breaker.[35][36][37]
Software Architecture Style refers to a high-level structural organization that defines the overall system organization, specifying how components are organized, how they interact, and the constraints on those interactions.[citation needed] Architecture styles typically include a vocabulary of component and connector types, as well as semantic models for interpreting the system's properties. These styles represent the most coarse-grained level of system organization. Examples include Layered Architecture, Microservices, and Event-Driven Architecture.[35][36][37]
See also
[edit]- Abstraction principle
- Algorithmic skeleton
- Anti-pattern
- Architectural pattern
- Canonical protocol pattern
- Debugging patterns
- Design pattern
- Distributed design patterns
- Enterprise Architecture framework
- GRASP (object-oriented design)
- Helper class
- Idiom in programming
- Interaction design pattern
- List of software architecture styles and patterns
- List of software development philosophies
- List of software engineering topics
- Pattern language
- Pattern theory
- Pedagogical patterns
- Portland Pattern Repository
- Refactoring
- Software development methodology
References
[edit]- ^ Alexandrescu, Andrei (2001). Modern C++ Design: Generic Programming and Design Patterns Applied. Addison-Wesley. p. xviii. ISBN 978-0-201-70431-0.
- ^ Horner, Mark (2005). "9". Pro .NET 2.0 Code and Design Standards in C#. Apress. p. 171. ISBN 978-1-59059-560-2.
- ^ Smith, Reid (October 1987). Panel on design methodology. OOPSLA '87 Addendum to the Proceedings. doi:10.1145/62138.62151.
Ward cautioned against requiring too much programming at, what he termed, 'the high level of wizards.' He pointed out that a written 'pattern language' can significantly improve the selection and application of abstractions. He proposed a 'radical shift in the burden of design and implementation' basing the new methodology on an adaptation of Christopher Alexander's work in pattern languages and that programming-oriented pattern languages developed at Tektronix has significantly aided their software development efforts.
- ^ Beck, Kent; Cunningham, Ward (September 1987). Using Pattern Languages for Object-Oriented Program. OOPSLA '87 workshop on Specification and Design for Object-Oriented Programming. Retrieved 2006-05-26.
- ^ Baroni, Aline Lúcia; Guéhéneuc, Yann-Gaël; Albin-Amiot, Hervé (June 2003). Design Patterns Formalization (Report). EMN Technical Report. Nantes: École Nationale Supérieure des Techniques Industrielles et des Mines de Nantes. CiteSeerX 10.1.1.62.6466. S2CID 624834 – via ResearchGate.
- ^ Bishop, Judith. "C# 3.0 Design Patterns: Use the Power of C# 3.0 to Solve Real-World Problems". C# Books from O'Reilly Media. Retrieved 2012-05-15.
If you want to speed up the development of your .NET applications, you're ready for C# design patterns -- elegant, accepted and proven ways to tackle common programming problems.
- ^ Tiako, Pierre F. (31 March 2009). "Formal Modeling and Specification of Design Patterns Using RTPA". In Tiako, Pierre F (ed.). Software Applications: Concepts, Methodologies, Tools, and Applications: Concepts, Methodologies, Tools, and Applications. p. 636. doi:10.4018/978-1-60566-060-8. ISBN 9781605660615.
- ^ Laakso, Sari A. (2003-09-16). "Collection of User Interface Design Patterns". University of Helsinki, Dept. of Computer Science. Retrieved 2008-01-31.
- ^ Heer, J.; Agrawala, M. (2006). "Software Design Patterns for Information Visualization". IEEE Transactions on Visualization and Computer Graphics. 12 (5): 853–60. CiteSeerX 10.1.1.121.4534. doi:10.1109/TVCG.2006.178. PMID 17080809. S2CID 11634997.
- ^ Dougherty, Chad; Sayre, Kirk; Seacord, Robert C.; Svoboda, David; Togashi, Kazuya (2009). Secure Design Patterns (PDF). Software Engineering Institute.
- ^ Garfinkel, Simson L. (2005). Design Principles and Patterns for Computer Systems That Are Simultaneously Secure and Usable (Ph.D. thesis).
- ^ "Yahoo! Design Pattern Library". Archived from the original on 2008-02-29. Retrieved 2008-01-31.
- ^ "How to design your Business Model as a Lean Startup?". 2010-01-06. Retrieved 2010-01-06.
- ^ Pattern Languages of Programming, Conference proceedings (annual, 1994—) [1]
- ^ a b c McConnell, Steve (June 2004). "Design in Construction". Code Complete (2nd ed.). Microsoft Press. p. 104. ISBN 978-0-7356-1967-8.
Table 5.1 Popular Design Patterns
- ^ a b Fowler, Martin (2002). Patterns of Enterprise Application Architecture. Addison-Wesley. ISBN 978-0-321-12742-6.
- ^ Alur, Deepak; Crupi, John; Malks, Dan (2003). Core J2EE Patterns: Best Practices and Design Strategies. Prentice Hall. p. 166. ISBN 978-0-13-142246-9.
- ^ Fowler, Martin (2002). Patterns of Enterprise Application Architecture. Addison-Wesley. p. 344. ISBN 978-0-321-12742-6.
- ^ Bloch, Joshua (2008). "Item 37: Use marker interfaces to define types". Effective Java (Second ed.). Addison-Wesley. p. 179. ISBN 978-0-321-35668-0.
- ^ "Twin – A Design Pattern for Modeling Multiple Inheritance" (PDF).
- ^ Schmidt, Douglas C.; Stal, Michael; Rohnert, Hans; Buschmann, Frank (2000). Pattern-Oriented Software Architecture, Volume 2: Patterns for Concurrent and Networked Objects. John Wiley & Sons. ISBN 978-0-471-60695-6.
- ^ Binding Properties
- ^ Nagel, Christian; Evjen, Bill; Glynn, Jay; Watson, Karli; Skinner, Morgan (2008). "Event-based Asynchronous Pattern". Professional C# 2008. Wiley. pp. 570–571. ISBN 978-0-470-19137-8.
- ^ Lock Pattern
- ^ Francalanza, Adrian; Tabone, Gerard (October 2023). "ElixirST: A session-based type system for Elixir modules". Journal of Logical and Algebraic Methods in Programming. 135. doi:10.1016/j.jlamp.2023.100891. S2CID 251442539.
- ^ Schmidt, Douglas C.; Vinoski, Steve (July–August 1996). "Object Interconnections: Comparing Alternative Programming Techniques for Multi-threaded CORBA Servers (Column 7)" (PDF). SIGS C++ Report. S2CID 2654843.
- ^ Gabriel, Dick. "A Pattern Definition". Archived from the original on 2007-02-09. Retrieved 2007-03-06.
- ^ Fowler, Martin (2006-08-01). "Writing Software Patterns". Retrieved 2007-03-06.
- ^ Norvig, Peter (1998). Design Patterns in Dynamic Languages.
- ^ Hannemann, Jan; Kiczales, Gregor (2002). "Design pattern implementation in Java and AspectJ". Proceedings of the 17th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications - OOPSLA '02. OOPSLA '02. p. 161. doi:10.1145/582419.582436. ISBN 1581134711.
- ^ Graham, Paul (2002). "Revenge of the Nerds". Retrieved 2012-08-11.
- ^ McConnell, Steve (2004). Code Complete: A Practical Handbook of Software Construction, 2nd Edition. Pearson Education. p. 105. ISBN 9780735619678.
- ^ Kragbæk, Mikael. "FizzBuzzEnterpriseEdition". Retrieved 2024-11-19.
- ^ Meyer, Bertrand; Arnout, Karine (July 2006). "Componentization: The Visitor Example" (PDF). IEEE Computer. 39 (7): 23–30. CiteSeerX 10.1.1.62.6082. doi:10.1109/MC.2006.227. S2CID 15328522.
- ^ a b c Fundamentals of Software Architecture: An Engineering Approach. O'Reilly Media. 2020. ISBN 978-1492043454.
- ^ a b c Design Patterns: Elements of Reusable Object-Oriented Software. ISBN 978-0201633610.
- ^ a b c Patterns of Enterprise Application Architecture. ISBN 978-0321127426.
Further reading
[edit]- Alexander, Christopher; Ishikawa, Sara; Silverstein, Murray; Jacobson, Max; Fiksdahl-King, Ingrid; Angel, Shlomo (1977). A Pattern Language: Towns, Buildings, Construction. New York: Oxford University Press. ISBN 978-0-19-501919-3.
- Alur, Deepak; Crupi, John; Malks, Dan (May 2003). Core J2EE Patterns: Best Practices and Design Strategies (2nd ed.). Prentice Hall. ISBN 978-0-13-142246-9.
- Beck, Kent (October 2007). Implementation Patterns. Addison-Wesley. ISBN 978-0-321-41309-3.
- Beck, Kent; Crocker, R.; Meszaros, G.; Coplien, J. O.; Dominick, L.; Paulisch, F.; Vlissides, J. (March 1996). Proceedings of the 18th International Conference on Software Engineering. pp. 25–30.
- Borchers, Jan (2001). A Pattern Approach to Interaction Design. John Wiley & Sons. ISBN 978-0-471-49828-5.
- Coplien, James O.; Schmidt, Douglas C. (1995). Pattern Languages of Program Design. Addison-Wesley. ISBN 978-0-201-60734-5.
- Coplien, James O.; Vlissides, John M.; Kerth, Norman L. (1996). Pattern Languages of Program Design 2. Addison-Wesley. ISBN 978-0-201-89527-8.
- Eloranta, Veli-Pekka; Koskinen, Johannes; Leppänen, Marko; Reijonen, Ville (2014). Designing Distributed Control Systems: A Pattern Language Approach. Wiley. ISBN 978-1118694152.
- Fowler, Martin (1997). Analysis Patterns: Reusable Object Models. Addison-Wesley. ISBN 978-0-201-89542-1.
- Fowler, Martin (2003). Patterns of Enterprise Application Architecture. Addison-Wesley. ISBN 978-0-321-12742-6.
- Freeman, Eric; Freeman, Elisabeth; Sierra, Kathy; Bates, Bert (2004). Head First Design Patterns. O'Reilly Media. ISBN 978-0-596-00712-6.
- Hohmann, Luke; Fowler, Martin; Kawasaki, Guy (2003). Beyond Software Architecture. Addison-Wesley. ISBN 978-0-201-77594-5.
- Gabriel, Richard (1996). Patterns of Software: Tales From The Software Community (PDF). Oxford University Press. p. 235. ISBN 978-0-19-512123-0. Archived from the original (PDF) on 2003-08-01.
- Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 978-0-201-63361-0.
- Hohpe, Gregor; Woolf, Bobby (2003). Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions. Addison-Wesley. ISBN 978-0-321-20068-6.
- Holub, Allen (2004). Holub on Patterns. Apress. ISBN 978-1-59059-388-2.
- Kircher, Michael; Völter, Markus; Zdun, Uwe (2005). Remoting Patterns: Foundations of Enterprise, Internet and Realtime Distributed Object Middleware. John Wiley & Sons. ISBN 978-0-470-85662-8.
- Larman, Craig (2005). Applying UML and Patterns. Prentice Hall. ISBN 978-0-13-148906-6.
- Liskov, Barbara; Guttag, John (2000). Program Development in Java: Abstraction, Specification, and Object-Oriented Design. Addison-Wesley. ISBN 978-0-201-65768-5.
- Manolescu, Dragos; Voelter, Markus; Noble, James (2006). Pattern Languages of Program Design 5. Addison-Wesley. ISBN 978-0-321-32194-7.
- Marinescu, Floyd (2002). EJB Design Patterns: Advanced Patterns, Processes and Idioms. John Wiley & Sons. ISBN 978-0-471-20831-0.
- Martin, Robert Cecil; Riehle, Dirk; Buschmann, Frank (1997). Pattern Languages of Program Design 3. Addison-Wesley. ISBN 978-0-201-31011-5.
- Mattson, Timothy G.; Sanders, Beverly A.; Massingill, Berna L. (2005). Patterns for Parallel Programming. Addison-Wesley. ISBN 978-0-321-22811-6.
- Shalloway, Alan; Trott, James R. (2001). Design Patterns Explained, Second Edition: A New Perspective on Object-Oriented Design. Addison-Wesley. ISBN 978-0-321-24714-8.
- Vlissides, John M. (1998). Pattern Hatching: Design Patterns Applied. Addison-Wesley. ISBN 978-0-201-43293-0.
- Weir, Charles; Noble, James (2000). Small Memory Software: Patterns for systems with limited memory. Addison-Wesley. ISBN 978-0-201-59607-6. Archived from the original on 2007-06-17.
Software design pattern
View on GrokipediaFundamentals
Definition and Purpose
A software design pattern is a general, reusable solution to a commonly occurring problem in software design, consisting of a description of communicating objects and classes customized to address a general design problem within a particular context.[3] Unlike a finished design or concrete code, it serves as a template that identifies the key aspects of a recurring structure, allowing developers to adapt it flexibly without repeating the same implementation.[7] The primary purpose of software design patterns is to promote code reuse by encapsulating proven solutions, thereby enhancing the flexibility, elegance, and maintainability of software systems.[3] They provide a shared vocabulary that improves communication among development teams, reduces the complexity of large-scale systems through standardized approaches, and facilitates ongoing maintenance by making design intentions explicit and easier to evolve.[7] In object-oriented programming, patterns particularly leverage mechanisms like encapsulation and polymorphism to achieve these goals. The concept of design patterns in software draws foundational influence from architectural patterns developed by Christopher Alexander, whose work emphasized reusable descriptions of solutions to recurring environmental design problems. This analogy underscores how software patterns adapt similar principles to abstract, context-specific challenges in code architecture, promoting harmonious and adaptable system designs.[3] Software design patterns address diverse problem areas in engineering practice, such as managing object creation to avoid tight coupling, organizing system structure for scalability, and handling interactions between components to ensure loose dependencies and behavioral flexibility.[7] By targeting these issues, patterns enable developers to build robust applications that are easier to extend and debug over time.Key Characteristics
Software design patterns are distinguished by their context-specific nature, meaning they address recurring problems within particular environmental constraints and requirements of a software system, rather than offering universal solutions applicable in isolation.[8] This specificity ensures that patterns are tailored to the problem's context, such as object interactions or system scalability needs, making them adaptable yet not blindly replicable. Additionally, patterns are abstract, providing high-level blueprints or templates that guide implementation without prescribing exact code, allowing developers to customize them to fit diverse programming languages and scenarios.[8] They are also proven, having been validated through real-world application and refinement over time, which lends them reliability as tested solutions to common design challenges.[8] Furthermore, patterns are composable, enabling them to be combined with one another to form more complex structures that address multifaceted problems effectively.[3] A well-defined software design pattern typically comprises several essential elements that facilitate its understanding and application. The name serves as a concise identifier, allowing developers to reference and discuss the pattern succinctly in communication and documentation.[3] The problem description outlines the specific design issue it solves, including the forces or constraints at play, such as flexibility versus efficiency.[3] The solution structure describes the core arrangement of classes, objects, and interactions, often illustrated with UML diagrams to visualize relationships and responsibilities.[3] Consequences detail the trade-offs involved, such as gains in maintainability at the potential cost of increased complexity or performance overhead.[3] Finally, implementation considerations offer practical guidance on applying the pattern, including known uses, variations, and potential pitfalls.[3] In terms of abstraction levels, software design patterns occupy a middle ground in the design hierarchy: they operate at a higher level of abstraction than algorithms or data structures, which focus on low-level computational steps or memory organization, but at a lower level than full system architectures, which define overarching system organization and component interactions.[9] This positioning allows patterns to bridge detailed implementation with broader structural goals, providing reusable solutions that enhance design without dictating the entire system's blueprint.[9] The adoption of software design patterns yields significant benefits, including improved code readability through a shared vocabulary that streamlines team collaboration and documentation.[10] They also promote scalability by offering adaptable, time-tested approaches that facilitate system growth and maintenance.[10] However, these advantages come with trade-offs; misapplication can lead to over-engineering, where unnecessary complexity is introduced to simple problems, potentially increasing development time and reducing performance without commensurate gains.[11]Historical Development
Origins in Architecture and Software
The concept of design patterns traces its roots to architecture, where Christopher Alexander and his collaborators introduced the notion of a "pattern language" in their 1977 book A Pattern Language: Towns, Buildings, Construction. This work outlined 253 patterns as proven, reusable solutions to recurring problems in designing towns, buildings, and living spaces, emphasizing timeless principles that foster human-centered environments.[12] Alexander's patterns were structured as descriptive templates, each capturing a problem, context, and solution to promote harmonious and adaptable habitats.[13] In the 1980s, software engineers facing the complexities of object-oriented programming adapted Alexander's architectural patterns to create reusable abstractions for code design.[14] This transition was spurred by the need to manage increasing software scale and modularity, particularly as object-oriented languages like Smalltalk gained prominence. Early efforts culminated in workshops such as the OOPSLA '87 Workshop on Specification and Design for Object-Oriented Programming, where participants explored pattern languages as a means to document and share effective design strategies.[15] Pivotal pre-Gang of Four influences included the work of Ward Cunningham and Kent Beck, who in the late 1980s developed a small set of patterns specifically for user interface design while at Tektronix. In their 1987 paper "Using Pattern Languages for Object-Oriented Programs," presented at OOPSLA, they outlined an adaptation of pattern languages to object-oriented contexts, demonstrating five patterns for building flexible GUIs in Smalltalk.[15] These efforts highlighted patterns' role in encapsulating design knowledge for reuse.[16] The initial motivations for software patterns stemmed from the demand for standardized, reusable solutions amid the escalating complexity of graphical user interfaces and emerging distributed systems in the 1980s. Developers grappled with challenges like maintaining consistency in UI interactions and coordinating components across networked environments, where ad-hoc designs led to brittle code.[14] Patterns provided a way to abstract these issues into named, communicable solutions, inheriting from architecture the emphasis on contextual problem-solving.[16]Key Publications and Contributors
The seminal work formalizing software design patterns for object-oriented programming is the 1994 book Design Patterns: Elements of Reusable Object-Oriented Software, published by Addison-Wesley and authored by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides—known collectively as the Gang of Four (GoF). This text catalogs 23 foundational patterns, divided into creational, structural, and behavioral categories, providing reusable solutions to recurring design challenges while emphasizing abstraction, encapsulation, and flexibility in software architecture. The GoF authors drew from diverse professional experiences in industry and academia to develop these ideas: Gamma, a Swiss software engineer at Taligent, Inc. (a joint Apple-IBM venture); Helm, a consultant at DMR Group in Canada; Johnson, a professor at the University of Illinois at Urbana-Champaign; and Vlissides, a researcher at IBM's T.J. Watson Research Center. Their collaboration, influenced by earlier object-oriented practices, popularized patterns as a shared vocabulary for communicating design intent among developers.[17] Building on the GoF foundation, the Pattern Languages of Program Design book series, initiated in 1995 by Addison-Wesley, emerged from the inaugural Pattern Languages of Programs (PLoP) conference held in 1994 at Allerton Park, Illinois, and organized by Ralph Johnson. These volumes compiled shepherded and peer-reviewed patterns submitted to PLoP gatherings, broadening the scope to include concurrent, distributed, and domain-specific designs while promoting collaborative pattern refinement through writer workshops.[18] Parallel efforts in Europe advanced pattern standardization via the first European Conference on Pattern Languages of Programs (EuroPLoP) in July 1994 at Kloster Irsee, Germany, which focused on interdisciplinary applications and has since produced annual proceedings fostering global pattern communities.[19][20] In the mid-1990s, Mohamed E. Fayad emerged as a key contributor, co-authoring the influential 1996 Communications of the ACM article "Software Patterns" with Douglas C. Schmidt and Ralph E. Johnson, which framed patterns as formalized, context-aware solutions to software engineering problems and advocated their integration with knowledge representation techniques. Fayad's 1990s writings, including contributions to pattern series and journals, emphasized domain analysis and knowledge maps to make patterns more systematic and reusable across applications. Subsequent influencers like Martin Fowler extended patterns to enterprise-scale systems through his 2002 book Patterns of Enterprise Application Architecture, published by Addison-Wesley, which documented over 40 patterns for data access, web presentation, and concurrency in large-scale applications, drawing from real-world Java and .NET implementations to address integration and scalability challenges.[21] These landmark publications and contributors, through their texts and foundational conferences like PLoP and EuroPLoP, transformed ad hoc design practices into a rigorous discipline, enabling widespread adoption and ongoing evolution in software engineering.Application and Practice
Identifying and Applying Patterns
The process of identifying software design patterns involves analyzing the problem context within software development to recognize recurring issues that benefit from proven solutions. Designers evaluate the forces at play, including constraints such as performance requirements, maintainability needs, and interoperability demands, alongside the potential consequences of different approaches. Pattern catalogs, such as the seminal collection in Design Patterns: Elements of Reusable Object-Oriented Software, provide structured descriptions—including the problem statement, applicability context, solution template, and trade-offs—that enable matching the current scenario to an appropriate pattern.[22] Applying a selected pattern proceeds through defined steps to integrate it effectively into the design. Initially, the pattern is articulated in design documentation, specifying its role in resolving the identified problem and its alignment with broader system architecture. Implementation follows by adapting code templates, such as defining classes, interfaces, and collaborations inherent to the pattern (e.g., encapsulating object creation in a Factory pattern). For legacy systems, refactoring techniques are employed to introduce the pattern incrementally, preserving external interfaces while enhancing internal structure and flexibility.[23][22] Best practices emphasize initiating pattern use at the high-level design phase to foster reusability and simplify complex interactions, followed by iterative application informed by testing outcomes and stakeholder feedback. Thorough documentation of the pattern's rationale, including why it was chosen over alternatives, supports long-term maintainability and establishes a shared vocabulary among development teams, mitigating misinterpretation during evolution.[22][23] Challenges in this process include the risk of overfitting, where patterns are applied rigidly to ill-suited problems, resulting in over-engineered and inflexible code that complicates future changes. Additionally, overlooking contextual forces or selecting suboptimal patterns due to incomplete analysis can lead to unintended consequences, such as reduced performance or increased coupling, while the inherent learning curve exacerbates adoption barriers in teams lacking experience.[24][25]Domain-Specific Adaptations
In software engineering, design patterns are often tailored to address the unique constraints and requirements of specific application domains, extending general principles to optimize for factors such as performance, scalability, resource limitations, or distributed interactions. These adaptations ensure that patterns remain effective while accommodating domain-specific challenges, such as the stateless nature of web protocols or the real-time demands of embedded hardware. By modifying established patterns like MVC or introducing specialized ones, developers can achieve better alignment with domain goals, enhancing reliability and maintainability. In web development, the Model-View-Controller (MVC) pattern is commonly adapted to handle the statelessness of the HTTP protocol, where each request is independent and lacks inherent session continuity. This adaptation typically incorporates server-side session management or client-side state persistence, such as cookies or tokens, to maintain user context across requests while keeping the controller layer lightweight and focused on routing. For instance, frameworks like Spring MVC implement this by separating request handling from stateful business logic, allowing stateless controllers to process HTTP requests efficiently without retaining conversation state. Similarly, RESTful architectural patterns for APIs, introduced by Roy Fielding, emphasize stateless interactions by treating resources as uniform interfaces accessible via standard HTTP methods, enabling scalable web services that avoid server-side session overhead. These adaptations promote loose coupling in distributed web environments, facilitating easier scaling and caching. For embedded systems, design patterns are extended to manage resource constraints and real-time requirements, often prioritizing predictability and low latency over flexibility. Real-time patterns, such as those using finite state machines (FSMs), are adapted for hardware interactions where timing guarantees are critical; for example, the state-machine pattern encapsulates behavioral transitions in a modular way, allowing embedded software to respond deterministically to interrupts or sensor inputs without excessive memory usage. This is particularly evident in UML-based approaches for real-time embedded systems, where state machines model concurrent behaviors while integrating with schedulers to meet deadlines in resource-limited environments like microcontrollers. Such adaptations ensure fault tolerance and efficiency in domains like automotive or IoT devices. In enterprise and distributed systems, integration patterns address the complexity of interconnecting heterogeneous applications across networks. The Enterprise Integration Patterns (EIPs), cataloged by Gregor Hohpe and Bobby Woolf, provide a foundational set of messaging-based adaptations, including patterns like the message router and aggregator, which handle asynchronous communication and data transformation in large-scale environments. Published in 2004, this collection emphasizes canonical solutions for challenges such as loose coupling and fault tolerance in enterprise service buses, influencing standards in middleware technologies like Apache Camel. These patterns extend behavioral and structural designs to support scalable, reliable data flows in business-critical systems. Game development employs the Entity-Component-System (ECS) as a structural adaptation optimized for high-performance rendering and simulation, where traditional object-oriented hierarchies prove inefficient for managing thousands of dynamic entities. In ECS, entities serve as mere identifiers, components hold raw data (e.g., position or velocity), and systems process groups of components in a data-oriented manner, enabling cache-friendly operations and parallel processing crucial for real-time graphics. This pattern, widely adopted in engines like Unity's ECS implementation, decouples data from behavior to achieve better scalability in performance-intensive scenarios, such as open-world simulations. Its long-standing use in the industry highlights its role in balancing flexibility with computational efficiency. Emerging domains like cloud-native applications and AI/ML pipelines have spurred specialized patterns to tackle scalability and reproducibility. In microservices architectures, the circuit breaker pattern, popularized by Michael Nygard, prevents cascading failures by monitoring remote service calls and "opening" to halt traffic when errors exceed thresholds, allowing time for recovery; this adaptation of the proxy pattern is essential for resilient distributed systems in cloud environments. For AI/ML pipelines, software engineering patterns focus on modular workflows, such as the model-data separation pattern, which isolates training data handling from inference logic to ensure reproducibility and scalability across stages like preprocessing and deployment. These patterns, drawn from multivocal literature reviews, emphasize automation and monitoring to bridge research prototypes with production systems, addressing challenges like data versioning and model drift.Patterns by Programming Paradigm
Object-Oriented Patterns
Object-oriented design patterns are a cornerstone of software engineering, primarily developed within the paradigm of object-oriented programming (OOP), where they provide reusable solutions to common problems in designing flexible and maintainable systems. These patterns emerged as a way to standardize best practices for structuring code around classes and objects, emphasizing modularity and extensibility. The seminal work in this area, "Design Patterns: Elements of Reusable Object-Oriented Software" by Gamma et al. (1994), formalized 23 such patterns tailored to OOP environments. At their foundation, OOP design patterns leverage core principles of the paradigm: encapsulation, inheritance, and polymorphism. Encapsulation allows patterns to hide internal implementation details of objects, promoting information hiding and reducing dependencies between components. Inheritance enables patterns to build hierarchical relationships among classes, facilitating code reuse and specialization. Polymorphism, in turn, supports flexible object interactions by allowing objects of different classes to be treated uniformly through common interfaces. These principles underpin the assumption in GoF patterns that software is composed of interacting classes and objects, enabling patterns to address issues like variability in object creation and behavior. In OOP contexts, patterns are adapted into three main categories based on their focus: creational, structural, and behavioral. Creational patterns manage the instantiation of objects, decoupling the creation process from the using code to allow for more flexible and controlled object lifecycles. Structural patterns deal with class and object composition, forming larger structures from smaller ones while keeping them flexible and efficient. Behavioral patterns handle communication and responsibilities among objects, assigning duties to promote loose coupling and clearer delineation of roles. These adaptations ensure that OOP systems remain scalable and adaptable to changing requirements. Implementation of these patterns occurs in various OOP languages, such as Java, C++, and Python, where language-specific features influence their realization. In Java, for instance, interfaces and abstract classes are commonly used to enforce polymorphic behavior in patterns, while C++ employs templates for generic implementations that enhance performance. Python's dynamic typing and duck typing simplify pattern application by allowing behavioral flexibility without strict inheritance. Visualization of these implementations often relies on Unified Modeling Language (UML) diagrams, such as class diagrams to depict relationships and sequence diagrams to illustrate object interactions, aiding in design communication and verification. Despite their benefits, OOP design patterns carry limitations, particularly the risk of introducing tight coupling when they enforce rigid inheritance hierarchies. Over-reliance on inheritance can lead to fragile base class problems, where changes in a parent class propagate unintended effects to subclasses, increasing maintenance costs. This issue arises because patterns like those in the GoF catalog often prioritize class-based hierarchies, potentially limiting adaptability in dynamic environments. To mitigate this, developers are advised to favor composition over inheritance where possible, aligning with modern OOP practices that emphasize interfaces and dependency injection.Patterns in Other Paradigms
In functional programming, design patterns emphasize immutability, higher-order functions, and composability to manage state and control flow without mutable objects. Monads, a foundational pattern, encapsulate state management and side effects, allowing pure functions to handle computations like input/output or error propagation in a composable manner. In Haskell, monads provide a structure for sequencing operations while preserving referential transparency, as detailed in Philip Wadler's seminal work on monads for functional programming. For instance, the Maybe monad handles optional values to avoid null pointer exceptions, replacing patterns like Null Object from object-oriented designs. Similarly, in Scala, monads such as Future enable asynchronous computations, adapting behavioral patterns through functor and applicative interfaces. Higher-order functions often subsume traditional behavioral patterns; for example, map and fold operations in languages like Haskell or Clojure replace Iterator or Strategy patterns by abstracting over data transformations without explicit loops or conditionals. Procedural and imperative paradigms rely on patterns that leverage functions, modules, and explicit control flow to promote modularity and reusability in languages without classes. The Adapter pattern facilitates integration of legacy C code by wrapping incompatible procedural interfaces with compatible ones, enabling seamless calls between modules without altering original implementations. This is particularly useful in systems like Unix utilities, where adapters bridge differing function signatures in libraries. Event-driven patterns are prevalent in GUI libraries for procedural languages, such as those in C using libraries like GTK or Win32 API, where callbacks and message loops handle user interactions asynchronously. In these setups, a central event dispatcher polls for inputs and invokes registered handlers, mirroring the Observer pattern but implemented via function pointers and queues rather than objects. This approach ensures responsive interfaces in resource-constrained environments, as seen in early windowing systems. In concurrent and reactive paradigms, patterns focus on message passing and non-blocking operations to handle parallelism without shared state. The Actor model, pioneered by Carl Hewitt, treats actors as independent units that communicate via asynchronous messages, providing a behavioral alternative for distributed systems. In Erlang, actors (implemented as lightweight processes) encapsulate state and behavior, enabling fault-tolerant concurrency through supervision trees that restart failed components automatically. This pattern avoids locks and race conditions by isolating state within actors, contrasting with thread-based synchronization in imperative languages. For asynchronous operations in JavaScript, the Promise pattern represents the eventual completion of tasks, allowing chaining of thenable operations to manage callbacks without the "callback hell" of nested functions. Standardized in ECMAScript 2015, Promises facilitate reactive flows in event loops, as in Node.js event emitters. Post-2010 evolutions have adapted patterns for declarative and distributed paradigms. In declarative UI frameworks like React, hooks provide a functional alternative to class-based lifecycle methods, encapsulating state and side effects in reusable components. Custom hooks, such as useState or useEffect, replace patterns like State or Template Method by composing behavior through function calls, promoting colocation of logic without inheritance hierarchies. In serverless architectures, patterns like the Saga pattern (originally proposed in 1987 by Hector Garcia-Molina and Kenneth Salem)[26] orchestrate distributed transactions across functions, using compensating actions to ensure consistency in stateless environments like AWS Lambda. The MapReduce pattern, adapted for serverless, processes data streams via event-triggered invocations, scaling horizontally without managing servers. These patterns reflect shifts toward event sourcing and choreography, as outlined in analyses of serverless systems.Classification and Examples
Creational Patterns
Creational patterns address the instantiation of objects in a manner that promotes flexibility and decoupling from specific classes, allowing systems to vary the creation process without altering client code. These patterns are particularly valuable in object-oriented programming, where direct instantiation can lead to tight coupling and reduced maintainability. By encapsulating object creation logic, creational patterns enable easier testing, extension, and reuse of code. The five primary creational patterns—Abstract Factory, Builder, Factory Method, Prototype, and Singleton—were formalized in the seminal work on design patterns.[3] The **Abstract Factory** pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes, ensuring that the created objects are compatible within a product family. Its intent is to support the creation of varying sets of products, such as different themes in a user interface toolkit where buttons, menus, and scrollbars must match across platforms like Windows or macOS. This pattern is applicable when a system should be independent of how its products are created, composed, or represented, or when families of products need to be introduced without breaking existing client code. The structure involves an abstract factory interface declaring methods for each product type, concrete factories implementing these for specific families, and abstract and concrete product classes. For instance, in GUI development, an abstract factory can produce a family of Windows-style widgets or macOS-style widgets uniformly.[27][3] The Builder pattern constructs complex objects step by step, separating the construction process from the object's representation to allow the same construction process to create different representations. It addresses scenarios where objects have many optional parameters or intricate initialization steps, avoiding the need for numerous constructors or telescoping constructor anti-patterns. Applicable for building objects like documents, meals in a restaurant simulation, or configurations with varying attributes, the builder enables fluent interfaces for readability. The structure includes a director to orchestrate the building process (optional), a builder interface defining construction steps, concrete builders for specific representations, and a product class holding the complex object. An example is constructing a bank account with optional fields like interest rate and fees using a fluent builder chain. This approach, refined by Joshua Bloch for modern languages like Java, enhances immutability and clarity.[27][3] The Factory Method pattern defines an interface for creating an object but allows subclasses to decide which class to instantiate, deferring instantiation to subclasses to promote loose coupling. It motivates the need to let subclasses alter the type of objects created, simplifying the delegation of creation in frameworks or libraries. This pattern is suitable when a class cannot anticipate the type of objects it needs to create, or when object creation involves simple logic without complex configuration. The structure comprises a creator class with a factory method declaring the object return type, concrete creators overriding the method to return specific instances, and a product interface with concrete products. For example, a polygon drawing application can use a factory method to create shapes like triangles or squares based on subclass decisions.[27][3] The Prototype pattern creates new objects by cloning an existing prototype instance, specifying the kinds of objects to create using a prototypical instance rather than a class-specific constructor. It solves problems where object creation is more efficient through copying than from scratch, especially for objects that are expensive to initialize or require runtime configuration. Applicable in scenarios like document editors needing to clone shapes or game engines duplicating entities with varied states, it avoids subclass proliferation for creation variations. The structure involves a prototype interface with a clone method, concrete prototypes implementing cloning (often using language-specific mechanisms like Java's Cloneable), and a client that registers and retrieves prototypes via a factory. In Java, this can leverage the Object.clone() method or serialization for deep copies, as demonstrated in examples cloning graphical elements.[28][3] The Singleton pattern ensures that a class has only one instance and provides a global point of access to it, restricting instantiation to control access to shared resources. Its motivation is to manage limited resources like database connections, caches, or loggers where multiple instances would be wasteful or inconsistent. Applicable for thread-safe global state management, such as configuration managers or print spoolers, it requires careful implementation to handle concurrency. The structure features a class with a private constructor, a static field holding the single instance, and a static getInstance() method for lazy initialization. A thread-safe variant uses a static inner class to defer instantiation until first access, preventing race conditions without synchronization overhead. For example, a logger class can enforce a single instance across an application.[27][3]Structural Patterns
Structural design patterns focus on how classes and objects can be composed to form larger, more complex structures while maintaining flexibility and efficiency in object-oriented systems. These patterns emphasize the relationships between entities, using inheritance and composition to simplify the assembly of interfaces and implementations without altering the underlying code. Unlike creational patterns that handle object instantiation, structural patterns build upon existing objects to create hierarchies or interfaces that support scalability and reusability. The seven core structural patterns—Adapter, Bridge, Composite, Decorator, Facade, Flyweight, and Proxy—address common challenges in composing software components, enabling developers to integrate disparate parts seamlessly. The **Adapter** pattern converts the interface of a class into another interface that clients expect, allowing otherwise incompatible classes to work together. This is particularly useful for integrating legacy systems or third-party libraries where the existing interface does not match the required one, acting as a wrapper that translates calls between the two. In practice, adapters can be implemented via class inheritance for interface adaptation or object composition for more flexible wrapping, ensuring that clients remain unaware of the adaptee's original interface. For instance, adapting an old data format reader to a new API without modifying the legacy code exemplifies its role in maintaining backward compatibility. The Bridge pattern decouples an abstraction from its implementation, permitting the two to vary independently and supporting multiple implementations without affecting client code. It achieves this by defining a separate hierarchy for abstractions and implementations, connected through a composition relationship rather than inheritance, which avoids the proliferation of subclasses in complex systems. This pattern is applicable when both the abstraction and implementation may change over time, such as in graphics systems where shapes (abstractions) can render differently across platforms (implementations). By bridging these concerns, it promotes extensibility and reduces coupling in evolving software architectures. The **Composite** pattern composes objects into tree structures to represent part-whole hierarchies, treating individual objects and compositions of objects uniformly through a common interface. This enables clients to interact with complex structures as if they were simple leaf objects, simplifying algorithms that traverse hierarchical data like file systems or organizational charts. The pattern relies on recursive composition, where container objects hold references to child components, supporting operations like adding or removing parts dynamically. It is ideal for scenarios requiring uniform treatment of single and grouped elements, such as UI component trees in graphical applications. The Decorator pattern attaches additional responsibilities to an object dynamically, providing a flexible alternative to subclassing for extending functionality. It uses composition to wrap objects with concrete decorators that add behaviors without modifying the original class, allowing an unlimited series of decorations to be stacked. This is beneficial for adding features like borders or scrolling to visual components in a windowing system, where each decorator conforms to the same interface as the component it enhances. The pattern preserves the open-closed principle by keeping classes open for extension but closed for modification. The **Facade** pattern provides a unified, simplified interface to a complex subsystem, hiding its intricacies from clients and defining a higher-level entry point for subsystem interactions. It promotes loose coupling by allowing clients to access subsystem functionality without needing to understand or directly call multiple underlying classes. For example, a home theater facade might orchestrate DVD players, projectors, and amplifiers with a single "watch movie" method, shielding users from the subsystem's complexity. This pattern is applicable when simplifying interactions with libraries or frameworks that have many interdependent components. The **Flyweight** pattern minimizes memory usage by sharing as much data as possible among similar objects, treating fine-grained objects efficiently through intrinsic (shared) and extrinsic (context-specific) states. It is suited for applications with a large number of similar objects, such as characters in a text editor, where the flyweight factory manages a pool of reusable instances keyed by intrinsic properties. Clients pass extrinsic data at runtime to complete the object's behavior, reducing overall resource consumption. This pattern excels in resource-constrained environments like games or simulations with numerous repeated elements. The Proxy pattern provides a surrogate or placeholder for another object to control access to it, such as for remote resources, large objects, or protected entities. Variants include virtual proxies for lazy initialization (loading objects on demand), protection proxies for access control, and remote proxies for distributed systems. For instance, a proxy might delay image loading until the object is displayed, optimizing performance. By intercepting calls to the real subject, proxies enforce policies like caching or logging without altering the subject's interface, making them essential for managing indirect object access.Behavioral Patterns
Behavioral design patterns focus on the interactions and responsibilities among objects, defining how they communicate and collaborate to fulfill complex behaviors while promoting loose coupling and flexibility in object-oriented systems. These patterns address the assignment of responsibilities, control flow, and algorithmic variations, enabling objects to work together without tight dependencies on specific implementations. Unlike structural patterns, which emphasize composition of classes and objects, behavioral patterns prioritize runtime dynamics, such as request handling, state management, and notification mechanisms.[29] The Chain of Responsibility pattern avoids coupling the sender of a request to its receiver by allowing multiple objects to handle the request sequentially along a chain until one processes it. This decouples the sender from the receiver, as the request passes dynamically through handlers, such as in graphical user interface event processing where events propagate through window hierarchies. It is applicable when the handler is not known in advance, multiple objects might respond, or the set of handlers can vary at runtime, reducing direct references and enhancing modularity.[29] Command encapsulates a request as an object, permitting parameterization of clients with different requests, queuing, logging, or support for undoable operations. By turning requests into standalone objects, it enables flexible invocation, such as in menu systems where commands represent actions like save or cut that can be queued or reversed. This pattern applies to scenarios requiring undo/redo functionality, transaction scripting, or delayed execution, as seen in systems like macro recorders.[29] The **Interpreter** pattern defines a representation for a language's grammar and an interpreter that uses this to process sentences in the language, suitable for simple languages where efficiency is not paramount. It structures rules as classes forming a syntax tree, allowing evaluation of expressions like regular expressions or simple query languages. Applicable for domain-specific languages with frequent rule changes or when building interpreters for rule-based systems, it combines with other patterns like Flyweight for complex grammars.[29] Iterator provides sequential access to an aggregate object's elements without exposing its underlying representation, supporting uniform traversal across collections like lists or trees. It abstracts iteration logic, allowing multiple iterators over the same structure, such as in database query results or document processors. This pattern is useful for hiding collection internals, supporting polymorphic traversals, or when multiple concurrent iterations are needed.[29] The Mediator pattern defines an object that centralizes interactions among a set of objects, promoting loose coupling by preventing direct references between them and allowing independent variation of interaction protocols. In applications like dialog boxes, the mediator coordinates widget behaviors, such as updating fields based on user input. It applies to complex object meshes where direct coupling would lead to tangled dependencies, or when reusable components need customizable communication.[29] Memento captures and externalizes an object's internal state without violating encapsulation, enabling later restoration to that state for features like undo. A narrow interface accesses the memento for state storage, while the originator controls access, as in constraint-solving applications saving snapshots. This is applicable for undo mechanisms, state rollback in transactions, or archiving object histories while preserving privacy of internals.[29] The Observer pattern establishes a one-to-many dependency where a subject notifies all dependents of state changes, ensuring automatic updates and consistency. Subjects maintain a list of observers and broadcast changes, commonly used in graphical user interfaces for view updates or event systems. It suits scenarios where changes in one object must propagate to multiple others, or for decoupling publishers from subscribers in data synchronization.[29] State allows an object to change its behavior dynamically as its internal state alters, appearing as if it changes class by delegating to state objects. This eliminates large conditional statements, as in TCP connection protocols transitioning between listening, established, or closed states. Applicable when an object's behavior varies significantly by state, transitions are frequent, or states are numerous, it uses delegation to encapsulate state-specific logic.[29] The Strategy pattern defines a family of interchangeable algorithms, encapsulating each to allow runtime selection independent of client code. Clients hold a strategy reference and invoke it polymorphically, such as in text formatters choosing line-breaking algorithms. It is ideal for varying algorithms without subclass proliferation, supporting families like sorting methods, or when behaviors need to be configurable externally.[29] Template Method outlines an algorithm's skeleton in a method, deferring specific steps to subclasses while fixing the structure, enabling customization without altering the overall flow. Abstract classes define hooks or primitive operations that concrete subclasses implement, as in framework initialization sequences. This applies to common algorithms with variable steps, enforcing invariants across subclasses, or when avoiding code duplication in hierarchies.[29] Finally, Visitor represents operations on elements of an object structure without modifying their classes, allowing new operations via double dispatch where visitors traverse and perform actions. Useful for syntax trees in compilers adding analyses like pretty-printing, it centralizes operations related to structure traversal. Applicable when the structure is stable but operations frequently added, or for accumulating results over heterogeneous collections.[29]Concurrency and Advanced Patterns
Concurrency patterns address the challenges of multi-threaded programming by providing structured ways to coordinate threads and manage shared resources, extending traditional behavioral patterns to handle synchronization and communication in parallel environments. The Producer-Consumer pattern, a foundational concurrency design, involves producers generating data that consumers process from a shared buffer, ensuring thread-safe coordination through mechanisms like semaphores or blocking queues to prevent buffer overflow or underflow.[30] This pattern, detailed in Pattern-Oriented Software Architecture Volume 2, facilitates efficient decoupling of production and consumption rates in systems like message queues. The Read-Write Lock pattern optimizes access to shared data by allowing multiple threads to read simultaneously while requiring exclusive access for writes, reducing contention in read-heavy scenarios compared to standard mutexes.[30] Implemented in libraries like Java'sReentrantReadWriteLock, it supports higher throughput for applications such as caches where reads outnumber writes.[31]
Extending the creational Singleton pattern for multi-threaded safety, the Thread-Safe Singleton employs double-checked locking to lazily initialize the instance only once, using a volatile flag to ensure visibility across threads and avoid race conditions.[32] This optimization, analyzed in depth by Schmidt and colleagues, minimizes synchronization overhead while guaranteeing singleton properties in concurrent contexts, though it requires careful memory barrier handling to prevent reordering issues on multiprocessors.[32]
In distributed systems, advanced patterns like the Circuit Breaker enhance fault tolerance by monitoring remote service calls and "opening" to fail fast during failures, preventing cascading errors and allowing time for recovery.[33] Popularized in microservices architectures, such as Netflix's Hystrix library, it transitions between closed (normal operation), open (block calls), and half-open (test recovery) states to maintain system resilience.[33]
The Saga pattern manages long-running transactions across microservices by breaking them into a sequence of local transactions, each compensated if subsequent steps fail, ensuring eventual consistency without distributed locks.[34] Originating from database research and adapted by Chris Richardson for microservices, it uses orchestration or choreography to coordinate compensating actions, ideal for e-commerce workflows spanning multiple services.[34]
Reactive patterns handle asynchronous data streams in concurrent systems, with backpressure mechanisms preventing consumer overload by signaling producers to slow down emission rates.[35] In RxJava, operators like onBackpressureBuffer or Flowable implement this by buffering or dropping excess items, supporting scalable stream processing in reactive applications.[35]
Event Sourcing, a reactive persistence pattern, stores application state as an immutable sequence of events rather than current snapshots, enabling auditability through complete change histories and facilitating temporal queries or replays for debugging.[36] As described by Martin Fowler, it integrates with Command Query Responsibility Segregation (CQRS) to decouple writes from reads, providing high traceability in domains like finance where regulatory compliance demands full audit trails.[36]
Recent evolutions in asynchronous programming, such as async/await in C# and JavaScript, address limitations in traditional Gang of Four patterns by simplifying callback hell and promise chaining into linear, readable code that scales to concurrent tasks without explicit thread management. Introduced in C# 5.0 for Task-based Asynchronous Pattern (TAP) compliance, async/await propagates exceptions naturally and composes operations efficiently, filling gaps in GoF behavioral patterns for non-blocking I/O. In JavaScript (ES2017), it builds on Promises to handle web APIs asynchronously, promoting patterns like async generators for iterable streams in modern front-end architectures.
