Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 429
Reshma Avatar asked Sep 20 '25 21:09

Reshma


1 Answers

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")
}
like image 167
Nikolas Charalambidis Avatar answered Sep 23 '25 11:09

Nikolas Charalambidis