The series of articles on embedded design patterns has been updated several times. Interested readers can click onprevious articles to view:
Basics of Embedded Design Patterns – Inheritance, Encapsulation, and Polymorphism in C Language
Embedded Design Patterns – Simple Factory Pattern in C Language
Embedded Design Patterns – Abstract Factory Pattern in C Language
Embedded Design Patterns – Builder Pattern in C Language
Embedded Design Patterns – Observer Pattern in C Language
Embedded Design Patterns – Prototype Pattern in C Language
Embedded Design Patterns – Bridge Pattern in C Language
Embedded Design Patterns – Mediator Pattern in C Language
This issue updates the structural design pattern – Decorator Pattern.
What is the Decorator Pattern?
The official GOF definition of the Decorator Pattern is:
“Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.”
Translation:Dynamic addition of extra responsibilities to an object. In terms of extending functionality, the Decorator Pattern provides a more flexible alternative to subclassing.
In simpler terms: “without changing the original class code and without breaking the interface,” using a combination of recursion and wrapping to layer new functionalities onto an object at runtime, avoiding class explosion caused by inheritance.
Why is the Decorator Pattern Needed?
In object-oriented design, there are two traditional ways to extend object functionality – modifying the source code or using inheritance, but both introduce structural risks such as tight coupling, class explosion, or violation of the Open/Closed Principle. Therefore, the Decorator Pattern becomes an indispensable supplementary mechanism.
- Runtime Dynamism
It allows for the transparent addition or removal of responsibilities to an object at runtime without recompilation, completely eliminating compile-time hard binding.
- Suppressing Class Explosion
Using composition instead of multiple inheritance reduces the number of classes from exponential to linear.
- Strictly Adhering to the Open/Closed Principle
No changes to the original object’s interface; new responsibilities are implemented only through new decorator classes, allowing existing code to remain frozen and untested.
- Clear Structure and Recursion
Transparent wrapping supports arbitrary levels of nesting, with each layer of responsibility independently encapsulated, making the system behavior enhancement path clear at a glance.
The Decorator Pattern replaces “inheritance + source code modification” with “composition + recursive wrapping,” providing flexible extension capabilities while maintaining a stable and simple architecture, serving as the core solution to resolve the contradiction between dynamic functionality enhancement and code rigidity.
Core Roles

- Component
It is the “root protocol” of the entire decoration system. Both the original object and the decorator implement this interface, allowing the client to treat the “real object” and the “wrapped object” consistently. The interface is usually simplified, declaring only those core operations that may be extended.
- ConcreteComponent
The “wrapped object,” which serves as the “anchor point” for all subsequent additional responsibilities. It only knows how to perform its own duties and is unaware of how many layers of decoration will be applied externally; this “zero awareness” ensures the stability of the original code.
- Decorator
The common parent class (or interface) for all decorators. It “is both Component and holds a Component” – obtaining an internal object through constructor injection and directly delegating the default implementation to this internal object. The existence of the Decorator makes “layer upon layer” possible, while allowing specific decorators to focus only on the “part they want to add.”
- ConcreteDecorator
Adds new logic before and after forwarding requests, adds new states (fields) to support new logic, and even provides entirely new interfaces (if supported by the language). Multiple ConcreteDecorators can wrap each other in any order, forming a “responsibility stack” to achieve flexible runtime combinations.
UML Example

Comparison with Similar Patterns
The pattern most similar to the Decorator Pattern is the Proxy Pattern. We will discuss the Proxy Pattern later.
| Comparison Dimension | Decorator Pattern | Proxy Pattern |
|---|---|---|
| Structural Form | Both implement the same interface and hold and forward requests to the internal object | Also implements the interface and forwards requests to the “real subject” |
| Client Perspective | No need to know whether it is wrapped, used transparently | No need to know whether it is proxied, used transparently |
| Class Diagram Structure | Almost identical (interface → implementation class → wrapper class) | Almost identical (interface → real subject → proxy class) |
Core Differences
- Decorator:Function enhancement – layering new behaviors, interface remains unchanged but semantics expand.
- Proxy: Access control – performs permission, delay, remote, caching, etc., control before and after accessing the real object, without adding new functional semantics.
Example
Below, we will still use a simple LED case to intuitively feel how the core roles of the Decorator Pattern should be defined in actual development.

- Component (Led)
All subsequent structures must have Led as the first member, allowing any object pointer to be safely cast to Led, achieving “the same interface with multiple implementations” static polymorphism.

- ConcreteComponent (RawLed)
Its function pointer is stored in raw._ for the decorator to call through inner->on(inner); hardware write operations take effect immediately when the callback occurs.

- Decorator (Dec)
The first field of any ConcreteDecorator instance is Dec, so ((Dec *)ptr)->inner can safely obtain the address of the next level object, achieving a recursive call chain.

- ConcreteDecorator (inv)
When the client calls l->on(l), it actually enters invOn → calls inner->off → writes to the ODR register, forming a “client intention to light → actual pin off” reverse relationship; the call chain depth is 1 level of decoration.
The final call path of on() from the client is replaced in place with a call chain containing “reverse” logic, thus the LED control responsibility (originally only writing to the register) hasan additional “reverse” step added, while the object memory address and register operationcode remains unchanged – this aligns with the GoF’s intent of “dynamically adding extra responsibilities“: extending behavior without altering existing implementations.
Conclusion
The Decorator Pattern mainly provides a zero memory allocation, zero source code modification extension mechanism in embedded development.
It abstracts the extension logic into homogenous objects that can be nested layer by layer, using static composition instead of inheritance, linking at compile time, and only increasing deterministic function call overhead at runtime; it maintains the original code frozen while satisfying functional safety and bare-metal resource constraints, making it a core means of achieving an extension mechanism in embedded systems without dynamic memory, source code intrusion, or branching judgments.
