Loading...
Loading...
By continuing to use the platform, you accept the terms of the Privacy Policy and the use of cookies.
Decorator is structural design pattern that allows dynamically adding new responsibilities to object by wrapping it in "decorator" objects. At same time original object isn't changed, and additional logic is "mixed in" from outside.
Why Decorator is Needed:
Decorator pattern allows flexibly extending object functionality without changing their code. This is especially convenient if inheritance cannot (or shouldn't) be used.
When need to add new capabilities to objects "on the fly" without creating many subclasses.
When impossible or undesirable to modify class directly (e.g., classes from third-party library).
When several types of functionality need to be combined differently. Decorators allow combining features without creating explosive number of subclasses.
// 1. Base interface for components
interface DataSource {
writeData(data: string): void;
readData(): string;
}
// 2. Concrete class implementing interface
class FileDataSource implements DataSource {
private filename: string;
private buffer: string;
constructor(filename: string) {
this.filename = filename;
this.buffer = "";
}
writeData(data: string): void {
// Logic for writing data to file
this.buffer = data;
console.log(`Data "${data}" written to file: ${this.filename}`);
}
readData(): string {
// Logic for reading data from file
console.log(`Reading data from file: ${this.filename}`);
return this.buffer;
}
}
// 3. Common decorator class
class DataSourceDecorator implements DataSource {
protected wrappee: DataSource;
constructor(source: DataSource) {
this.wrappee = source;
}
writeData(data: string): void {
this.wrappee.writeData(data);
}
readData(): string {
return this.wrappee.readData();
}
}
// 4. Concrete decorators
class EncryptionDecorator extends DataSourceDecorator {
writeData(data: string): void {
const encrypted = btoa(data); // simple Base64 "encryption"
console.log("Encrypting data...");
super.writeData(encrypted);
}
readData(): string {
const data = super.readData();
console.log("Decrypting data...");
return atob(data);
}
}
class CompressionDecorator extends DataSourceDecorator {
writeData(data: string): void {
console.log("Compressing data...");
const compressed = `COMPRESSED(${data})`;
super.writeData(compressed);
}
readData(): string {
const data = super.readData();
console.log("Decompressing data...");
return data.replace(/^COMPRESSED\(|\)$/g, "");
}
}
// 5. Client code
function clientCode() {
const file = new FileDataSource("data.txt");
// Wrap file with decorators
const encryption = new EncryptionDecorator(file);
const compression = new CompressionDecorator(encryption);
// Write
compression.writeData("Hello, Decorator!");
// Read
console.log("Final data:", compression.readData());
}
clientCode();
Important:
If you use too many decorators, this can complicate operation sequence logic. Evaluate how much dynamics and flexibility is really needed before introducing Decorator pattern.
Read more about Decorator here.