Reshma
Reshma

Reputation: 51

Decorator design pattern with Composite Design pattern

I understand the composite design pattern and the decorator design pattern and their uses. Can we have a case where we have to use them together? How would the class diagram for such a scenario look like?

Will we have the decorator(from Decorator design pattern), leaf nodes, and composite(from Composite design pattern) inherited from the Component? Here is what I am referring to for the decorator pattern:wiki link for decorator pattern and for composite pattern: composite wiki link

Upvotes: 4

Views: 1465

Answers (2)

Christophe
Christophe

Reputation: 73446

Yes, the decorator pattern is often used together with a composite pattern. Here is what GoF says:

Decorator is often used with Composite. When decorators and composites are used together, they will usually have a common parent class. So decorators will have to support the Component interface (...)

The reason is that the composite pattern's goal is to let:

clients treat individual objects and compositions of objects uniformely.

Combining the two allows to add features and responsibilities to composites and leafs with decorators, thus using composition over inheritance. The main benefits are:

  • avoiding the drawbacks of the less flexible inheritance
  • avoiding the drawbacks of repeating implementing the common interface
  • enabling reuse of the same decorator for both composites and leaves, if the goal is to add common responsibilities

The result would look like: enter image description here

Interestingly, the decorator pattern looks graphically similar to the composite, which may feel troubling. GoF explains this feeling:

A decorator can be viewed as a degenerate composite with only one component. However a decorator adds additional responsibilities (...)

Upvotes: 2

Nikolas
Nikolas

Reputation: 44456

The decorator decorates a single object at the same time delegating its methods and hence modifying its behavior. Consider an interface Text with a single method String asString(); an example classes such as TrimmedText, UpperCaseText, and LowerCaseText.

The composite is a container of zero to n objects and just delegates behavior to its children.

These, however, can be combined into something called a composite decorator, which would be ConcatenatedText with respect to the previous examples.

Here is some code that proves the composite class can also act as a decorator and be decorated itself:

interface Text {
    String asString();

    // just a static helper to construct an instance of Text quickly
    static Text of(String text) {
        return () -> text;
    }
}
// 1st decorator
@AllArgsConstructor
static class TrimmedText implements Text {
    Text text;

    @Override
    public String asString() {
        return text.asString().trim();
    }
}

// 2nd decorator
@AllArgsConstructor
static class UpperCaseText implements Text {
    Text text;

    @Override
    public String asString() {
        return text.asString().toUpperCase();
    }
}
// composite decorator
@AllArgsConstructor
static class ConcatenatedText implements Text {
    List<Text> texts;

    public void add(String text) {
        texts.add(Text.of(text));
    }

    @Override
    public String asString() {
        return texts.stream().map(Text::asString).collect(Collectors.joining(", "));
    }
}
@Test
void foo() {
    Text trimmedUpperCaseText = new TrimmedText(new UpperCaseText(Text.of(" a b c ")));
    assertThat(trimmedUpperCaseText.asString(), is("A B C"));

    ConcatenatedText concatenatedText = new ConcatenatedText(new ArrayList<>(List.of(
        new UpperCaseText(Text.of("    a    ")),
        new TrimmedText(Text.of("    b    ")))));

    concatenatedText.add("c");

    Text refinedText = new TrimmedText(concatenatedText);

    assertThat(refinedText.asString(), is("A    , b, c")
}

Upvotes: 5

Related Questions