アブストラクトファクトリ(Abstract Factory) | Ruby デザインパターン


アブストラクトファクトリは、矛盾のないオブジェクトの生成を行うためのパターンです。
このアブストラクトファクトリをRubyコードで紹介します。

👽 ソースコードを使ったAbstract Factoryの説明

Abstract Factoryをソースコードを使って説明します。
ここでは次のような池をサンプルとして取り上げます。

  • 動物を表すクラス:

    • アヒルを表すDuckクラスは、食事(eat)メソッドを持っている
    • カエルを表すFrogクラスは、食事(eat)メソッドを持っている
  • 植物を表すクラス:

    • 藻を表すAlgaeクラスは、成長(grow)メソッドを持っている
    • スイレンを表すWaterLilyクラスは、成長(grow)メソッドを持っている

池の生態系を生成するクラス:

  • コンストラクタで動物と植物を定義する
  • 動物、植物のオブジェクトを返すメソッドを持っている
  • 池の環境(動物と植物の組み合わせ)は次の2種類のみが許されている
    • DuckとWaterLily
    • FrogとAlgae

🐝 サンプルコード

アヒル(Duckクラス)とカエル(Frogクラス)は次のようになります。

# アヒル
class Duck
def initialize(name)
@name = name
end
# 食べる(eat)
def eat
puts "アヒル #{@name} は食事中です"
end
end
# カエル
class Frog
def initialize(name)
@name = name
end
# 食べる(eat)
def eat
puts "カエル #{@name} は食事中です"
end
end

一方、藻(Algaeクラス)とスイレン(WaterLilyクラス)のは次のようになります。

# 植物/藻
class Algae
def initialize(name)
@name = name
end
def grow
puts "藻 #{@name} は成長中です"
end
end
# 植物/スイレン
class WaterLily
def initialize(name)
@name = name
end
def grow
puts "スイレン #{@name} は成長中です"
end
end

次に池を作成する前に、「池の環境の制約」について考えます。

  • 池の環境(動物と植物の組み合わせ)は2種類のみが許されている
    • アヒル(Duckクラス)とスイレン(WaterLilyクラス)
    • カエル(Frogクラス)と藻(Algaeクラス)

この池の環境の制約を守ること、言い換えると矛盾のないオブジェクトの組み合わせを作るのが「Abstract Factoryパターン」です。
今回はこの矛盾のない環境の作成を次の2つのクラスに担当してもらいます。

  • カエル(Frog)と藻(Algae)の生成を行う => FrogAndAlgaeFactory
  • アヒル(Duck)とスイレン(WaterLily)の生成を行う => DuckAndWaterLilyFactory

さらに上の2つのクラスのベースとなる池の生態系を表すクラスOrganismFactoryを作り、上記のクラスが継承するようにします。

ということで、ソースコードはこちら。

# 池の生態系を作る (Abstract Factory)
class OrganismFactory
def initialize(number_animals, number_plants)
@animals = []
# 池の動物を定義する
number_animals.times do |i|
animal = new_animal("動物 #{i}")
@animals << animal
end
@plants = []
# 池の植物を定義する
number_plants.times do |i|
plant = new_plant("植物 #{i}")
@plants << plant
end
end
# 植物についてのオブジェクトを返す
def get_plants
@plants
end
# 動物についてのオブジェクトを返す
def get_animals
@animals
end
end
# カエル(Frog)と藻(Algae)の生成を行う (Concrete Factory)
class FrogAndAlgaeFactory < OrganismFactory
private
def new_animal(name)
Frog.new(name)
end
def new_plant(name)
Algae.new(name)
end
end
# アヒル(Duck)とスイレン(WaterLily)の生成を行う(Concrete Factory)
class DuckAndWaterLilyFactory < OrganismFactory
private
def new_animal(name)
Duck.new(name)
end
def new_plant(name)
WaterLily.new(name)
end
end

😀 実行結果

プログラムを実行した結果は次のとおりです。

factory = FrogAndAlgaeFactory.new(4,1)
animals = factory.get_animals
animals.each { |animal| animal.eat }
#=> カエル 動物 0 は食事中です
#=> カエル 動物 1 は食事中です
#=> カエル 動物 2 は食事中です
#=> カエル 動物 3 は食事中です
plants = factory.get_plants
plants.each { |plant| plant.grow }
#=> 藻 植物 0 は成長中です
factory = DuckAndWaterLilyFactory.new(3,2)
animals = factory.get_animals
animals.each { |animal| animal.eat }
#=> アヒル 動物 0 は食事中です
#=> アヒル 動物 1 は食事中です
#=> アヒル 動物 2 は食事中です
plants = factory.get_plants
plants.each { |plant| plant.grow }
#=> スイレン 植物 0 は成長中です
#=> スイレン 植物 1 は成長中です

矛盾のない組み合わせて、オブジェクトを生成できたことがわかります。

🍄 Abstract Factoryの構成

Abstract Factoryは次の3つの要素で構成されています。

  • AbstractFactory:ConcreteFactoryの共通部分の処理を行う
  • ConcreteFactory:実際にオブジェクトの生成を行う
  • Product:ConcreteFactoryによって生成される側のオブジェクト

🎂 アブストラクトファクトリのメリットは?

  • 関連し合うオブジェクトの集まりを生成できる
  • 整合性が必要となるオブジェクト群を誤りなしに生成できる

🗻 GitHubリポジトリ

🐯 参考リンク

📚 おすすめの書籍