CrackedRuby CrackedRuby

Overview

The Decorator Pattern provides a flexible alternative to subclassing for extending functionality. Instead of creating inheritance hierarchies for each combination of features, decorators wrap objects and add behavior incrementally at runtime. Each decorator implements the same interface as the components it decorates, maintaining transparency to clients.

The pattern originated from the Gang of Four design patterns catalog as a solution to the class explosion problem that occurs when using inheritance to add features. When multiple optional features can combine in various ways, creating subclasses for each combination becomes unmaintainable. A system with five optional features would require 32 subclasses to cover all combinations.

Decorators compose behavior by wrapping one component with another. Each decorator adds one specific responsibility while delegating the core functionality to the wrapped component. Multiple decorators can wrap the same component, with each adding its own layer of behavior. The outermost decorator receives the initial request and passes it through the chain, with each decorator performing its operation before or after delegating.

# Basic decorator structure
class Component
  def operation
    "Base operation"
  end
end

class Decorator
  def initialize(component)
    @component = component
  end

  def operation
    @component.operation
  end
end

class ConcreteDecorator < Decorator
  def operation
    "Added behavior: #{@component.operation}"
  end
end

component = Component.new
decorated = ConcreteDecorator.new(component)
decorated.operation
# => "Added behavior: Base operation"

The pattern separates concerns by placing each optional feature in its own class. Components handle core functionality while decorators handle optional features. This separation follows the Single Responsibility Principle and makes both components and decorators easier to understand and maintain.

Key Principles

The Decorator Pattern operates on four core principles: interface consistency, composition over inheritance, transparent wrapping, and recursive composition.

Interface Consistency

Decorators and components implement the same interface. Clients interact with decorated objects using the same methods they would use with undecorated objects. This transparency allows decorators to substitute for components without requiring client code changes. The interface defines the contract that both components and decorators must fulfill.

# All participants share the same interface
module DataSource
  def read_data
    raise NotImplementedError
  end

  def write_data(data)
    raise NotImplementedError
  end
end

class FileDataSource
  include DataSource

  def initialize(filename)
    @filename = filename
  end

  def read_data
    File.read(@filename)
  end

  def write_data(data)
    File.write(@filename, data)
  end
end

class EncryptionDecorator
  include DataSource

  def initialize(source)
    @source = source
  end

  def read_data
    decrypt(@source.read_data)
  end

  def write_data(data)
    @source.write_data(encrypt(data))
  end

  private

  def encrypt(data)
    data.reverse
  end

  def decrypt(data)
    data.reverse
  end
end

Composition Over Inheritance

The pattern favors object composition instead of class inheritance for extending functionality. Each decorator contains a reference to a component rather than inheriting from it. This approach provides more flexibility than inheritance because decorators can combine at runtime in any order and quantity. Inheritance creates static relationships at compile time, while composition creates dynamic relationships at runtime.

Transparent Wrapping

Decorators wrap components without exposing the wrapping mechanism to clients. From the client's perspective, a decorated object behaves like any other object implementing the interface. The decorator forwards requests to the wrapped component after adding its own behavior. This forwarding happens transparently, maintaining the abstraction boundary.

Recursive Composition

Decorators can wrap other decorators because both implement the same interface. This recursive structure creates chains where each decorator adds behavior and delegates to the next component in the chain. The chain terminates with a concrete component that implements the actual functionality. Each decorator in the chain processes the request during forwarding, either before delegation (pre-processing) or after delegation (post-processing).

# Multiple decorators wrapping recursively
source = FileDataSource.new('data.txt')
encrypted = EncryptionDecorator.new(source)
compressed = CompressionDecorator.new(encrypted)
logged = LoggingDecorator.new(compressed)

# Request flows through: logged -> compressed -> encrypted -> source
logged.write_data("sensitive information")

The pattern adheres to the Open/Closed Principle by allowing systems to add new decorators without modifying existing components or decorators. Each new decorator extends functionality while preserving the original component interface.

