~ It’s about the hierarchy or tree system, that has composite and leaf nodes.

The GoF called the design pattern for our “the sum acts like one of the parts” situation the Composite pattern. We need to use the Composite pattern when you are trying to build a hierarchy or tree of objects, and you do not want the code that uses the tree to constantly have to worry about whether it is dealing with a single object or a whole bushy branch of the tree.

To build the Composite pattern, you need three moving parts.

Screenshot 2023-03-04 at 14.16.34.png

# First: an abstract base class in the sense that it is not really complete: 
# It just keeps track of the name of the task and has a do-nothing get_time_required method.
class Task
  attr_reader :name
  def initialize(name)
    @name = name
	end

  def get_time_required
    0.0
	end 
end

# Second: simple subclasses of Task
class AddDryIngredientsTask < Task
  def initialize
    super('Add dry ingredients')
	end
  
	def get_time_required
    1.0             # 1 minute to add flour and sugar
	end 
end

class MixTask < Task
  def initialize
    super('Mix that batter up!')
	end

  def get_time_required
    3.0             # Mix for 3 minutes
	end 
end

# Third: Define all of the basic cake-baking tasks
class MakeBatterTask < Task
  def initialize
    super('Make batter')
    @sub_tasks = []
    **add_sub_task( AddDryIngredientsTask.new )
    add_sub_task( AddLiquidsTask.new )
    add_sub_task( MixTask.new )**
	end

   def add_sub_task(task)
    @sub_tasks << task
	end
  
	def remove_sub_task(task)
    @sub_tasks.delete(task)
	end
  
	def **get_time_required**
    time=0.0
    @sub_tasks.each {|task| time += task.get_time_required}
    time
	end 
end

An Inconvenient Difference

We can make the composite and leaf objects different. For example, we can supply the composite object with add_child and remove_child methods and simply omit these methods from the leaf class. This approach has certain logic behind it: After all, leaf objects are childless, so they do not need the child management plumbing.

On the other hand, our main goal with the Composite pattern is to make the leaf and composite objects indistinguishable. If the code that uses your composite has to know that only some of the components—the composite ones—have get_child and add_child methods while other components—the leaves—do not, then the leaf and composite objects are not the same. But if you do include the child-handling methods in the leaf object, what happens if someone actually calls them on a leaf object? Responding to remove_child is not so bad—leaf objects do not have children so there is never anything to remove. But what if someone calls add_child on a leaf object? Do you ignore the call? Throw an exception? Neither response is very palatable.

As I say, how you handle this decision is mostly a matter of taste: Make the leaf and composite classes different, or burden the leaf classes with embarrassing methods that they do not know how to handle. My own instinct is to leave the methods off of the leaf classes. Leaf objects cannot handle child objects, and we may as well admit it.

Pointers This Way and That (Pattern from children)

So far, we have looked at the Composite pattern as a strictly top-down affair. Because each composite object holds references to its subcomponents but the child components do NOT know a thing about their parents, it is easy to traverse the tree from the root to the leaves but hard to go the other way.

It is easy to add a parent reference to each participant in the composite so that we can climb back up to the top of the tree.

# Interface: base class
class Task
	attr_accessor :name, **:parent**
	def initialize(name) 
		@name = name 
		**@parent = nil**
	end
end

# Leaf: subclasses
class CompositeTask < Task
  def initialize(name)
		super(name)
    @sub_tasks = []
  end
	def add_sub_task(task) 
		@sub_tasks << task 
		**task.parent = self**
	end

	def remove_sub_task(task) 
		@sub_tasks.delete(task) 
		**task.parent = nil**
	end
end

# With the parent references in place, we can now trace any composite component
# up to its ultimate parent:
**while** task
  puts("task: #{task}")
  **task = task.parent**
end