Composite Design Pattern in Go

1. Definition

The Composite Design Pattern provides a mechanism to treat individual objects and compositions of objects uniformly. It allows you to compose objects into tree structures to represent part-whole hierarchies.

2. Problem Statement

Suppose you have a system where you need to work with individual objects and groups of objects in the same way. For example, if you have a graphical system where both shapes and groups of shapes need to be treated as first-class citizens, using individual object structures can make the system complex and hard to manage.

3. Solution

With the Composite pattern, you define a unified component interface that can be used for both individual objects and groups. By doing this, you can treat them uniformly, simplifying client code.

4. Real-World Use Cases

1. Graphical systems where you have shapes and groups of shapes.

2. File systems with files and directories.

3. User interfaces with individual components and groups of components.

5. Implementation Steps

1. Define the component interface with methods that will be implemented by both leaf and composite objects.

2. Implement the leaf objects that don't have any children.

3. Implement composite objects that can have children, either leaf or other composite objects.

4. The client can then treat both leaf and composite objects uniformly.

6. Implementation in Go

package main
import "fmt"
// Step 1: Component interface
type Component interface {
	Operation() string
}
// Step 2: Leaf
type Leaf struct {
	name string
}
func (l *Leaf) Operation() string {
	return l.name
}
// Step 3: Composite
type Composite struct {
	children []Component
	name     string
}
func (c *Composite) Add(child Component) {
	c.children = append(c.children, child)
}
func (c *Composite) Operation() string {
	result := c.name + " contains:\n"
	for _, child := range c.children {
		result += "  - " + child.Operation() + "\n"
	}
	return result
}
// Client code
func main() {
	leaf1 := &Leaf{"Leaf1"}
	leaf2 := &Leaf{"Leaf2"}
	composite := &Composite{name: "Composite1"}
	composite.Add(leaf1)
	composite.Add(leaf2)
	fmt.Println(composite.Operation())
}

Output:

Composite1 contains:
  - Leaf1
  - Leaf2

Explanation:

1. The Component interface has the Operation method which will be used by both leaf and composite objects.

2. The Leaf struct is a simple implementation of the Component.

3. The Composite struct contains a slice of Component allowing it to store both leaf and other composite objects.

4. In the client code, we created two leaves and one composite, then added the leaves to the composite, and finally printed the operation of the composite.

7. When to use?

Use the Composite Design Pattern when:

1. You want to represent part-whole hierarchies of objects.

2. You want clients to ignore the difference between compositions of objects and individual objects. Clients will treat all objects uniformly.


Comments