Design patterns are typical solutions to common problems in software design. They represent best practices used by experienced object-oriented software developers.
Categories of Design Patterns
Creational Patterns These patterns abstract the instantiation process, focusing on how objects are created in a flexible and reusable manner.
- Factory Method
- Defines an interface for creating an object, but lets subclasses decide which class to instantiate.
- The Factory Method lets a class defer instantiation to subclasses, allowing the creation of related objects like
JavaCourseorPythonCourse.
- Abstract Factory
- Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
- Analogous to a manufacturing company producing different but related components, such as car frames, doors, and chassis.
- Builder
- Separates the construction of a complex object from its representation.
- The same construction process can create different representations, and the client does not need to know the internal construction details.
- Prototype
- Creates new objects by copying an existing object, known as the prototype.
- This pattern is useful when object creation is costly, and it helps improve performance by avoiding expensive initialization.
- Singleton
- Ensures a class has only one instance and provides a global point of access to it.
- Common implementations include:
- Lazy Initialization: The instance is created only when it is first needed.
- Double-Checked Locking: Uses synchronization with two condition checks for thread safety in lazy initialization.
- Static Inner Class: Leverages the class loader mechanism to ensure thread-safe lazy initialization.
- Eager Initialization: The instance is created at class loading time, using static initializers.
- Enum: Using an enum is considered a robust way to implement a singleton, as it naturally prevents multiple instantiations and resists common attacks.
- Preventing Singleton Destruction:
- Serialization Attack: Deserialization can create a new instance. Override the
readResolve()method to return the existing singleton instance.
private Object readResolve() { return SingletonHolder.INSTANCE; }- Reflection Attack: Using reflection to access the private constructor can break the singleton. The enum-based approach inherently prevents this.
- Serialization Attack: Deserialization can create a new instance. Override the
Structural Patterns These patterns explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient.
- Facade
- Provides a simplified interface to a complex subsystem, adhering to the Law of Demeter.
- It defines a higher-level interface that makes the subsystem easier to use.
- Adapter
- Acts as a bridge between two incompatible interfaces, allowing them to work together.
- It converts the interface of a class into another interface that a client expects.
- Bridge
- Decouples an abstraction from its implementation so that the two can vary independently.
- It involves an interface that acts as a bridge, making the concrete functionality independent of the interface implementers.
- Composite
- Composes objects into tree structures to represent part-whole hierarchies.
- Clients can treat individual objects and compositions of objects uniformly.
- Advantages: Uniform interface, flexibility in building complex structures, code reusability, and simplified client code.
- Disadvantages: Can be overly general, may cause performance issues with deep trees, and makes it difficult to restrict component types.
- Proxy
- Provides a surrogate or placeholder for another object to control access to it.
- The proxy acts as an intermediary, protecting or enhancing the target object's functionality, thereby reducing system coupling.
- Decorator
- Attaches additional responsibilities to an object dynamically without altering its structure.
- Offers a flexible alternative to subclassing for extending functionality.
- Flyweight
- Uses sharing to support a large number of fine-grained objects efficiently.
- Reduces memory footprint by reusing existing similar objects instead of creating new ones.
Behavioral Patterns These patterns are concerned with algorithms and the assignment of responsibilities between objects.
- Template Method
- Defines the skeleton of an algorithm in an operation, deferring some steps to subclasses.
- Lets subclasses redefine certain steps of an algorithm without changing its structure.
- Iterator
- Provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
- Offers a uniform interface for traversing different collection structures.
- Strategy
- Defines a family of algorithms, encapsulates each one, and makes them interchangeable.
- Useful for replacing complex conditional logic (e.g., multiple
if-elsestatements), enhancing security and maintainability.
- Interpreter
- Given a language, defines a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
- Applicable for scenarios like parsing logs with different formats.
- Observer
- Defines a one-to-many dependency between objects sothat when one object changes state, all its dependents are notified and updated automatically.
- Memento
- Captures and externalizes an object's internal state without violating encapsulation, so the object can be restored to this state later.
- Enables undo/rollback functionality.
- Command
- Encapsulates a request as an object, thereby allowing for parameterization of clients with different requests, queuing of requests, and logging of operations.
- Decouples the object that invokes the operation from the one that knows how to perform it.
- Mediator
- Defines an object that encapsulates how a set of objects interact, promoting loose coupling.
- Centralizes complex communications between objects, making the system easier to maintain.
- Chain of Responsibility
- Passes a request along a chain of handlers. Upon receiving a request, each handler decides either to process it or to pass it to the next handler in the chain.
- Decouples the sender of a request from its receiver.
- Visitor
- Represents an operation to be performed on elements of an object structure, allowing you to define a new operation without changing the classes of the elements.
- Separates an algorithm from the object structure on which it operates.
- State
- Allows an object to alter its behavior when its internal state changes. The object will appear to change its class.
- The behavior is dependent on the object's state.