CHAPTER 12
Intermediate
Decorator and Proxy Patterns
Updated: May 16, 2026
35 min read
# CHAPTER 12
Decorator and Proxy Patterns
1. Introduction
Inheritance is static. If you have aUser class and you want to add caching capabilities, you might create a CachedUser subclass. If you want to add logging, you create a LoggedUser subclass. But what if you want a user that is both cached *and* logged? You must create a CachedAndLoggedUser subclass. This leads to a catastrophic explosion of subclasses known as the "Class Explosion" problem. We need a way to add behaviors to objects dynamically at runtime, without rewriting the original class or relying on rigid inheritance. In this chapter, we will master the Decorator and Proxy patterns. We will learn how to wrap objects like Russian nesting dolls to extend their functionality, and deploy Proxies to control access to heavy, sensitive resources.
2. Learning Objectives
By the end of this chapter, you will be able to:- Identify the "Class Explosion" problem caused by static inheritance.
- Define the intent and structural mechanics of the Decorator Pattern.
- Understand the concept of "Object Wrappers."
- Differentiate between a Decorator (adding behavior) and a Proxy (controlling access).
- Implement a lazy-loading Virtual Proxy to optimize performance.
3. The Decorator Pattern (The Wrapper)
The Decorator allows you to attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.- The Concept: Think of wearing clothes. You are the base object. When it gets cold, you wrap yourself in a Sweater (a Decorator). The Sweater adds the "Warmth" behavior. You don't have to genetically alter your DNA (Inheritance) to become warm; you just add a wrapper at runtime.
- The Mechanics: The Decorator class implements the exact same interface as the base object it is wrapping. This means the client code cannot tell the difference between the base object and the decorated object.
4. The Proxy Pattern (The Bodyguard)
A Proxy provides a surrogate or placeholder for another object to control access to it.- The Concept: A credit card is a Proxy for a bank account. It implements the same interface (making payments), but it controls access (checking PINs, preventing overdrafts) without you needing to carry stacks of physical cash.
- Types of Proxies:
- *Virtual Proxy (Lazy Loading):* If an object is massive (like a high-res image), the Proxy takes its place and delays loading the actual image into memory until the exact second it is needed on screen.
-
*Protection Proxy:* Checks access rights before allowing a method to be called (e.g., verifying a user is an Admin before executing
deleteDatabase()).
5. Decorator vs. Proxy (The Difference)
They look identical structurally (they both wrap an object and implement its interface), but their intents are entirely different.- Decorator: Adds *new* features or behaviors to an object dynamically. (e.g., Taking a base Notification object and wrapping it to also send an SMS).
- Proxy: Controls *access* to an object without changing its behavior. (e.g., Taking a base Database object and intercepting calls to check for authentication).
6. UML Diagram
*Decorator / Wrapper Structure*
text
7. Code Example: Decorator (PHP)
Let's solve the Class Explosion problem for a Notification system.
php
8. Best Practices
- Middleware is a Decorator: If you have ever used modern web frameworks (Express.js, Laravel), you have used Middleware. Middleware is simply the Decorator pattern. A basic HTTP Request object is wrapped in an Authentication Decorator, then wrapped in a Logging Decorator, forming a pipeline.
9. Common Mistakes
-
Identity Crisis: Because the Decorator wraps the original object, if you write code that checks the exact class type (e.g.,
if ($obj instanceof EmailNotifier)), it will fail when the object is wrapped inside anSMSDecorator. You must program strictly to the Interface (if ($obj instanceof Notifier)).
10. Mini Project: Build a Protection Proxy
-
1.
The Target: Create a
Databaseinterface and aRealDatabaseclass with aquery()method.
-
2.
The Proxy: Create a
DatabaseProxyclass that implementsDatabaseand wrapsRealDatabase.
-
3.
The Logic: Add a
$roleproperty to the Proxy constructor. In the Proxy'squery()method, write anifstatement. If$role == 'admin', forward the call to theRealDatabase. If not, throw an "Access Denied" exception.
11. Practice Exercises
- 1. Define the "Class Explosion" problem associated with static inheritance. How does the Decorator pattern resolve this using Composition?
- 2. Explain the architectural difference in intent between a Decorator and a Proxy, despite their identical structural UML.
12. MCQs with Answers
Question 1
An architect needs to add logging and caching behaviors to a specific object at runtime, but wants to avoid creating permanent subclasses for every possible combination of features. Which structural pattern utilizes "wrapper" objects to solve this?
Question 2
A massive high-resolution image object takes 5 seconds to load into memory. To optimize performance, the system uses a placeholder object that implements the same image interface, but only loads the heavy 5-second image into memory if the user actually scrolls down to look at it. What type of pattern is this?
13. Interview Questions
- Q: Explain how modern web framework "Middleware" (like HTTP request interception) is an implementation of the Decorator pattern.
- Q: Walk me through the mechanics of a "Virtual Proxy." How does placing a Proxy in front of an expensive resource optimize memory and CPU usage in an enterprise application?
- Q: On a whiteboard, draw the "Russian Nesting Doll" architecture of a Decorator pattern. Explain how a single method call cascades down through multiple layers of wrappers to reach the base component.