Ruby Implementation

Ruby provides multiple approaches for implementing decorators, from explicit decorator classes to modules and SimpleDelegator. Each approach offers different trade-offs between explicitness and conciseness.

Explicit Decorator Classes

The traditional approach defines a base decorator class that wraps a component and implements the component interface. Concrete decorators inherit from the base decorator and override specific methods.

class CoffeeComponent
  def cost
    2.0
  end

  def description
    "Coffee"
  end
end

class CoffeeDecorator
  def initialize(coffee)
    @coffee = coffee
  end

  def cost
    @coffee.cost
  end

  def description
    @coffee.description
  end
end

class MilkDecorator < CoffeeDecorator
  def cost
    @coffee.cost + 0.5
  end

  def description
    "#{@coffee.description}, Milk"
  end
end

class SugarDecorator < CoffeeDecorator
  def cost
    @coffee.cost + 0.2
  end

  def description
    "#{@coffee.description}, Sugar"
  end
end

coffee = CoffeeComponent.new
with_milk = MilkDecorator.new(coffee)
with_milk_and_sugar = SugarDecorator.new(with_milk)

with_milk_and_sugar.cost
# => 2.7
with_milk_and_sugar.description
# => "Coffee, Milk, Sugar"

Module-Based Decoration

Ruby's module system enables decoration through dynamic module inclusion. This approach extends objects at runtime by mixing in modules that add behavior. The extend method adds module methods as singleton methods on individual objects without affecting the class or other instances.

module Markdown
  def render
    "**#{super}**"
  end
end

module HTMLEscape
  def render
    super.gsub('<', '&lt;').gsub('>', '&gt;')
  end
end

class TextContent
  attr_reader :text

  def initialize(text)
    @text = text
  end

  def render
    @text
  end
end

content = TextContent.new("<Hello>")
content.extend(HTMLEscape)
content.extend(Markdown)

content.render
# => "**&lt;Hello&gt;**"

SimpleDelegator

Ruby's standard library includes SimpleDelegator, which provides automatic method forwarding. Decorators inherit from SimpleDelegator and override only the methods they need to modify. All other methods forward automatically to the wrapped object.

require 'delegate'

class TimestampDecorator < SimpleDelegator
  def write(data)
    __getobj__.write("[#{Time.now}] #{data}")
  end
end

class UppcaseDecorator < SimpleDelegator
  def write(data)
    __getobj__.write(data.upcase)
  end
end

class Logger
  def write(message)
    puts message
  end
end

logger = Logger.new
timestamped = TimestampDecorator.new(logger)
uppercased = UppcaseDecorator.new(timestamped)

uppercased.write("error occurred")
# Output: [2025-10-06 14:30:45] ERROR OCCURRED

Refinements for Scoped Decoration

Refinements provide lexically scoped modifications to classes. Unlike global monkey patching, refinements apply only within the scope where they are activated using using. This approach suits scenarios requiring temporary behavior modification.

module StringFormatting
  refine String do
    def emphasize
      "*** #{self} ***"
    end
  end
end

class Formatter
  using StringFormatting

  def format(text)
    text.emphasize
  end
end

formatter = Formatter.new
formatter.format("Important")
# => "*** Important ***"

"Important".emphasize
# => NoMethodError: undefined method 'emphasize'

Method Aliasing

Method aliasing creates decorators by renaming original methods and defining new methods that add behavior before calling the original. This technique modifies classes directly rather than wrapping instances.

class Calculator
  def add(a, b)
    a + b
  end
end

class Calculator
  alias_method :add_without_logging, :add

  def add(a, b)
    result = add_without_logging(a, b)
    puts "Added #{a} and #{b}, result: #{result}"
    result
  end
end

calc = Calculator.new
calc.add(3, 4)
# Output: Added 3 and 4, result: 7
# => 7

Practical Examples

