Composite Pattern

실리콘·2023년 2월 1일
0

Studying GoF's Design Patterns(1994) book

Composite pattern

Intent

Using tree structure to represent part-whole hiearchies. Let's clients treat both individual objects and compositions of objects uniformly.

Motivation

The key is an abstract class that can represent both primitives and containers that contain a composition of these containers.

Diagram from textbook

So Graphic is the abstract class. And Line, Rectangle, Text are derived primitive subclasses. Then Picture Class is a container(or aggregate), composed of these primitive classes. Therefore it has child managing feature like Add(), Remove(), GetChild(). Since Picture Class also conforms to Graphic Abstract class, its draw() can tell its child to call draw() recursively. Note that Picture class can also be a child to another Picture class.

A more general diagram from book

Participants

Component - the abstract class
Leaf - primitives
Composite - aggregate of primitives + child related functionality
Client - uses objects in composition, via the component interface.

Consequences

Defines hiearchy where primitive objects can be composed into more complex objects recursively.
Client code is simpler. Client code can treat Composite and Leaf as same. No need for tag-and-case-statement-style functions
Make it easier to add new components, using the Component abstract class.
Can make your design overly general. As downside of making adding new components easier, the abstract class can be too general to enforce constraints. May need to do runtime checks instead of using type system at compile time.

Implementation

Explicit parent reference. Usually in the Component abstract class. Make sure to have the invariant, where parent has reference to child and child has reference to parent. reference should go both ways, like doubly-linked list. Easiest way is to change a component's parent only when it is added or removed from composite.

Sharing components. You can make it so there can only be one parent. But to share component, a child can have more than one parent. But this can lead to ambiguities. Flyweight Pattern shows how to rework a design to avoid having reference to parents altogether, by externalizing some or all of their state.

Maximizing the Component interface. good, but sometimes conflicts with principle of class hiearchy design that says a class should only define operations that are meaningful to its subclasses.

Declaring the child management operations. You can define them in components, or define them in composites. tradeoff vs safety tradeoff.
or you could try to do GetComposite() check in component abstract class, that defaults to a null pointer. If it returns a composite object, you can do child management operations. Else, no. Usually it’s better to make Add and Remove fail by default (perhaps by raising an exception) if the component isn’t allowed to have children or if the argument of Remove isn’t a child of the component, respectively.

Should component implement a list of components? No. Only if there are very few children in the structure.

Child ordering. If needed, but design carefully. check Iterator pattern.

Caching to improve performance. Maybe. But when component changes, it needto invalidate cache of parents. I think modern interpreters/processors or sth handles this well already?

Who should delete components? Most languages have garbage collection nowdays. just remove reference to it. no need to free() memory or anything.

What's the best DS for storing components? Depends. check Interpreter pattern for example.

profile
software engineer

0개의 댓글