はじめに

Pythonの型定義において、ProtocolABC(抽象基底クラス)はともに「インターフェース設計」を担う強力な構文です。
しかし、コードレビューの現場ではしばしばこの2つが無思慮に混用されており、設計意図が不透明なまま進行するケースが後を絶ちません。

本記事では、レビューアー視点で「ProtocolとABCのどちらを採用すべきか」を判断するための明確な基準を提示します。
実務コードに現れる具体構造をベースに、違いを理解し、設計上の選択とそのレビュー観点を整理していきます。

使用目的から設計意図を読み解く

Protocolとは

typing.Protocolは構造的部分型(structural subtyping)を実現するための仕組みであり、「この属性やメソッドを持っていれば実装されたと見なす」性質を持ちます。
JavaやC++のinterfaceとは異なり、明示的な継承を必要とせず、「形が合えば使える」ことが最大の特徴です。

抽象基底クラス(ABC)とは

abc.ABCを基底としたクラス設計は、「明示的に継承されたクラスのみが、その抽象クラスを満たしている」と見なされます。
設計上の契約を強制し、サブクラスに明示的な実装を求めるため、実装保証を重視する設計に向いています。

レビュー時に最も注視すべきは、「設計意図と構文選択の整合性」です。
意図が「どのような機能を提供するか」ではなく、「誰がどう使うか」になっている場合、Protocolの方が設計として適しているケースが多く見られます。

典型コードとレビュー指摘例

設計意図の曖昧なインターフェース定義
from abc import ABC, abstractmethod

class Notifier(ABC):
    @abstractmethod
    def send(self, message: str) -> None:
        pass

class PrintNotifier:
    def send(self, message: str) -> None:
        print(message)

def notify_all(n: Notifier, message: str):
    n.send(message)

# 実行時エラーになる
notify_all(PrintNotifier(), "Hello")
Notifierの定義方式へのレビュー指摘
@Reviewer: `PrintNotifier`は構造的には`send()`を持っており、Protocolでの定義であれば利用可能です。  
「型チェック強制」よりも「柔軟な実装の受容」が意図される場合、ABCよりProtocolが設計意図に沿います。
Protocolの採用が適するケース
  • インターフェースを後付けで柔軟に適用したい(Duck Typingの形式保証)
  • 複数のクラスに似た形の振る舞いが自然発生的に存在する
  • ライブラリ使用者に特定の継承を強制せず、振る舞い契約のみを共有したい
ABCの方が望ましいケース
  • 派生クラスに実装の義務を強く求める(契約的強制)
  • 既存クラスとは異なる階層構造を作りたい
  • 単体テストなどでisinstance()による型判定が必要になる

使用意図の視覚化:クラス設計図

UML Diagram

図中のように、設計上の「用途拡張性」と「明示的な継承の契約」を分けて扱う構図を可視化することで、レビュー判断の軸が整理されます。

実務レビューにおける指摘テンプレート

Protocol適用を促すレビューコメント
@Reviewer: このインターフェースは外部ライブラリ由来のクラスにも使われる構造です。  
特定の継承を強制しないためにも`Protocol`ベースに切り替える方が柔軟性・再利用性が向上します。
ABC適用を促すレビューコメント
@Reviewer: 抽象クラスとして共通初期化処理も含まれており、継承構造を意図的に作る場合は`ABC`を明示したほうがコードの意図が明確になります。

補足:動的型チェックと静的型チェック

  • Protocolはmypyやpyrightなどの静的型チェックでのみ機能する構造です。
  • 実行時の型判定ではisinstance(obj, MyProtocol)は使用できません(@runtime_checkableで一部緩和可能)。
  • ABC実行時の型チェックにも対応するため、テスト時に動的判定を重視する場合は有効です。
@runtime_checkableとは

Protocolに@runtime_checkableを付与することで、isinstance()issubclass()が実行時に使えるようになります。
ただし、これはProtocolの柔軟性をある程度犠牲にする行為でもあります。

結論:レビュー観点としての判断軸

観点 Protocol 抽象基底クラス(ABC)
継承の強制性 不要 必要
静的型チェック 対応(mypy/pyright等) 対応
実行時チェック 基本非対応(※装飾で一部可能) 対応
拡張性・後付け設計 高い 低い
明示的契約の強制 不可 可能
利用者の自由度 高い 制限される

レビューアーは、「誰が使うか」「どう拡張されるか」の視点を軸に、この2つを明確に選び分ける必要があります。
型設計は型安全性そのものではなく、「設計意図を形式的にどう表現するか」の一手段です。
構文の選択が設計方針を誤認させるようであれば、それはレビューによって修正されるべきポイントです。