The Decorator Pattern applies to scenarios requiring flexible feature combinations without creating inheritance hierarchies.

HTTP Client with Optional Features

An HTTP client supports optional features like authentication, caching, logging, and retry logic. Each feature exists as a decorator, allowing clients to compose only needed functionality.

class HTTPClient
  def get(url)
    # Simulate HTTP request
    { status: 200, body: "Response from #{url}" }
  end

  def post(url, data)
    { status: 201, body: "Created" }
  end
end

class AuthenticationDecorator
  def initialize(client, token)
    @client = client
    @token = token
  end

  def get(url)
    puts "Adding auth header: Bearer #{@token}"
    @client.get(url)
  end

  def post(url, data)
    puts "Adding auth header: Bearer #{@token}"
    @client.post(url, data)
  end
end

class CachingDecorator
  def initialize(client)
    @client = client
    @cache = {}
  end

  def get(url)
    if @cache.key?(url)
      puts "Cache hit for #{url}"
      @cache[url]
    else
      puts "Cache miss for #{url}"
      @cache[url] = @client.get(url)
    end
  end

  def post(url, data)
    @cache.clear
    @client.post(url, data)
  end
end

class RetryDecorator
  def initialize(client, max_retries: 3)
    @client = client
    @max_retries = max_retries
  end

  def get(url)
    attempt = 0
    begin
      @client.get(url)
    rescue => e
      attempt += 1
      retry if attempt < @max_retries
      raise
    end
  end

  def post(url, data)
    attempt = 0
    begin
      @client.post(url, data)
    rescue => e
      attempt += 1
      retry if attempt < @max_retries
      raise
    end
  end
end

# Compose features as needed
client = HTTPClient.new
authenticated = AuthenticationDecorator.new(client, "abc123")
cached = CachingDecorator.new(authenticated)
resilient = RetryDecorator.new(cached)

resilient.get("https://api.example.com/users")
# Output: Cache miss for https://api.example.com/users
#         Adding auth header: Bearer abc123
# => {:status=>200, :body=>"Response from https://api.example.com/users"}

Stream Processing Pipeline

Data streams pass through decorators that transform content. Each decorator adds one transformation step, creating processing pipelines where data flows through multiple stages.

class DataStream
  def initialize(data)
    @data = data
  end

  def read
    @data
  end
end

class FilterDecorator
  def initialize(stream, &condition)
    @stream = stream
    @condition = condition
  end

  def read
    @stream.read.select(&@condition)
  end
end

class MapDecorator
  def initialize(stream, &transform)
    @stream = stream
    @transform = transform
  end

  def read
    @stream.read.map(&@transform)
  end
end

class TakeDecorator
  def initialize(stream, count)
    @stream = stream
    @count = count
  end

  def read
    @stream.read.take(@count)
  end
end

# Build processing pipeline
numbers = DataStream.new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
evens = FilterDecorator.new(numbers) { |n| n.even? }
doubled = MapDecorator.new(evens) { |n| n * 2 }
first_three = TakeDecorator.new(doubled, 3)

first_three.read
# => [4, 8, 12]

Text Rendering with Formatting

Text content passes through decorators that apply formatting transformations. Each decorator adds one type of formatting, allowing combinations like bold italic text or escaped HTML with line breaks.

class Text
  attr_reader :content

  def initialize(content)
    @content = content
  end

  def render
    @content
  end
end

class BoldDecorator
  def initialize(text)
    @text = text
  end

  def render
    "<b>#{@text.render}</b>"
  end
end

class ItalicDecorator
  def initialize(text)
    @text = text
  end

  def render
    "<i>#{@text.render}</i>"
  end
end

class LinkDecorator
  def initialize(text, url)
    @text = text
    @url = url
  end

  def render
    "<a href=\"#{@url}\">#{@text.render}</a>"
  end
end

class ParagraphDecorator
  def initialize(text)
    @text = text
  end

  def render
    "<p>#{@text.render}</p>"
  end
