【Python】変なクラス


:::note info
この事例は、実際の事例をもとにして簡略化したものです。
（ので、なんかイマイチなところがあるかもしれません。）
:::

見てもらいたいクラス、ですか……？
これは……。
お菓子屋さんの、商品を管理するクラスですね。

Excelからデータを読み込んで、DBに登録する。
なるほど……。

### お菓子リストをExcelから読み込んで、DBに登録するクラス

```python
import pandas as pd

class SweetsLoader:
    def __init__(self, collection_name,file_name, is_frozen):
        self.collection_name = collection_name
        self.file_name = file_name
        self.is_frozen = is_frozen
        self.data = None

    def load_data(self):
        try:
            self.data = pd.read_excel(self.file_name)
        	# DB登録（省略）
        except Exception as e:
            print(f"Error loading file {self.file_name}: {e}")

```

### Excelから読み込んだお菓子リストを使用するクラス

```python
class SweetsSearcher:
    def search(self,collection_name, is_frozen, sweets_list):
        if is_frozen:
            result = get_db(collection_name) # 冷凍庫用DBからリストを取得する（省略）
        else:
            result = get_db(collection_name)... # 通常の棚のDBからリストを取得する（省略）

        return result
```

そうか、ＤＢは二つあって、常温と冷凍用のものがあるんですね。

初期化と、使用するところで、別々のファイルに分かれてますね。

SweetsSearcherのインスタンスが、二つ以上、別々のところで宣言されている？

あ、でも、アクセス先はＤＢだから、取得自体は正しく済むんですね。
でも、たしかに変です。
どうしてそんなことになってしまったんでしょうか。

# 静的インスタンスにしてみる

あれ？
このSweetsSearcher、selfが一度も使われていない……。

これって、何か意味があるんですか？

そうですか……。
コードを引き継いだだけなので、わからない。

うーん。

例えば、なんですけど、selfを一回も使ってないなら、静的メソッドでいいんじゃないでしょうか？

そうしたら、インスタンスをどこで作るのかも気にしなくてよくなります。

##  Excelから読み込んだお菓子リストを使用するクラス(静的メソッド化)

```python

class SweetsSearcher:
    @staticmethod
    def search(collection_name, is_frozen, sweets_list):
        if is_frozen:
            result = get_db(collection_name) # 冷凍庫用DBからリストを取得する（省略）
        else:
            result = get_db(collection_name) # 通常の棚のDBからリストを取得する（省略）

        return result

```

はい。
はい、そうですか。
あのクラスは、静的クラスになって、ちゃんと動いているんですね。
ほっとしました。

でもなんか、コレ、使いにくいな……。
すみません、やっぱり、もう少し、コードをお借りしてもよろしいですか？

（栗原さんに聞いてみよう……。）

## 栗原さんのリファクタリング

栗原さん：このクラスは……やっぱり変ですね。

栗原さん：単純な計算クラスでもない限り、インスタンスがいらないというのは変じゃないかと思います。
たとえば、`collection_name`とか、そういうのはクラスが持つべきだと思います。

栗原さん：どうして初期化と使用部分が別のファイルなんでしょうか？

ええっと、もともと、作りが肥大化したデモアプリがあって、そちらの実装から切り出した影響みたいですね。

栗原さん：たしかにファイルを分割すると再利用性は増しますが、過度な分割はよくないです。
結果として悪いコードになってしまっているように感じます。

思い切って、クラスはまとめましょう。

## ひとつのおやつ管理クラスになった
```python
import pandas as pd

class SweetsManager:
    def __init__(self, collection_name, file_name, is_frozen):
        self.collection_name = collection_name
        self.file_name = file_name
        self.is_frozen = is_frozen
        self.data = None

    def load_data(self):
        try:
            self.data = pd.read_excel(self.file_name)
            # DB登録処理（省略）
            print(f"Data loaded from {self.file_name} and registered to {self.collection_name} DB.")
        except Exception as e:
            print(f"Error loading file {self.file_name}: {e}")

    def search(self):
        try:
            if self.is_frozen:
                result = get_db(self.collection_name)  # 冷凍庫用DBからリストを取得する（省略）
            else:
                result = get_db(self.collection_name)  # 通常の棚のDBからリストを取得する（省略）

            return result
        except Exception as e:
            print(f"Error searching in {self.collection_name} DB: {e}")
            return None
```

# is_frozen

栗原さん：このis_frozenですが……。

栗原さん：sweets_listは「常温」と「冷蔵」の二つしかないわけですよね。
file_nameが分かれば、is_frozenは外から与えなくてもわかります。

栗原さん：この場合は、プロパティを使うといいですね。

プロパ、ティ？

栗原さん：はい。プロパティを用意して、collection_nameから判断します。
たとえば、「冷凍庫棚」ならフラグを立ててあげます。

```python

class SweetsManager:
    def __init__(self, collection_name, file_name):
        self.collection_name = collection_name
        self.file_name = file_name
        self.data = None

    @property
    def is_frozen(self):
        return self.collection_name == '冷凍庫棚'
```

栗原さん：この場合は、getだけでいいですね。
これで少しはすっきりするのではないかと。

## 細かいアドバイス

栗原さん：細かいことですが、引数はもう少し数が増えそうなので、このように書くと便利です。

```python
class SweetsManager:
    def __init__(
        self,
        collection_name: str,
        file_name: str,
        is_frozen: bool
    ):
```

栗原さん：さらに、最後にカンマを打っておくと

```python
class SweetsManager:
    def __init__(
        self,
        collection_name: str,
        file_name: str,
        is_frozen: bool, #←ココ！！
    ):
```

あ！　引数を足すとき、1行コピペして書ける。

## まとめ

何でもかんでもクラス分割すればよいというものではない。
（細かい話）
単なる計算のユーティリティというわけでもないのに「static」だけで済むのは稀で、そうなってるときは作りが間違っているかもしれない。こういう違和感に気が付けると栗原さんに近づけるようである。