読者です 読者をやめる 読者になる 読者になる

Factoryパターン(1) - Factory Method

python デザインパターン

Rubyによるデザインパターン』(ラス・オルセン著 ピアソン・エデュケーション刊)の例をPythonに変換して書いています。目次


どのクラスを選択するかは、時に難しい問題になります。いや、もちろん、dictとsetとstringを選ぶのは簡単だと思いますが・・・。

Template Methodパターンで作った、複数のクラスのどれを使うのかは、時に重要な問題です。クラスの選択をパッケージ化するのがFactoryパターンです。

池の生態系シミュレータを作りました。
Pond(池)には、Duck(アヒル)が住んでいて、アヒルは食べたり鳴いたり眠ったりします。

class Pond(object):
    def __init__(self, number_ducks):
        self.ducks = []
        for i in xrange(number_ducks):
            self.ducks.append(Duck("アヒル%d"%(i,)))
      #ここで「Duck」を作っている
    
    def simulate_one_day(self):
        for duck in self.ducks:
            duck.eat()
            duck.speak()
            duck.sleep()

class Duck(object):
    def __init__(self, name):
        self.name = name
    
    def eat(self):
        print("アヒル(%s)は食事をしています。"%(self.name,))
    
    def speak(self):
        print("アヒル(%s)はガーガー鳴いています。"%(self.name,))
    
    def sleep(self):
        print("アヒル(%s)は寝ています。"%(self.name,))

このシミュレータは大変上手くいったので、
Frog(カエル)にも対応しようという事になりました。

しかし、上のコードでは明示的にアヒルを生成しているという問題があります。

そこで、Template Methodパターンを活用しましょう!

class Pond(object):
    def __init__(self, number_animals):
        self.animals = []
        for i in xrange(number_animals):
            animal = self.new_animal("動物%d"%(i,))
            self.animals.append(animal)
    
    def simulate_one_day(self):
        for animal in self.animals:
            animal.eat()
            animal.speak()
            animal.sleep()
    
    def new_animal(self, name):
        raise NotImplementedError
    
class FrogPond(Pond):
    """カエルだらけの池"""
    def new_animal(self, name):
        return Frog(name) 

class DuckPond(Pond):
    """アヒルだらけの池"""
    def new_animal(self, name):
        return Duck(name) 


これをFactory Methodパターンと呼びます。

つまり、Factory MethodはTemplate Method の一変種です。

Factory Methodパターンにおいて、PondのようなクラスをCreator、DuckPond、FrogPondのようなクラスをConcrete Creator と呼びます。

ちなみに、共通のインターフェイスを持たなければならないのは、Pondの側だけではなく、カエルやアヒルの方もそうであることに注意です。