end

# Create formatted text
text = Text.new("Click here")
bold_text = BoldDecorator.new(text)
link = LinkDecorator.new(bold_text, "https://example.com")
paragraph = ParagraphDecorator.new(link)

paragraph.render
# => "<p><a href=\"https://example.com\"><b>Click here</b></a></p>"

Notification System with Channels

A notification system sends messages through multiple channels. Each decorator adds a delivery channel while preserving the core notification functionality. Users configure which channels receive notifications based on message priority or user preferences.

class Notification
  attr_reader :message

  def initialize(message)
    @message = message
  end

  def send
    # Base notification does nothing
  end
end

class EmailDecorator
  def initialize(notification, email)
    @notification = notification
    @email = email
  end

  def send
    puts "Sending email to #{@email}: #{@notification.message}"
    @notification.send
  end
end

class SMSDecorator
  def initialize(notification, phone)
    @notification = notification
    @phone = phone
  end

  def send
    puts "Sending SMS to #{@phone}: #{@notification.message}"
    @notification.send
  end
end

class SlackDecorator
  def initialize(notification, channel)
    @notification = notification
    @channel = channel
  end

  def send
    puts "Posting to Slack #{@channel}: #{@notification.message}"
    @notification.send
  end
end

# Send critical alert through all channels
alert = Notification.new("System failure detected")
with_email = EmailDecorator.new(alert, "admin@example.com")
with_sms = SMSDecorator.new(with_email, "+1234567890")
with_slack = SlackDecorator.new(with_sms, "#alerts")

with_slack.send
# Output: Posting to Slack #alerts: System failure detected
#         Sending SMS to +1234567890: System failure detected
#         Sending email to admin@example.com: System failure detected

Design Considerations

Selecting between decorators and alternatives depends on the required flexibility, number of feature combinations, and runtime versus compile-time composition needs.

When to Use Decorators

Decorators fit scenarios with multiple optional features that combine in various ways. A reporting system might support optional features for pagination, sorting, filtering, and formatting. Creating subclasses for each combination produces exponential class growth. Decorators compose these features independently at runtime.

Systems requiring runtime behavior modification benefit from decorators. A web application might apply different security policies based on user roles or request context. Decorators add appropriate validation and authorization without modifying the core request handling logic.

Alternatives to Decorators

Strategy Pattern provides an alternative when selecting between different algorithms rather than combining features. A payment processor might support multiple payment methods, but each transaction uses exactly one method. Strategy Pattern swaps entire algorithms while Decorator Pattern combines incremental behaviors.

Inheritance works when the feature set is fixed and small. A graphics system with shape classes might support filled and unfilled variants through two subclasses. Adding more orthogonal features like borders, shadows, or patterns would require decorators to avoid class explosion.

Chain of Responsibility passes requests through a chain of handlers where each decides whether to process or forward the request. Decorators always process and forward, while Chain of Responsibility handlers may terminate the chain. Logging systems use Chain of Responsibility when different log levels route to different handlers.

Trade-offs

Decorators increase object count and indirection. Each decorator adds a wrapper object, and deeply nested decorators create long delegation chains. Performance-sensitive code might suffer from the overhead of multiple method calls. Profiling identifies when decorator chains become bottlenecks.

# Decorator overhead example
class Component
  def operation
    # Simple operation
  end
end

# Each decorator adds a method call
decorated = Decorator1.new(
  Decorator2.new(
    Decorator3.new(
      Decorator4.new(Component.new)
    )
  )
)

# Operation now involves 5 method calls instead of 1
decorated.operation

Decorators complicate debugging because stack traces include all decorators in the chain. Finding the source of behavior requires understanding which decorators are active and their order. Explicit naming conventions and logging help track decorator chains during development.

Interface pollution occurs when decorators must implement large interfaces but only modify a few methods. Each decorator must forward all unmodified methods, creating repetitive boilerplate. SimpleDelegator or method_missing reduces this burden in Ruby but can hide errors.

