この記事のポイント

  • 独自例外型を設計する理由の妥当性をレビューアーが見極める視点を整理
  • 標準系ではなくカスタム例外が必要になる設計理由を具体例で理解
  • 開発者が迷わぬレビュー指摘例を実務形式で提示

そもそも独自例外型とは

C++では標準例外体系(std::exception系)が整備されていますが、
それだけではカバーできないプロジェクト固有の責務違反ドメインエラーを表現するために
独自例外型(カスタム例外)を設計する場合があります。

  • 標準系 → 汎用構文・共通抽象
  • 独自系 → 業務ドメイン・プロジェクト固有契約

レビューアーは「その独自例外が標準型で代替不能か」を設計理由の妥当性から確認します。

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

  • カスタム例外は安易に乱立されやすい
  • 意味粒度の曖昧化が起きやすい
  • 汎用catch不能な型が増殖する
  • 障害診断情報が不足しがち

レビュー段階で設計理由を明確化させることが品質を左右します。

レビューアー視点

  • 独自例外型を定義する必然性が説明できるか
  • 標準系の上位分類に位置付け可能ではないか
  • catch網羅性と整合性を損ねていないか
  • プロジェクト全体での命名統一が取られているか
  • 例外情報が診断性に貢献しているか

開発者視点

  • 原則:標準例外系で収まるなら安易にカスタムしない
  • 独自例外化は「ビジネスルール名詞化」が発生している時のみ実施
  • what()情報はできるだけ詳細化

良い実装例

責務を明示するカスタム例外
#include <stdexcept>
#include <string>

class DuplicateUserException : public std::logic_error {
public:
    explicit DuplicateUserException(int userId)
        : std::logic_error("User ID already exists: " + std::to_string(userId)) {}
};
  • 登録済みID → 業務契約違反(logic_error系統)
  • 独自名詞 DuplicateUserException により違反内容が一目瞭然
実行系障害ドメイン名詞化
#include <stdexcept>
#include <string>

class PaymentGatewayConnectionException : public std::runtime_error {
public:
    explicit PaymentGatewayConnectionException(const std::string& gatewayName)
        : std::runtime_error("Payment gateway unreachable: " + gatewayName) {}
};
  • 汎用ネットワーク障害ではなく、決済責務名詞化が必要
  • 外部APIとの業務結合責務が明示化

レビュー観点

  • その独自名詞は業務責務違反を具体的に表現しているか
  • 標準型へ帰属できる設計を意図的に避ける必要があるか
  • what()にドメイン情報が具体挿入されているか
  • 独自型導入がcatch網羅設計に負担を掛けていないか

良くない実装例: ケース1(安易な独自例外乱立)

無意味に独自例外化
#include <string>

class CustomException {
public:
    explicit CustomException(const std::string& message) : message_(message) {}

    std::string what() const { return message_; }

private:
    std::string message_;
};

%% @Reviewer 既存のstd::runtime_errorまたはlogic_errorで十分に表現可能です。意味的粒度を具体化できない独自型は不要です。

問題点

  • 抽象度が高すぎてcatch側で用途判別不能
  • 標準例外体系破壊

改善例

改善例(標準系活用+責務名詞化)
#include <stdexcept>
#include <string>

class UserConflictException : public std::logic_error {
public:
    explicit UserConflictException(int userId)
        : std::logic_error("Conflict: User ID already exists - " + std::to_string(userId)) {}
};

良くない実装例: ケース2(標準系無視)

標準例外系の継承忘れ
#include <string>

class DatabaseException {
public:
    explicit DatabaseException(const std::string& message) : message_(message) {}

    std::string what() const { return message_; }

private:
    std::string message_;
};

%% @Reviewer std::runtime_error系継承が欠落しています。必ず標準系に連結し汎用catch網羅性を確保してください。

改善例

改善例(標準継承接続)
#include <stdexcept>
#include <string>

class DatabaseConnectionException : public std::runtime_error {
public:
    explicit DatabaseConnectionException(const std::string& dbName)
        : std::runtime_error("DB connection failed: " + dbName) {}
};

PlantUML:カスタム例外設計の分類整理

UML Diagram

観点チェックリスト

まとめ

カスタム例外レビューでは
「なぜ標準型では不十分なのか?」
をレビューアーが問い続けるのが核心です。

  • 汎用抽象 → 標準系活用
  • 業務責務名詞 → カスタム系適用

この粒度整理に沿って、設計意図を読み取り、明確にレビュー指摘していく姿勢が実務品質を左右します。