「オブジェクト作成時に、作成するオブジェクトのクラスをサブクラスに選ばせる」パターンです。
牛(Cow)を作る工場と鶏(Chicken)を作る工場があるとします。
出荷された動物のチェックとして餌を与え、鳴き声のチェックを行います。
(各animalに対し eat()
と speak()
をさせる)
(「農場で育てる」の方が正しいですが、説明のために工場で作っていることにします)
import abc
def main():
cow = CowFactory()
cow.check_animal()
chiken = ChickenFactory()
chiken.check_animal()
# Creator
class Factory:
def __init__(self):
self.animal = self.factory_method()
# template_method
def check_animal(self):
self.animal.eat()
self.animal.speak()
@abc.abstractmethod
def factory_method(self):
pass
# Product
class Animal:
@abc.abstractmethod
def eat(self):
pass
@abc.abstractmethod
def speak(self):
pass
# ConcreteCreator
class CowFactory(Factory):
def factory_method(self):
return Cow()
# ConcreteCreator
class ChickenFactory(Factory):
def factory_method(self):
return Chicken()
# ConcreteProduct
class Cow(Animal):
def eat(self):
print("Cow:eat")
def speak(self):
print("Cow:speak")
# ConcreteProduct
class Chicken(Animal):
def eat(self):
print("Chiken:eat")
def speak(self):
print("Chiken:speak")
if __name__ == "__main__":
main()
大事なのは以下の部分です。
class Factory:
def __init__(self):
self.animal = self.factory_method()
# check_animal関数
@abc.abstractmethod
def factory_method(self):
pass
self.animal
に対し、self.factory_method()
関数で作成したものを代入していますが、
self.factory_method()
自体は抽象メソッドであり、子クラスにオブジェクトの生成を選択させています。
たとえば、以下のようなコードでも対応できます。
class Factory:
def __init__(self, animal_name):
if animal_name == "cow":
self.animal = Cow()
elif animal_name == "chicken":
self.animal = Chicken()
# あと同じ
しかし、このように書くと、牛、鶏以外に新しいクラスを追加する時に、
if文も書き換える必要が出てきてしまいます。
なお、今回の例のように、簡単なコードだったらif文で対応してもよいと思うので、
その辺は変更の起きやすさ、コードの複雑さと相談でしょう。
2つ省略できるポイントがあります。
① Pythonは動的型付け言語なため、Animalクラスは省略することのほうが多いです。
②「Pythonはクラスが第一級オブジェクトである」が使えます。
第一級オブジェクトとは簡単にいうと、「変数に入れたりすることができる」と思っていてOKです。
(詳細については上記リンク先に記載しています。)
簡単な例を示します。
class Hoge:
def test(self):
print("hoge")
hoge_class_copied = Hoge
hoge_instance = hoge_class_copied()
hoge_instance.test() # => hoge
以上のように、Hogeを一度変数に入れても使用することができます。
この特性を使うと、factory_method
が不要になります。
以下にサンプルコードを示します。
def main():
cow_factory = Factory(Cow)
cow_factory.check_animal()
chiken_factroy = Factory(Chicken)
chiken_factroy.check_animal()
class Factory:
def __init__(self, animal_class):
self.animal = animal_class()
def check_animal(self):
self.animal.eat()
self.animal.speak()
# * Animalクラスを継承しなくなった
# クラスの中身は1コ目のサンプルコードと同じであるため省略
class Cow:
pass
# * Animalクラスを継承しなくなった
# クラスの中身は1コ目のサンプルコードと同じであるため省略
class Chicken:
pass
if __name__ == "__main__":
main()
出力結果は1コ目のサンプルコードと全く同じです。
一番大事なところは self.animal = animal_class()
の1行で、ここでクラスを指定することで
「オブジェクト作成時に、作成するオブジェクトのクラスをサブクラスに選ばせる」ことを実現しています。