この記事のポイント

  • classmethodとstaticmethodの正しい設計責任分離を理解する
  • インスタンス非依存処理の設計整理をレビューアー視点で学ぶ
  • 実務レビューでレビューアーが指摘すべき典型パターンを整理

そもそもclassmethodとstaticmethodとは

Pythonのクラス定義内で、通常のインスタンスメソッドとは異なり以下の2つの特殊な静的メソッド定義が用意されています。

装飾子 第1引数 主な用途
@classmethod cls クラス生成・状態操作
@staticmethod なし 完全ユーティリティ処理
最小例
class Sample:

    @classmethod
    def from_config(cls, config):
        return cls(config["value"])

    @staticmethod
    def is_valid(value):
        return isinstance(value, int) and value > 0
  • classmethod は「クラス責任(cls)」にアクセス可能
  • staticmethod は「完全外部関数化できる責務」をクラス内部に整理

設計レビューでは「なぜその責務をそこに置くのか」が常に焦点になります。

なぜこれをレビューするのか

  • 静的メソッド化すべき処理がインスタンスメソッド化して肥大化
  • ファクトリ処理が外出しされクラス内一貫性を損失
  • ユーティリティ処理がグローバル関数汚染
  • classmethodなのにインスタンス責任を混在

レビューアーは「状態参照責任を軸に分類整理」して読む必要があります。

レビューアー視点

  • インスタンスに依存しない処理がインスタンスメソッド化していないか
  • ユーティリティ処理が適切にstaticmethod化されているか
  • クラス状態利用がclassで適切に受けているか
  • ファクトリ生成責任はclassで統一されているか
  • 静的化に伴う責務漏洩が発生していないか

開発者視点

  • 状態を参照しない処理はできるだけstaticmethodに閉じ込める
  • 生成・変換系はclass責任に統一してfrom_xxx形式に整理
  • 実行責任の外部依存度を最小化

良い実装例

正常設計例:責務分離
class User:

    def __init__(self, user_id: str, age: int):
        self.user_id = user_id
        self.age = age

    @classmethod
    def from_json(cls, data: dict):
        return cls(
            user_id=data["user_id"],
            age=data["age"]
        )

    @staticmethod
    def validate_user_id(user_id: str) -> bool:
        return user_id.isalnum() and len(user_id) <= 20

良い理由

  • from_jsonは生成責任をclass単位で集中
  • validate_user_idは外部状態依存せずstatic化
  • インスタンス不要の処理を排除

レビュー観点

  • classmethodは生成系/変換系に整理されているか
  • staticmethodは外部依存ゼロの純粋ユーティリティ責務に限定されているか
  • 状態依存しているのにstaticmethod化していないか
  • ファクトリ責任が呼び出し元に漏洩していないか

良くない実装例: ケース1

問題例: インスタンス不要メソッドの濫用
class User:

    def __init__(self, user_id):
        self.user_id = user_id

    def is_valid_user_id(self, user_id):
@Reviewer
このメソッドはインスタンスを必要としない処理です。staticmethodに整理してください。
return user_id.isalnum() and len(user_id) <= 20

問題点

  • インスタンス依存しないのにインスタンスメソッド化
  • 利用箇所で不要にインスタンス生成強制

改善例

改善後: staticmethod化
class User:

    @staticmethod
    def is_valid_user_id(user_id: str):
        return user_id.isalnum() and len(user_id) <= 20
設計補足
  • インスタンス生成強制不要
  • 呼び出し順序の自由度拡大

良くない実装例: ケース2

問題例: ファクトリ生成責任の外出し
class User:

    def __init__(self, user_id, age):
        self.user_id = user_id
        self.age = age

def create_user_from_json(data):
@Reviewer
ファクトリ生成責任がクラス外部に露出しています。classでfrom_jsonとして整理してください。
return User(data["user_id"], data["age"])

問題点

  • ファクトリロジックがグローバル関数汚染
  • クラス拡張時に変換責任が呼び出し側乱立

改善例

改善後: classmethod化
class User:

    @classmethod
    def from_json(cls, data):
        return cls(data["user_id"], data["age"])
設計補足
  • 拡張時もclsで自動適用可能
  • ファクトリロジックをクラス単位で統制

良くない実装例: ケース3

問題例: classmethod内に状態変更混在
class User:
    user_count = 0

    @classmethod
    def create(cls, user_id):
        instance = cls(user_id)
        instance.initialize_profile()
@Reviewer
classmethod内で生成と初期化副作用が混在しています。生成責任と初期化責任を分離しましょう。
return instance

問題点

  • 生成と状態操作が混在
  • 副作用依存によりテスト不安定化

改善例

改善後: 生成責任を明確分離
class User:
    user_count = 0

    @classmethod
    def create(cls, user_id):
        return cls(user_id)

    def initialize_profile(self):
        # 状態操作はインスタンス責任に整理
        pass
設計補足
  • classmethodは生成責任のみ集中
  • 状態操作はインスタンス責任に分離

PlantUML設計イメージ

UML Diagram
UML Diagram

観点チェックリスト


まとめ

classmethodとstaticmethodは「インスタンス依存の分離設計」を読み解くポイントです。
レビューアーは、生成責任集中・外部依存最小化・呼び出し負担軽減を軸に整理し、
静的化設計の肥大化と責務混在を静かに解消していくレビュー技術が求められます。
静的設計は柔軟ですが、柔軟さの裏側にある「責務境界」を読み取るのがプロのレビューです。