pythonにおける仮想関数と純粋仮想関数

# 仮想関数

### 仮想関数とは？

仮想関数とは、基底クラスの関数を、サブクラス(派生クラス)で、上書き(オーバーライド)できる関数のこと。

### 使いどき

デフォルト処理を設定したい場合などに使用する。
継承先で、当該関数を上書きして使用することで、好きな処理内容に書き換えることもできる。


# 純粋仮想関数

### 純粋仮想関数とは？

純粋仮想関数とは、仮想関数に、実装をもたない(処理を書かない)関数のこと。
仮想関数とは異なり、継承先で、基底クラスで定義した、関数名（引数・戻り値を含む）を”必ず実装”しなければならないことが大きな特徴です。

### 使いどき

最初から実装すれば良いじゃん。と私も思ってました。が、同じような行為や、ある程度同じ処理手順のとき、いちいち実装なんてしていたら、仕事だったら、時間がいくらあっても足りません。

どんなときか？
私は、料理を例にとって、考えていきました。

料理をするには、一連の調理工程(作業手順)があり、最初(材料買い出し)と、最後(片づけ)は同じことをする。とします。

しかし、料理によっては、調理工程は様々ですよね。
オブジェクトとしては、料理という塊は意識したい。
こういったときに利用するといいと思います。

# 具体的な例(設計)

料理を調理(Cook)というクラスとして考えてみましょう。

調理(Cook)クラスの、料理する(DoCook)を呼び出すと、以下の順に処理を行うとします。

1. 材料の買い出し(普通のメンバ関数)
2. 下ごしらえ(仮想関数)
3. 調理工程Ａ(純粋仮想関数)
4. 調理工程Ｂ(純粋仮想関数)
5. 調理工程Ｃ(純粋仮想関数)
6. 皿に盛り付ける(普通のメンバ関数)
7. 片づける(普通のメンバ関数)


つぎに、「調理」Cookクラスを継承した、「ラーメンの調理」RamenCookクラスを作成したとします。
以下が、ラーメンの調理(RamenCook)の処理手順です。

1. 材料の買い出し(普通のメンバ関数)
2. 下ごしらえ(仮想関数)
    →オーバーライド
    - 野菜を切って水に浸す。
3. 調理工程Ａ(純粋仮想関数)
    →継承先で実装
    - 野菜をゆでる。
4. 調理工程Ｂ(純粋仮想関数)
    →継承先で実装
    - お湯を沸かして、麺をゆでる。
5. 調理工程Ｃ(純粋仮想関数)
    →継承先で実装
    - 卵を割って、スープの素を入れる。
6. 皿に盛り付ける(普通のメンバ関数)
7. 片づける(普通のメンバ関数)

# 実装

では、こんどは、実装してみましょう。
設計した結果を、反映すると以下のような実装になります。

```python
################
## Cook.py ##
################
from abc import ABC, abstractmethod

# 調理クラス
class Cook(ABC): 

    # 買い出し(普通のメンバ関数)
    def __shopping(self):
        print("　買い出しをする")
    # 下ごしらえ(仮想関数)
    @abstractmethod
    def _food_preparation(self):
        print("　野菜を切る")
    # 調理工程Ａ(純粋仮想関数)
    @abstractmethod
    def _cooking_process_A(self):
        pass
    # 調理工程Ｂ(純粋仮想関数)
    @abstractmethod
    def _cooking_process_B(self):
        pass
    # 調理工程Ｃ(純粋仮想関数)
    @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
```

```python

################
## Ramen.py ##
################

class RamenCooking(Cook):

    # 下ごしらえ(仮想関数)
    def _food_preparation(self):
        print("　野菜を切って水につける")
    # 調理工程Ａ(純粋仮想関数)
    def _cooking_process_A(self):
        print("　お湯を沸かす")
    # 調理工程Ｂ(純粋仮想関数)
    def _cooking_process_B(self):
        print("　野菜を炒める")
    # 調理工程Ｃ(純粋仮想関数)
    def _cooking_process_C(self):
        print("　麺をゆでる")

```

```python
################
## FriedRice.py ##
################

class FriedRice(Cook):

    # 下ごしらえ(仮想関数)
    def _food_preparation(self):
        print("　ネギ・チャーシューを切る")
    # 調理工程Ａ(純粋仮想関数)
    def _cooking_process_A(self):
        print("　フライパンを温める")
    # 調理工程Ｂ(純粋仮想関数)
    def _cooking_process_B(self):
        print("　溶き卵をフライパンへ入れて、ご飯と混ぜる")
    # 調理工程Ｃ(純粋仮想関数)
    def _cooking_process_C(self):
        print("　ネギ・チャーシューを加えて味付けする")


```

```python
# メインの実行部分
if __name__ == "__main__":
    print("①ラーメンを作る")
    ramen_cooking = RamenCooking()
    ramen_cooking.do_cooking()
    print("")
    print("②チャーハンを作る")
    fired_rice = FriedRice()
    fired_rice.do_cooking()
```

実行結果
``` cmd
①ラーメンを作る
　買い出しをする
　野菜を切って水につける
　お湯を沸かす
　野菜を炒める
　麺をゆでる
　皿にもりつける。

②チャーハンを作る
　買い出しをする
　ネギ・チャーシューを切る
　フライパンを温める
　溶き卵をフライパンへ入れて、ご飯と混ぜる
　ネギ・チャーシューを加えて味付けする
　皿にもりつける。
```

※実際には、importを指定する必要があります。


そして、今回は、ラーメン以外にも、チャーハンクラスを作成し、工程が異なるが
調理という枠の中で、表現をしてみました。

以上。