DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Code of Shadows: Master Shifu and Po Use Functional Java to Solve the Decorator Pattern Mystery
  • Decorating Microservices
  • Decorator Pattern to Solve Integration Scenarios in Existing Systems

Trending

  • Optimizing High-Volume REST APIs Using Redis Caching and Spring Boot (With Load Testing Code)
  • How SaaS Architectures Break at Scale — and the Engineering Decisions That Prevent It
  • Using LLMs to Automate Data Cleaning and Transformation Pipelines
  • AWS Kiro: The Agentic IDE That Makes Specs the Unit of Work

The Decorator Builder

The Decorator Pattern is useful, but the stacking of Decorators can make your code hard to read. Combine the Builder and Decorator patterns for more synergy.

By 
Nehme Bilal user avatar
Nehme Bilal
·
Dec. 20, 16 · Tutorial
Likes (27)
Comment
Save
Tweet
Share
17.5K Views

Join the DZone community and get the full member experience.

Join For Free

In my previous article, Is Inheritance Dead?, I discussed the advantages of using the decorator pattern over inheritance. While most readers agree that the decorator pattern (and composition in general) has many advantages over inheritance, composing decorators came up several times as one of the main pain-points of using the decorator pattern. In this short article, I will present a simple approach to overcome this difficulty.

Composing Decorators

Recall that the decorator pattern is designed in a way that multiple decorators can be stacked on top of each other, each adding a new functionality (see Is Inheritance Dead? for more details). Stacking decorators and choosing their order (referred to as composing decorators) can be difficult to implement and can lead to code that is difficult to read and maintain.

To illustrate, let’s consider the email service example discussed in Is Inheritance Dead? We can create an instance of IEmailService that is thread-safe, implements retries, has a cache and has verbose logging by composing decorators as follows:

IEmailService emailService = new EmailServiceCacheDecorator(
  new EmailServiceLoggingDecorator(new EmailServiceRetryDecorator(
    new EmailServiceLoggingDecorator(new EmailServiceThreadSafetyDecorator(
  new EmailService())))));

As you can see, the resulting code can be a bit overwhelming. In addition, applying decorators in the wrong order can lead to incorrect behavior.

In contrast to a hierarchy issue, such as the exploding class hierarchy issue discussed in my previous post, the difficulty of composing decorators can be easily overcome by organizing the code a little bit differently.

The Decorator Builder

The decorator builder is not a new pattern, it's just a way of using the builder pattern to make composing decorators easier. This is particularly useful for public API's where simplicity is crucial.

The code below illustrates a decorator builder for the email service API discussed in the previous section:

public class EmailServiceBuilder
{
    private IEmailService emailService = new EmailService();

    public EmailServiceBuilder synchronize() {
        emailService = new EmailServiceThreadSafetyDecorator(emailService);
        return this;
    }

    public EmailServiceBuilder retry() {
        emailService = new EmailServiceRetryDecorator(emailService);
        return this;
    }

    public EmailServiceBuilder log() {
        emailService = new EmailServiceLoggingDecorator(emailService);
        return this;
    }

    public EmailServiceBuilder cache() {
        emailService = new EmailServiceCacheDecorator(emailService);
        return this;
    }

    public IEmailService build() {
        IEmailService es = emailService;

        // Reset the builder so it can be reused
        emailService = new EmailService();

        return es;
    }
}


Using this decorator builder, the same instance of IEmailService discussed above can be created as follows:

IEmailService emailService = new EmailServiceBuilder().synchronize()
  .log().retry().log().cache().build();


Notice that the resulting code is easier to read, is verbose and less error prone than the original code. Furthermore, the order in which the decorators are added to the builder is the same as the order in which the decorators will be executed at runtime. In fact, without the builder, the order was inversed, which can cause confusion.

Conclusion

Composing decorators is not really a drawback of the decorator pattern. In fact, like any other Java code, it can be dramatically simplified by organizing the code a little bit differently (e.g. by using more functions and classes). In this article, we used the builder pattern to simplify composing decorators. As shown, the resulting code is simple and easy to maintain.

Decorator pattern

Opinions expressed by DZone contributors are their own.

Related

  • Code of Shadows: Master Shifu and Po Use Functional Java to Solve the Decorator Pattern Mystery
  • Decorating Microservices
  • Decorator Pattern to Solve Integration Scenarios in Existing Systems

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook