Careeroza
Decorator
Add Behaviour by Wrapping
A Decorator implements the same interface as the object it wraps and adds behaviour before/after delegating. Decorators can be stacked in any order — no explosion of subclasses like LoggingCachingRetryingClient.
Example
import java.util.*;
public class Main {
public static void main(String[] args) {
PriceService service = new LoggingPriceService(new CachingPriceService(new DbPriceService()));
System.out.println(service.price("PHN-01"));
System.out.println(service.price("PHN-01"));
System.out.println(service.price("LAP-02"));
}
}
interface PriceService { int price(String sku); }
class DbPriceService implements PriceService {
public int price(String sku) {
System.out.println(" DB query for " + sku);
return sku.startsWith("PHN") ? 15999 : 54999;
}
}
class CachingPriceService implements PriceService {
private final PriceService inner;
private final Map<String, Integer> cache = new HashMap<>();
CachingPriceService(PriceService inner) { this.inner = inner; }
public int price(String sku) { return cache.computeIfAbsent(sku, inner::price); }
}
class LoggingPriceService implements PriceService {
private final PriceService inner;
LoggingPriceService(PriceService inner) { this.inner = inner; }
public int price(String sku) {
System.out.println("-> price(" + sku + ")");
return inner.price(sku);
}
}Output
-> price(PHN-01)
DB query for PHN-01
15999
-> price(PHN-01)
15999
-> price(LAP-02)
DB query for LAP-02
54999In the JDK
java.io is built on decorators: new BufferedReader(new InputStreamReader(new FileInputStream(f))). Also Collections.unmodifiableList(list) and synchronizedMap(map). Spring AOP proxies (@Transactional, @Cacheable) apply the same idea automatically.