![Composite](/images/patterns/cards/composite-mini.png?id=a369d98d18b417f255d04568fd0131b8)
Composite を Ruby で
Composite は、 構造に関するデザインパターンの一つで、 オブジェクトを木のような構造に構成し、 あたかも単一のオブジェクトであるかのように扱えるようにします。
Composite は、 ツリー構造の構築を必要とする問題の大部分の解決策として、 かなりの人気を得るようになりました。 Composite の大きな特徴は、 ツリー構造全体でメソッドを再帰的に実行し、 結果をまとめあげることです。
複雑度:
人気度:
使用例: Composite パターンは、 Ruby コードではよく見かけます。 ユーザー・インターフェースのコンポーネントの階層構造の表現や、 グラフに対して動作するコードで多く使われます。
見つけ方: オブジェクト・ツリーがあって、 ツリーのそれぞれのオブジェクトが同じクラス階層の部分であれば、 十中八九、 コンポジットです。 もしこれらのクラス中のメソッドが、 ツリーの子オブジェクトに仕事を委任し、 それを階層の基底クラスやインターフェースを介して行うなら、 これは間違いなくコンポジットです。
概念的な例
この例は、 Composite デザインパターンの構造を説明するためのものです。 以下の質問に答えることを目的としています:
- どういうクラスからできているか?
- それぞれのクラスの役割は?
- パターンの要素同士はどう関係しているのか?
main.rb: 概念的な例
# The base Component class declares common operations for both simple and
# complex objects of a composition.
class Component
# @return [Component]
def parent
@parent
end
# Optionally, the base Component can declare an interface for setting and
# accessing a parent of the component in a tree structure. It can also provide
# some default implementation for these methods.
def parent=(parent)
@parent = parent
end
# In some cases, it would be beneficial to define the child-management
# operations right in the base Component class. This way, you won't need to
# expose any concrete component classes to the client code, even during the
# object tree assembly. The downside is that these methods will be empty for
# the leaf-level components.
def add(component)
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
# @abstract
#
# @param [Component] component
def remove(component)
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
# You can provide a method that lets the client code figure out whether a
# component can bear children.
def composite?
false
end
# The base Component may implement some default behavior or leave it to
# concrete classes (by declaring the method containing the behavior as
# "abstract").
def operation
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# The Leaf class represents the end objects of a composition. A leaf can't have
# any children.
#
# Usually, it's the Leaf objects that do the actual work, whereas Composite
# objects only delegate to their sub-components.
class Leaf < Component
# return [String]
def operation
'Leaf'
end
end
# The Composite class represents the complex components that may have children.
# Usually, the Composite objects delegate the actual work to their children and
# then "sum-up" the result.
class Composite < Component
def initialize
@children = []
end
# A composite object can add or remove other components (both simple or
# complex) to or from its child list.
# @param [Component] component
def add(component)
@children.append(component)
component.parent = self
end
# @param [Component] component
def remove(component)
@children.remove(component)
component.parent = nil
end
# @return [Boolean]
def composite?
true
end
# The Composite executes its primary logic in a particular way. It traverses
# recursively through all its children, collecting and summing their results.
# Since the composite's children pass these calls to their children and so
# forth, the whole object tree is traversed as a result.
def operation
results = []
@children.each { |child| results.append(child.operation) }
"Branch(#{results.join('+')})"
end
end
# The client code works with all of the components via the base interface.
def client_code(component)
puts "RESULT: #{component.operation}"
end
# Thanks to the fact that the child-management operations are declared in the
# base Component class, the client code can work with any component, simple or
# complex, without depending on their concrete classes.
def client_code2(component1, component2)
component1.add(component2) if component1.composite?
print "RESULT: #{component1.operation}"
end
# This way the client code can support the simple leaf components...
simple = Leaf.new
puts 'Client: I\'ve got a simple component:'
client_code(simple)
puts "\n"
# ...as well as the complex composites.
tree = Composite.new
branch1 = Composite.new
branch1.add(Leaf.new)
branch1.add(Leaf.new)
branch2 = Composite.new
branch2.add(Leaf.new)
tree.add(branch1)
tree.add(branch2)
puts 'Client: Now I\'ve got a composite tree:'
client_code(tree)
puts "\n"
puts 'Client: I don\'t need to check the components classes even when managing the tree:'
client_code2(tree, simple)
output.txt: 実行結果
Client: I've got a simple component:
RESULT: Leaf
Client: Now I've got a composite tree:
RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf))
Client: I don't need to check the components classes even when managing the tree:
RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf)+Leaf)