オブジェクト指向の利点が、コード例でなんとなくわかる記事

# 記事を書いた背景
オブジェクト指向(OOP)は、実際のコード例を見ると、その利点が分かりやすくなるのではないかと思い、記事にしてみました。
# コード例（非オブジェクト指向）
今回はPythonで、簡単な銀行口座の機能を作ってみます。
機能は、残高確認、預け入れ、引き出し、です。
まずは、非オブジェクト指向から
```non_oop.py
account_number_A=1234
password_A=12340000
zandaka_A=account_number_A*password_A

account_number_B=2345
password_B=23450000
zandaka_B=account_number_B*password_B

#すべてのアカウントの、ナンバー、パスワード、残高が入っている辞書のリスト
private_list=[{'account_number':account_number_A,'password':password_A,'zandaka':zandaka_A},{'account_number':account_number_B,'password':password_B,'zandaka':zandaka_B}]


def account_is_current(account_number, password):
    #private_listから、account_numberとpasswordが一致している要素があればTrueを返す
    for account in private_list:
        if account['account_number'] == account_number and account['password'] == password:
            return True
    return False

def bank_zandaka(account_number,password):
        for item in private_list:
            if item['account_number'] == account_number :
                print('あなたの預金残高は、'+str(item['zandaka'])+'円です')
                return True
        return False

def bank_hikidashi(account_number, password, price):
    for item in private_list:
        if item['account_number'] == account_number and account_is_current(account_number, password):
            # 残高が、引き出し額より多いか確認
            if item['zandaka'] >= price:
                item['zandaka'] -= price
                return True
            else:
                return False
    return False


def bank_azukeire(account_number, password, price):
    for item in private_list:
        if item['account_number'] == account_number and account_is_current(account_number, password):
            item['zandaka'] += price
            return True
    return False


def zandaka_kakunin(account_number, password):
    if bank_zandaka(account_number,password):
        pass
    else:
        print("不正なログインです")


def hikidashi(account_number, password, price):
    if bank_hikidashi(account_number, password, price):
        print("引き出し成功")
    else:
        print("引き出し失敗：残高不足または不正なログインです")


def azukeire(account_number, password, price):
    if bank_azukeire(account_number, password, price):
        print("預け入れ成功")
    else:
        print("預け入れ失敗：不正なログインです")

```
実際にこの機能を使う、`runtime.py`はこちら
```runtime.py
# 非オブジェクト指向
from non_oop import *

azukeire(1234,12340000,100)##預け入れ成功
hikidashi(1234,12340000,100)##引き出し成功
azukeire(2345,23450000,1000)##預け入れ成功
hikidashi(2345,23450000,2000)##引き出し成功
hikidashi(123,12300,200)##引き出し失敗：残高不足または不正なログインです
zandaka_kakunin(1234,12340000)##あなたの口座残高は、15227560000円です
zandaka_kakunin(2345,23450000)##あなたの口座残高は、54990249000円です
```
`runtime.py`の中で、azukeire()、hikidashi()、いずれの関数を使うにせよ、いちいちナンバーとパスワードを引数に入れなければならず、不便です。
`non_oop.py`についても、コードの初めの方に、それぞれのアカウントのナンバー、パスワード、残高をすべて定義しているため、コードが無駄に長く、読みにくくなっています。また、関数が同じようにいくつも配置されているため、それぞれの関数の役割が、分かりにくくなっています。
# コード例（オブジェクト指向）
先ほどの例での問題点を改善すべく、クラスを用いて以下のように記述することができます。
```oop.py
class Bank:
    #インスタンス生成時に実行される
    def __init__(self,*account_numbers) -> None:
        self.private_list=[]
        # アカウント情報の複数作成
        for account_number in account_numbers:
            password_i=int(str(account_number)+'0000')
            zandaka_i=account_number*password_i
            dict_i={'account_number':account_number,'password':password_i,'zandaka':zandaka_i}
            #private_listにアカウントごとの辞書型データを追加
            self.private_list.append(dict_i)


    def account_is_current(self,account_number, password):
        for account in self.private_list:
            if account['account_number'] == account_number and account['password'] == password:
                return True
        return False
    

    def bank_zandaka(self,account_number,password):
        for item in self.private_list:
            if item['account_number'] == account_number :
                print('あなたの預金残高は、'+str(item['zandaka'])+'円です')
                return True
        return False


    def bank_hikidashi(self,account_number, password, price):
        for item in self.private_list:
            if item['account_number'] == account_number :
                # 残高が、引き出し額より多いか確認
                if item['zandaka'] >= price:
                    item['zandaka'] -= price
                    return True
                else:
                    return False
        return False


    def bank_azukeire(self,account_number, password, price):
        for item in self.private_list:
            if item['account_number'] == account_number :
                item['zandaka'] += price
                return True
        return False

#2つのアカウント情報を作成したbank_insを作る(インスタンス生成)
bank_ins=Bank(1234,2345)


class Account:
    #インスタンス生成時に実行される
    def __init__(self,account_number,password) :
        #アカウントが存在しているか確認する
        if bank_ins.account_is_current(account_number,password):
            #アカウント情報定義
            self.account_number=account_number
            self.password=password
            print('アカウントは正常に作成されました')
        #存在していなければ、処理を中止
        else:
            print("不正なログインです")
            #処理を中止
            exit()

    def zandaka_kakunin(self):
        if bank_ins.bank_zandaka(self.account_number,self.password):
            pass
        else:
            print("失敗しました")

    def hikidashi(self,price):
        if bank_ins.bank_hikidashi(self.account_number,self.password, price):
            print("引き出し成功")
        else:
            print("引き出しに失敗しました。")

    def azukeire(self, price):
        if bank_ins.bank_azukeire(self.account_number,self.password, price):
            print("預け入れ成功")
        else:
            print("預け入れに失敗しました")
```
そして、`runtime.py`がこちら
```runtime.py
# オブジェクト指向
from oop import *

account_A=Account(1234,12340000)##アカウントは正常に作成されました
account_B=Account(2345,23450000)##アカウントは正常に作成されました
account_A.azukeire(100)##預け入れ成功
account_A.hikidashi(100)##引き出し成功
account_B.azukeire(1000)##預け入れ成功
account_B.hikidashi(2000)##引き出し成功
account_A.zandaka_kakunin()##あなたの口座残高は、15227560000円です
account_B.zandaka_kakunin()##あなたの口座残高は、54990249000円です
account_C=Account(123,12300)##不正なログインです
```
`runtime.py`では、いちいちアカウント情報を引数に入れる必要がなくなり、より直感的な記述ができるようになっています。また、アカウントのインスタンス生成時に、アカウントが存在しているかどうかを確認できる、という利点が生まれています。
`oop.py`では、関連するメソッド（関数）を同じクラス内に記述することで、それぞれの関数の役割が、より分かりやすくなっています。
Bankクラス内で、アカウント情報の作成をしているため、`non_oop.py`では最初に記述されていた情報が、すべてBankクラス内で収まることになり、コードが読みやすくなりました。アカウント情報を変えたいときは、bank_ins=Bank(1234,2345,123)などのように、一行変更するだけで良くなったことも、大きな利点です。
# コード例を通して
`oop.py`は、一貫して、関連する変数、メソッドを、同じクラス内に記述する、といったことがなされています。

# 最後に
オブジェクト指向という概念は、まだまだ奥が深く、本来は、解説すべきことがまだまだ数多くあるようです。
私もオブジェクト指向を完全に理解できているとは言えず、このようなざっくりとした解説になってしまいましたが、少しでも利点が伝わる記事になっていれば幸いです。