A design pattern is a general repeatable/reusable solution to a commonly occurring problem with a given context in software design. It can be treated as a description or template for how to solve a problem that can be used in many different situations.
These patterns deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. Examples include:
- Singleton: Ensures a class has only one instance and provides a global point of access to it.
- Factory Method: Defines an interface for creating an object but allows subclasses to alter the type of objects that will be created.
- Abstract Factory: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
- Builder: Separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
- Prototype: Creates new objects by copying an existing object, known as a prototype.
These patterns deal with object composition, focusing on how objects are assembled into larger structures. Examples include:
- Adapter: Allows incompatible interfaces to work together by wrapping one of the interfaces.
- Decorator: Adds additional functionality to an object dynamically without altering its structure.
- Facade: Provides a simplified interface to a complex subsystem.
- Composite: Composes objects into tree structures to represent part-whole hierarchies, allowing clients to treat individual objects and compositions uniformly.
- Proxy: Provides a surrogate or placeholder for another object to control access to it.
These patterns are concerned with object interaction, focusing on communication between objects. Examples include:
- Observer: Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
- Strategy: Defines a family of algorithms, encapsulates each one, and makes them interchangeable. The strategy pattern allows the algorithm to vary independently from clients that use it.
- Command: Encapsulates a request as an object, thereby allowing for parameterization of clients with different requests, queuing of requests, and logging of requests.
- Iterator: Provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
- State: Allows an object to alter its behavior when its internal state changes. The object will appear to change its class.
Each of these patterns has its own use case, advantages, and trade-offs. Understanding when and how to apply them can significantly improve the design and structure of your C# applications.