Decorator Ordering

Decorator order affects behavior when decorators interact. An encryption decorator applied before a compression decorator produces different results than compression applied before encryption. Systems must document decorator dependencies and enforce correct ordering.

# Order matters for interacting decorators
source = FileDataSource.new('data.txt')

# Compress then encrypt
compressed_first = EncryptionDecorator.new(
  CompressionDecorator.new(source)
)

# Encrypt then compress
encrypted_first = CompressionDecorator.new(
  EncryptionDecorator.new(source)
)

# These produce different results
compressed_first.write_data("test")
encrypted_first.write_data("test")

Common Patterns

Several decorator patterns address recurring design needs and implementation challenges.

Transparent Decorator

Transparent decorators forward all interface methods to wrapped components. Only overridden methods add behavior, while all other methods delegate unchanged. This pattern maintains full interface compatibility and allows decorators to appear anywhere in a chain.

require 'delegate'

class TransparentDecorator < SimpleDelegator
  def enhanced_method
    "Enhanced: #{__getobj__.enhanced_method}"
  end

  # All other methods forward automatically
end

class Component
  def enhanced_method
    "base"
  end

  def other_method
    "other"
  end

  def another_method
    "another"
  end
end

component = Component.new
decorated = TransparentDecorator.new(component)

decorated.enhanced_method    # => "Enhanced: base"
decorated.other_method       # => "other"
decorated.another_method     # => "another"

Half-Object Plus Protocol

This pattern splits decorator responsibilities between a wrapper (half-object) and a protocol for accessing the wrapped object. The decorator implements the protocol for retrieving the wrapped component, allowing introspection and unwrapping.

module Decorable
  def component
    @component
  end

  def base_component
    current = self
    current = current.component while current.respond_to?(:component)
    current
  end
end

class Decorator
  include Decorable

  def initialize(component)
    @component = component
  end
end

class ConcreteDecorator < Decorator
  def operation
    "decorated: #{@component.operation}"
  end
end

component = Component.new
decorated = ConcreteDecorator.new(ConcreteDecorator.new(component))

decorated.base_component == component  # => true

Conditional Decorator

Conditional decorators apply behavior based on runtime conditions. The decorator checks conditions before deciding whether to apply its behavior or delegate directly.

class ConditionalCachingDecorator
  def initialize(component, cache_condition)
    @component = component
    @cache_condition = cache_condition
    @cache = {}
  end

  def fetch(key)
    if @cache_condition.call(key)
      @cache[key] ||= @component.fetch(key)
    else
      @component.fetch(key)
    end
  end
end

repository = DataRepository.new

# Only cache GET requests
cached = ConditionalCachingDecorator.new(
  repository,
  ->(key) { key.start_with?('GET:') }
)

cached.fetch('GET:/users')      # Cached
cached.fetch('POST:/users')     # Not cached

Composite Decorator

Composite decorators combine multiple decorators into a single unit. Instead of wrapping components multiple times, a composite decorator applies several transformations internally.

class CompositeDecorator
  def initialize(component, decorators)
    @component = component
    @decorators = decorators
  end

  def operation
    @decorators.reduce(@component) do |comp, decorator_class|
      decorator_class.new(comp)
    end.operation
  end
end

component = Component.new
composite = CompositeDecorator.new(
  component,
  [LoggingDecorator, TimingDecorator, ValidationDecorator]
)

composite.operation

Decorator Factory

Factories construct decorated objects based on configuration. The factory encapsulates decorator selection and ordering logic, providing a simpler interface for clients.

class HTTPClientFactory
  def self.build(options = {})
    client = HTTPClient.new

    client = AuthenticationDecorator.new(client, options[:token]) if options[:auth]
    client = CachingDecorator.new(client) if options[:cache]
    client = RetryDecorator.new(client, max_retries: options[:retries]) if options[:retry]
    client = LoggingDecorator.new(client) if options[:log]

    client
  end
