简体   繁体   中英

Composite Design Pattern Leaf Management

Most of the descriptions of the composite design pattern I have seen have the Composite implement the add() and remove() methods and leave those methods unimplemented in the Leaf objects. For example, the diagram in the Wiki Page on the subject suggests this, which is more or less the same as the GoF diagram.

Regarding implementing these in the parent Component class, the GoF have the following to say:

Defining the child management interface at the root of the class hierarchy gives you transparency, because you can treat all components uniformly. It costs you safety, however, because clients may try to do meaningless things like add and remove objects from leaves.

I agree that having a Leaf implement remove() is strange to handle. (Do you delete yourself? Do you have to implement some sort of NullLeaf object?) But since the point of the pattern is to make Leaf s and Composite s behave the same way I don't see why add() could be implemented in Component .

My question: Why can't Component at least implement add() ? Would doing so violate any key design principles? Does doing so no longer make this a composite design pattern? Below is an example in Python which captures the essence of what I tried to implement in my own work:

class Component:
    def add(self, other):
        # in Python, may be more natural to define __add__
        c = Composite()
        c.children = self.children + other.children
        return c

class Leaf(Component):
    def __init__(self):
        self.children = [self]  # possibly strange?

    def operation(self):
        # operation specific to this leaf

class Composite(Component):
    def __init__(self):
        self.children = []

    def operation(self):
        for child in self.children:
            child.operation()

With an "example usage":

>>> l1 = Leaf()
>>> l2 = Leaf()
>>> c = l1.add(l2)  # c is a `Composite` instance
>>> c.operation()

Why can't Component at least implement add()? Would doing so violate any key design principles? Does doing so no longer make this a composite design pattern?

You answered the question yourself, as you wrote

Defining the child management interface at the root of the class hierarchy gives you transparency, because you can treat all components uniformly. It costs you safety, however, because clients may try to do meaningless things like add and remove objects from leaves.

It is a trade-off to add the child management methods to the interface. When you do it, you gain the benefit of handling all objects in the hierarchy uniformly. But for the leaves you then have to decide a strategy to deal with interface that makes no sense. What could be done is either create empty implementations for the methods or throw exception when they are called. Yet another way to do it is to create an abstract base class Component that defines the add() and remove() methods, and then just use that implementation in Composite and override the methods in Leaf with an empty implementation for example. All these implementations are Composite pattern variants. IMHO the patterns should not be read word to word but as guiding principles which let you tailor the implementation for yourself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM