Advanced OOP Concepts
Composition and Aggregation
In object-oriented programming (OOP), composition and aggregation are two ways to relate two or more objects. Composition is a strong relationship, where the composed object is owned by the composing object. Aggregation is a weaker relationship, where the aggregated object is not owned by the aggregating object.
For example, a car is composed of many parts, such as the engine, the wheels, and the body. The car owns these parts, and they cannot exist without the car. In contrast, a car can be aggregated with other objects, such as a trailer or a driver. The car does not own these objects, and they can exist independently of the car.
Interfaces and Contracts
In OOP, an interface is a specification of the methods and properties that an object must have. A contract is a more formal agreement between two objects, that specifies how they will interact with each other.
Interfaces and contracts are used to ensure that objects are compatible with each other. This can help to improve the modularity and flexibility of code.
Design Patterns in JavaScript OOP
Design patterns are reusable solutions to common problems in software design. They can be used to improve the readability, maintainability, and flexibility of code.
There are many different design patterns, but some of the most common ones in JavaScript OOP include:
- Singleton pattern: This pattern ensures that there is only one instance of a particular object in a program.
- Factory pattern: This pattern provides a way to create objects without having to specify their concrete class.
- Observer pattern: This pattern allows objects to be notified of changes to other objects.
Singleton Pattern
The singleton pattern is a design pattern that ensures that there is only one instance of a particular class in a program. This can be useful for objects that need to be globally accessible, or for objects that need to be thread-safe.
The singleton pattern can be implemented in JavaScript in a number of ways. One common way is to use the new.target property. The new.target property is a reference to the constructor that created an object. This can be used to check if an object is a singleton.
function Singleton() {
if (this instanceof Singleton) {
return this;
}
// Create a new instance of the singleton.
this.instance = new Singleton();
return this.instance;
}
const singleton = Singleton(); Factory Pattern
The factory pattern is a design pattern that provides a way to create objects without having to specify their concrete class. This can be useful for creating objects that are dynamically loaded, or for creating objects that depend on the environment.
The factory pattern can be implemented in JavaScript in a number of ways. One common way is to use the create() function. The create() function takes a string as its argument, and returns an object of the corresponding class.
function create(className) {
// Get the class constructor.
const constructor = window[className];
// Create a new instance of the class.
return new constructor();
}
const object = create("MyObject"); Observer Pattern
The observer pattern is a design pattern that allows objects to be notified of changes to other objects. This can be useful for objects that need to be kept in sync, or for objects that need to be informed of events.
The observer pattern can be implemented in JavaScript in a number of ways. One common way is to use the addEventListener() method. The addEventListener() method takes two arguments: the event name, and a callback function. The callback function will be called whenever the event occurs.
const object = {
name: "MyObject",
observers: [],
addEventListener(eventName, callback) {
this.observers.push({
eventName,
callback,
});
},
removeEventListener(eventName, callback) {
const index = this.observers.findIndex(observer => observer.eventName === eventName && observer.callback === callback);
if (index !== -1) {
this.observers.splice(index, 1);
}
},
notifyObservers(eventName) {
this.observers.forEach(observer => observer.callback(eventName));
},
};
object.addEventListener("change", () => {
console.log("The object has changed.");
});
object.notifyObservers("change");