end

# Simple interface for complex decoration
client = HTTPClientFactory.build(
  auth: true,
  cache: true,
  retry: true,
  token: 'abc123',
  retries: 3
)

Memoizing Decorator

Memoizing decorators cache computation results to avoid redundant processing. The decorator checks its cache before delegating to the wrapped component, storing results for future requests.

class MemoizingDecorator
  def initialize(component)
    @component = component
    @cache = {}
  end

  def compute(input)
    @cache[input] ||= @component.compute(input)
  end

  def clear_cache
    @cache.clear
  end
end

expensive_calculator = ExpensiveCalculator.new
memoized = MemoizingDecorator.new(expensive_calculator)

memoized.compute(42)  # Calculates
memoized.compute(42)  # Returns cached result

Common Pitfalls

Decorators introduce several categories of errors related to interface compatibility, ordering, and state management.

Interface Violations

Decorators must implement the complete interface of components they wrap. Partial implementations break when clients call methods the decorator does not override. This problem appears when interfaces grow and existing decorators do not update.

# Problem: Incomplete interface implementation
class Component
  def method_a
    "A"
  end

  def method_b
    "B"
  end

  def method_c
    "C"
  end
end

class IncompleteDecorator
  def initialize(component)
    @component = component
  end

  def method_a
    "Decorated #{@component.method_a}"
  end

  # Missing method_b and method_c
end

component = Component.new
decorated = IncompleteDecorator.new(component)

decorated.method_a  # => "Decorated A"
decorated.method_b  # => NoMethodError

Solution: Use SimpleDelegator for automatic forwarding or implement method_missing to catch unhandled methods.

# Solution: Complete delegation
require 'delegate'

class CompleteDecorator < SimpleDelegator
  def method_a
    "Decorated #{__getobj__.method_a}"
  end

  # method_b and method_c forward automatically
end

State Leakage Between Decorators

Decorators that maintain state can cause unexpected behavior when state from one decorator affects another. Stateful decorators must isolate their state from the wrapped component and other decorators.

# Problem: State leakage
class CountingDecorator
  @@count = 0  # Class variable shared across instances

  def initialize(component)
    @component = component
  end

  def operation
    @@count += 1
    @component.operation
  end
end

decorator1 = CountingDecorator.new(Component.new)
decorator2 = CountingDecorator.new(Component.new)

decorator1.operation  # @@count = 1
decorator2.operation  # @@count = 2, unexpected cross-contamination

Solution: Use instance variables and ensure proper initialization.

# Solution: Isolated state
class CountingDecorator
  def initialize(component)
    @component = component
    @count = 0
  end

  def operation
    @count += 1
    @component.operation
  end
end

Incorrect Decorator Ordering

Decorators applied in the wrong order produce incorrect results. Compression applied after encryption compresses already-encrypted data, while encryption applied after compression encrypts already-compressed data. These produce different outcomes and may affect security or efficiency.

# Order-dependent behavior
class EncryptionDecorator
  def write(data)
    @component.write(encrypt(data))
  end
end

class CompressionDecorator
  def write(data)
    @component.write(compress(data))
  end
end

# Wrong order: encrypts compressed data
source = FileSource.new
compressed = CompressionDecorator.new(source)
encrypted = EncryptionDecorator.new(compressed)

# Correct order: compresses encrypted data
source = FileSource.new
encrypted = EncryptionDecorator.new(source)
compressed = CompressionDecorator.new(encrypted)

Excessive Decorator Depth

Deep decorator chains increase complexity and hurt performance. Each decorator adds overhead for method dispatch and object creation. Systems with more than five layers of decorators often indicate design problems.

Solution: Group related decorators into composite decorators or reconsider the design.

Forgetting Super Calls

Module-based decorators must call super to invoke the next method in the chain. Forgetting super terminates the chain prematurely, breaking functionality.

