Design patterns are reusable solutions to common problems in software design. They provide a standardized approach to solving issues that arise during the development process, helping developers create more efficient and maintainable code. Design patterns can be categorized into three main types: creational, structural, and behavioral. Each category addresses different aspects of object creation, class and object relationships, and interactions between objects.
Creational design patterns focus on the process of object creation. They provide mechanisms to create objects in a way that is independent of the classes that instantiate them. This allows for more flexible and scalable code, as new types of objects can be introduced with minimal changes to existing code.
Structural design patterns are concerned with how classes and objects can be composed to form larger structures. These patterns help in organizing complex systems by defining the relationships between different components.
Behavioral design patterns are focused on improving communication between objects. They help in defining how objects interact and communicate with each other, making the system more flexible and easier to manage.
The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. This is useful when exactly one object is needed to coordinate actions across the system.
1class Singleton {2static instance = null;34constructor() {5if (Singleton.instance) {6return Singleton.instance;7}8this.data = 'Singleton Data';9Singleton.instance = this;10}1112getData() {13return this.data;14}15}1617const singleton1 = new Singleton();18const singleton2 = new Singleton();1920console.log(singleton1 === singleton2); // true21console.log(singleton1.getData()); // "Singleton Data"
The Adapter pattern allows incompatible interfaces to work together. For instance, if you have a legacy system that uses an old interface and you want to integrate it with a new system using a different interface.
1class OldSystem {2request() {3return 'Old System Request';4}5}67class NewSystem {8specificRequest() {9return 'New System Specific Request';10}11}1213class Adapter extends OldSystem {14constructor(newSystem) {15super();16this.newSystem = newSystem;17}1819request() {20return `${this.newSystem.specificRequest()} (adapted from old system)`;21}22}2324const oldSystem = new OldSystem();25const newSystem = new NewSystem();26const adapter = new Adapter(newSystem);2728console.log(oldSystem.request()); // "Old System Request"29console.log(adapter.request()); // "New System Specific Request (adapted from old system)"
The Observer pattern is used when an object, called the subject, needs to maintain a list of its dependents, called observers, and notify them automatically of any state changes.
1class Subject {2constructor() {3this.observers = [];4}56subscribe(observer) {7this.observers.push(observer);8}910unsubscribe(observer) {11this.observers = this.observers.filter(obs => obs !== observer);12}1314notify(data) {15this.observers.forEach(observer => observer.update(data));16}17}1819class Observer {20update(data) {21console.log(`Observer received data: ${data}`);22}23}2425const subject = new Subject();26const observer1 = new Observer();27const observer2 = new Observer();2829subject.subscribe(observer1);30subject.subscribe(observer2);3132subject.notify('Hello Observers!'); // Output: Observer received data: Hello Observers!33// Observer received data: Hello Observers!3435subject.unsubscribe(observer1);3637subject.notify('Observer 2 only!'); // Output: Observer received data: Observer 2 only!
After understanding the basics of design patterns and their types, the next step is to dive deeper into creational patterns. This will provide a solid foundation for creating objects in a flexible and scalable manner.
Stay tuned for more detailed tutorials on each type of design pattern!
Info
Understanding and applying design patterns can significantly improve your software development skills by promoting cleaner, more maintainable code.