Python
python3
ポリモーフィズム
abc
抽象クラス

前置き

オブジェクト指向をあまり理解できていないと前から感じていたので、ちょうどKindleの50%セールで買ったオブジェクト指向でなぜつくるのか 第2版を読みました。

そこでオブジェクト指向プログラミングの3大概念であるクラス、ポリモーフィズム、継承についてしっかりと学んだのですが、クラスや継承は知っていてもポリモーフィズムは意味すらしりませんでした。

この記事ではポリモーフィズムという考えかたの簡単な説明(をしているqiita記事の紹介)、どういうメリットがあるか、またPython3を用いてポリモーフィズムに基づいたコードを実際に書いてみようと思います。

そもそもポリモーフィズムとは?

プログラマー1年生がポリモーフィズムについて学んだのでRPGで説明する。
この記事の『ポリモーフィズムってなんだ?』のところにわかりやすい説明が載っています。

なんでこの程度の考え方が3大概念の1つとか言われるのか?

ポリモーフィズムは、言ってしまえば、『異なるクラス間の類似したメソッドの名前を統一しただけ』です。

なぜこの程度のことが3大概念の1つとまで言われているのでしょうか?

プログラミングの入門書においても、オブジェクト指向について説明するときにクラスや継承と違って、ポリモーフィズムの説明はしない本もあります。僕がプログラミングを始めたときに読んだPythonの入門書が実際にそうでした。

ポリモーフィズムが役に立つ具体的な開発場面を想像していきましょう。

あなたはとあるゲームの制作にかかわっています。

このゲームは規模が大きくとても1人ではできるものではないので複数でチームを組んでゲーム制作をしています。(あくまで例です。僕はゲーム制作の経験はないです。)

キャラクターの行動設定をAさん担当していて、攻撃があったときの戦闘処理をあなたが担当していたとします。

ここでは役職が3つあって剣士なら攻撃メソッドがslash、魔術師なら攻撃メソッドがcast、武闘家だったら攻撃メソッドがpunchだったとしましょう。

あなたは、役職ごとに対応するslash、cast、punchメソッドを呼び出して、相手に1ダメージを与えるという処理を書きました。

ここでAさんが新しい役職の銃士を定義して攻撃メソッドをshootとするようにコードを書き加えた場合、あなたもまたshootメソッドが呼び出して処理(相手に1ダメージ与える)が行われるように、自分が書いたコードを修正する必要があります。

この例なら書き直す部分は少ないですが、大規模開発ではコードが膨大なのでコードの書き直しは思わぬ致命的なバグを生むこともあります。

ここでAさんとあなたでポリモーフィズムの考え方に基づいて、役職にかかわらず攻撃メソッドはattackと名付け、あなたは攻撃の際にattackメソッドを呼び出して戦闘処理をするようにコードを書いておけば、Aさんが新しい役職を付け加えても、あなたはコードを書き直す必要がないため、効率性の面でもセキュリティの面でも良いことばかりということなのです。

実際に書いてみよう Python3編

今回は、Python3の組み込みライブラリであるabcライブラリを使ってポリモーフィズムの考え方に基づいたクラス定義をしてみようと思います。

abcライブラリは、抽象クラスを定義するためのライブラリです。(おそらくABstract Classからabc)

抽象クラスについての説明は、ここではしません。
Javaですが、【納得Java】抽象クラス(abstract)を使うメリット にわかりやすく説明が載っています。

それでは、ポリモーフィズムの考え方に基づいてクラス定義をしましょう。

sample.py
from abc import ABCMeta, abstractmethod

#元となる人間クラス
class Human(metaclass = ABCMeta):

    #抽象メソッド
    @abstractmethod
    def attack(self):
        pass

    #共通メソッド
    def defend(self):
        return "防御"

#受け継ぎ先のクラス
class Saber(Human):
    def attack(self):
        return "斬撃攻撃"

class Archer(Human):
    def attack(self):
        return "射撃"

class Caster(Human):
    def attack(self):
        return "魔術攻撃"

抽象クラス(サンプルコードではHumanクラス)を定義するにはインポートしたABCMetaを継承します。

また抽象メソッドを定義するには、デコレーターとしてインポートしたabstractmethodでメソッドを修飾してあげます。

今回は上の例にならってメソッド名をattackにします。

そしてその後の具体的な役職でこのHumanクラスをインポートしてattackメソッドをオーバーライドしています。

こうすることで役職ごとの攻撃メソッドをattackで統一することができます。

ここで抽象クラスをわざわざ継承しなくても、個別に役職クラスを定義して攻撃メソッドの名前をattackにすればいいのでは?
と思うかもしれません。

しかし、サンプルコードと違って実際の開発場面ではHumanクラスが役職クラスたちに共通のインスタンス変数やメソッド(サンプルコードではdefendメソッド)をたくさん持っていることが多いので、Humanクラスでこの共通変数・メソッドを定義してから具体的な役職でHumanクラスを継承することで役職ごとに共通変数・メソッドを繰り返し定義する手間が省けるのです。

まとめ

以上でポリモーフィズムの簡単な説明を終えます。僕自身も勉強したばかりなので間違っているところがあるかもしれませんが、そこはおおめにみてくだしあ