# Problem: Missing super
module Decorator
  def operation
    "Decorated"  # Forgot to call super
  end
end

class Component
  def operation
    "Base"
  end
end

component = Component.new
component.extend(Decorator)
component.operation  # => "Decorated", lost base functionality

Solution: Always call super unless intentionally overriding.

# Solution: Include super
module Decorator
  def operation
    "Decorated: #{super}"
  end
end

component = Component.new
component.extend(Decorator)
component.operation  # => "Decorated: Base"

Type Checking Failures

Code that checks object types using instance_of? or class fails with decorated objects because decorators wrap the original type. Type checks should use is_a? or check for interface compliance rather than concrete types.

# Problem: Type checking decorated objects
class AuthDecorator
  def initialize(user)
    @user = user
  end

  def name
    @user.name
  end
end

user = User.new("Alice")
decorated = AuthDecorator.new(user)

user.instance_of?(User)       # => true
decorated.instance_of?(User)  # => false, breaks type checks

Solution: Use duck typing or respond_to? to check capabilities.

# Solution: Duck typing
def process_user(user)
  if user.respond_to?(:name)
    puts user.name
  end
end

process_user(decorated)  # Works regardless of decoration

Reference

Core Components

Component Purpose Responsibility
Component Defines interface Declares operations for objects
ConcreteComponent Implements base object Provides core functionality
Decorator Maintains component reference Forwards requests to component
ConcreteDecorator Adds specific behavior Extends component functionality

Ruby Implementation Approaches

Approach Syntax Use When
Class-based class Decorator; def initialize(component) Need explicit class hierarchy
Module-based module Decorator; def method; super Want runtime extension per instance
SimpleDelegator class Decorator < SimpleDelegator Want automatic method forwarding
Refinements module M; refine C do Need lexically scoped modifications
Method aliasing alias_method :old, :new Modifying existing classes

Decorator vs Alternatives

Pattern Purpose Key Difference
Decorator Add responsibilities Composes behaviors incrementally
Strategy Swap algorithms Replaces entire algorithm
Proxy Control access Adds indirection, same interface
Adapter Convert interfaces Changes interface to match client
Chain of Responsibility Handle requests May terminate chain early

Design Decision Matrix

Scenario Choose Decorator When Choose Alternative When
Feature combinations Multiple optional features combine Single feature selection needed
Modification time Runtime composition required Compile-time inheritance sufficient
Number of combinations Exponential combinations (2^n) Linear combinations (n)
Interface stability Interface remains constant Interface varies by type
Transparency Clients unaware of decoration Clients need decoration knowledge

Common Decorator Applications

Domain Decorator Type Purpose
I/O streams BufferedDecorator Add buffering to streams
I/O streams EncryptionDecorator Encrypt data transparently
I/O streams CompressionDecorator Compress data automatically
UI components ScrollableDecorator Add scrolling to components
UI components BorderDecorator Add borders to components
Web requests AuthenticationDecorator Add auth headers
Web requests CachingDecorator Cache responses
Web requests LoggingDecorator Log requests and responses
Notifications EmailDecorator Send via email
Notifications SMSDecorator Send via SMS
Text processing FormattingDecorator Apply text formatting
Text processing ValidationDecorator Validate content

Performance Characteristics

Aspect Impact Mitigation
Method calls O(n) overhead for n decorators Limit decorator depth to 5
Memory One object per decorator Use flyweight for shared decorators
Object creation Allocation cost per decorator Pool decorator instances
Cache efficiency Poor locality with deep chains Flatten frequently-used chains

Anti-Patterns

Anti-Pattern Problem Solution
Decorator explosion Too many decorator classes Use composite decorators
Order dependency Specific order required Document dependencies clearly
Stateful decorators Shared mutable state Isolate state per instance
Interface bloat Decorators implement unused methods Use SimpleDelegator
Deep nesting More than 5 decorator layers Refactor into composite
Type checking instanceof checks fail Use duck typing