仮想関数
仮想関数とは?
仮想関数とは、基底クラスの関数を、サブクラス(派生クラス)で、上書き(オーバーライド)できる関数のこと。
使いどき
デフォルト処理を設定したい場合などに使用する。
継承先で、当該関数を上書きして使用することで、好きな処理内容に書き換えることもできる。
純粋仮想関数
純粋仮想関数とは?
純粋仮想関数とは、仮想関数に、実装をもたない(処理を書かない)関数のこと。
仮想関数とは異なり、継承先で、基底クラスで定義した、関数名(引数・戻り値を含む)を”必ず実装”しなければならないことが大きな特徴です。
使いどき
最初から実装すれば良いじゃん。と私も思ってました。が、同じような行為や、ある程度同じ処理手順のとき、いちいち実装なんてしていたら、仕事だったら、時間がいくらあっても足りません。
どんなときか?
私は、料理を例にとって、考えていきました。
料理をするには、一連の調理工程(作業手順)があり、最初(材料買い出し)と、最後(片づけ)は同じことをする。とします。
しかし、料理によっては、調理工程は様々ですよね。
オブジェクトとしては、料理という塊は意識したい。
こういったときに利用するといいと思います。
具体的な例(設計)
料理を調理(Cook)というクラスとして考えてみましょう。
調理(Cook)クラスの、料理する(DoCook)を呼び出すと、以下の順に処理を行うとします。
- 材料の買い出し(普通のメンバ関数)
- 下ごしらえ(仮想関数)
- 調理工程A(純粋仮想関数)
- 調理工程B(純粋仮想関数)
- 調理工程C(純粋仮想関数)
- 皿に盛り付ける(普通のメンバ関数)
- 片づける(普通のメンバ関数)
つぎに、「調理」Cookクラスを継承した、「ラーメンの調理」RamenCookクラスを作成したとします。
以下が、ラーメンの調理(RamenCook)の処理手順です。
- 材料の買い出し(普通のメンバ関数)
- 下ごしらえ(仮想関数)
→オーバーライド- 野菜を切って水に浸す。
- 調理工程A(純粋仮想関数)
→継承先で実装- 野菜をゆでる。
- 調理工程B(純粋仮想関数)
→継承先で実装- お湯を沸かして、麺をゆでる。
- 調理工程C(純粋仮想関数)
→継承先で実装- 卵を割って、スープの素を入れる。
- 皿に盛り付ける(普通のメンバ関数)
- 片づける(普通のメンバ関数)
実装
では、こんどは、実装してみましょう。
設計した結果を、反映すると以下のような実装になります。
################
## Cook.py ##
################
from abc import ABC, abstractmethod
# 調理クラス
class Cook(ABC):
# 買い出し(普通のメンバ関数)
def __shopping(self):
print(" 買い出しをする")
# 下ごしらえ(仮想関数)
@abstractmethod
def _food_preparation(self):
print(" 野菜を切る")
# 調理工程A(純粋仮想関数)
@abstractmethod
def _cooking_process_A(self):
pass
# 調理工程B(純粋仮想関数)
@abstractmethod
def _cooking_process_B(self):
pass
# 調理工程C(純粋仮想関数)
@abstractmethod
def _cooking_process_C(self):
pass
# 皿に盛り付ける(普通のメンバ関数)
def __cleaning_up(self):
print(" 皿にもりつける。")
# 調理
def do_cooking(self):
self.__shopping()
self._food_preparation()
self._cooking_process_A()
self._cooking_process_B()
self._cooking_process_C()
self.__cleaning_up()
return True
################
## Ramen.py ##
################
class RamenCooking(Cook):
# 下ごしらえ(仮想関数)
def _food_preparation(self):
print(" 野菜を切って水につける")
# 調理工程A(純粋仮想関数)
def _cooking_process_A(self):
print(" お湯を沸かす")
# 調理工程B(純粋仮想関数)
def _cooking_process_B(self):
print(" 野菜を炒める")
# 調理工程C(純粋仮想関数)
def _cooking_process_C(self):
print(" 麺をゆでる")
################
## FriedRice.py ##
################
class FriedRice(Cook):
# 下ごしらえ(仮想関数)
def _food_preparation(self):
print(" ネギ・チャーシューを切る")
# 調理工程A(純粋仮想関数)
def _cooking_process_A(self):
print(" フライパンを温める")
# 調理工程B(純粋仮想関数)
def _cooking_process_B(self):
print(" 溶き卵をフライパンへ入れて、ご飯と混ぜる")
# 調理工程C(純粋仮想関数)
def _cooking_process_C(self):
print(" ネギ・チャーシューを加えて味付けする")
# メインの実行部分
if __name__ == "__main__":
print("①ラーメンを作る")
ramen_cooking = RamenCooking()
ramen_cooking.do_cooking()
print("")
print("②チャーハンを作る")
fired_rice = FriedRice()
fired_rice.do_cooking()
実行結果
①ラーメンを作る
買い出しをする
野菜を切って水につける
お湯を沸かす
野菜を炒める
麺をゆでる
皿にもりつける。
②チャーハンを作る
買い出しをする
ネギ・チャーシューを切る
フライパンを温める
溶き卵をフライパンへ入れて、ご飯と混ぜる
ネギ・チャーシューを加えて味付けする
皿にもりつける。
※実際には、importを指定する必要があります。
そして、今回は、ラーメン以外にも、チャーハンクラスを作成し、工程が異なるが
調理という枠の中で、表現をしてみました。
以上。