概要
-
依存関係逆転の原則(DIP)等の場面、つまり、抽象に依存した実装を行いたい場合に、Pythonでそれっぽく実装してみる
- Python3.5から利用可能なTypeHintsの型アノテーションを利用してみる。
- Pythonは言語機能としてInterfaceがないため、
abc
ライブラリを使う。 - 型アノテーションは型が違っていても怒らないので、あくまでそれっぽく書くというだけ。。。
前提
- 環境
- python: v.3.6.8
作ってみるもの
- animalモジュール
-
Animal
というInterfaceクラスと、それを継承したCat
クラスを実装する。 -
Animal
クラスはcry()
methodを持つ。 -
Cat
などの具象クラスでは、cry()
methodにて、それぞれの具象化された動物泣き声をprintする。というポリモーフィズムを説明する際に出てくるヤツです。 -
Dog
クラスも用意するが、このクラスはAnimal
を継承しない。
-
- myclassモジュール
-
Animal
の具象クラスを受け取り、cry()
methodを叩くMyClass
クラスを実装する。ここでMyClass
では-
Cat
クラスに依存しないようにしたい。(具象クラスの修正や、別の具象クラスが渡ってきても影響を受けないようにしたい -
Animal
というInterfaceクラスを継承していることは知っておきたい
-
-
- mainモジュール
-
Cat
やDog
クラスを具象化し、MyClass
のmethodに渡す。
-
animalモジュールを用意
Interfaceクラスを用意
-
abc
ライブラリを使って、継承クラスにてInterfaceクラスを作ってみる
class Animal(object, metaclass=abc.ABCMeta):
"""動物を表すInterfaceクラス。
"""
@abc.abstractmethod
def cry(self):
"""動物の鳴き声のInterface
"""
pass # 具体的な実装はなし
具象クラスを用意
-
Animal
クラスを継承して具象クラスを作ってみる
class Cat(Animal):
"""猫を表すクラス
Args:
Animal: インターフェースクラス
"""
def cry(self):
"""猫の鳴き声の具象method
"""
print('meow')
-
Animal
クラスとCat
クラスの動きを確認してみる-
Cat
クラスをインスタンス化して、cry()
を実行してみる
-
cat = Cat()
cat.cry()
meow
````
- Interfaceクラスをそのままインスタンス化しようとすると怒られる
```shell
>>> animal = Animal()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Animal with abstract methods cry
```
- 具象クラスでmethodを実装しなかった場合も、怒られる
```shell
class Cat(Animal):
... """猫を表すクラス
... Args:
... Animal: インターフェースクラス
... """
... def nocry(self):
... """検証用method
... """
... print('???')
...
cat = Cat()
Traceback (most recent call last):
File "", line 1, in
TypeError: Can't instantiate abstract class Cat with abstract methods cry
```
Interfaceクラスと関係ないクラスを実装してみる
- ぱっと見は似てるが、
Animal
クラスを継承して いないDog
クラスを作ってみる
class Dog(object):
"""犬を表すクラス。インターフェースは継承してない
"""
def cry(self):
"""犬の鳴き声の具象method
"""
print('bow wow')
myclassモジュールを用意
具象クラスを受け取って、cry()
methodを実行するクラスを用意する
- 型アノテーションを使って、引数が
Animal
Interfaceを継承したクラスであることを明示
from animal import Animal
class MyClass(object):
def run(self, animal: Animal) -> None:
animal.cry()
mainモジュールを用意
- 実際の具象クラスをインスタンス化し、処理はMyClassに委譲するロジックを用意
from animal import Cat, Dog
from myclass import MyClass
myclass = MyClass()
cat = Cat()
dog = Dog()
myclass.run(cat)
myclass.run(dog)
- 結果は
$python main.py
meow
bow wow
- やっぱり犬も吠えてるね(´・ω・`)
考察
- 型アノテーションはあくまで注釈なので、Interfaceクラスの派生クラスでなくても、動いてしまう
- とりあえず、それっぽいことはできてる(意味があるかはおいておいて)
- 簡単な実験のメモ程度の情報だけど何かしらの参考になれば幸いです。。。
- 「複数人で実装するときに、Interfaceクラスに注目してね」くらいの意味は与えられるのかな?