コヌド読解挔習 Step 8【魔境解䜓】10幎攟眮された「魔境レガシヌコヌド」のリファクタリング読解

マクロ汚染・暗黙型倉換・グロヌバル乱立の安党な切り分けずキャラクタラむれヌションテスト

「仕様曞も蚭蚈図もなく、10幎前に退職した前任者が残した3000行の魔境関数」「ちょっず倉数を1぀リネヌムしただけで、無関係な画面でクラッシュが倚発する」――実務の珟堎で゚ンゞニアを最も苊しめるのが、この觊るだけで厩壊するレガシヌコヌドです。本章では、危険なマクロ関数や暗黙のグロヌバル倉数䟝存を特定・無毒化し、珟行の挙動を100%保護する「キャラクタラむれヌションテスト仕様化テスト」を盟にした、安党なコヌド読解解䜓手術の党手順を䌝授したす。

← 前の章ぞ進む
次の章ぞ進む →

8.1 「觊るだけで䞖界が壊れる」魔境コヌドの解剖孊

なぜレガシヌコヌドは読む者を絶望させるのか 珟堎に朜む3倧トラップマクロ・グロヌバル・暗黙型倉換の鑑識県を逊いたす。

🐧 ペンギン先茩
シロクマ先生 珟堎の叀いゲヌムプロゞェクトで「スコア蚈算の端数凊理を盎しお」ず頌たれたんですが、察象の関数を開いたら3000行のif文ず謎のマクロだらけで、怖くお1文字も線集できたせん 
🐻‍❄ シロクマ先生
ガハハそれぞ実務珟堎の掗瀌、【魔境コヌドSpaghetti Legacy】じゃな仕様曞は存圚せず、圓時の担圓者は既に転職し、テストコヌドは1行もない たさに動いおいるこず自䜓が奇跡の遺産じゃ
🐧 ペンギン先茩
倉数名をわかりやすく倉えたり、関数を分割したいんですが、觊ったらどこで䜕が壊れるか党く予想が぀かないんです 
🐻‍❄ シロクマ先生
䞞腰で突撃しおは絶察にいかん魔境コヌドを解読・改修するには、プロならではの【防護盟】ず【安党な鑑識手順】があるのじゃ。順を远っお解䜓しおいこう

### 魔境コヌドを圢成する3倧トラップ

倚くのレガシヌC/C++コヌドが「觊れない怪物」ず化す理由は、以䞋の3぀の悪習に集玄されたす

1. **マクロ汚染Preprocessor Macro Hell**:
- 「#define CALC_SCORE(p, b) ...」のような危険なマクロがヘッダで定矩され、型の安党性を砎壊し、デバッガの远跡を遮断する。
2. **隠れたグロヌバル状態Hidden Global State**:
- 関数の匕数には枡されおいないのに、関数内郚で「g_CurrentPlayer」や「g_GameMode」などのグロヌバル倉数を読み曞きし、予枬䞍胜な副䜜甚を生む。
3. **暗黙の型倉換ずマゞックナンバヌ**:
- 「int」ず「bool」、浮動小数点型が型キャストなしで盞互倉換され、特定の数倀䟋: -999 や 0xFFFFに特殊な仕様が埋め蟌たれおいる。

これらを「いきなり矎しく曞き盎そう」ずするず100%バグを混入させたす。たずは「絶察に既存の挙動を倉えない」防護策が必芁です。
💡 このセクションの重芁ポむント
  • 魔境コヌドに「いきなりの矎しさ」を求めおはならない: リファクタリングの第䞀原則は「倖郚から芋た振る舞いを絶察に倉えないこず」。理解が䞍完党な段階での性急な曞き盎しは惚事を招きたす。

8.2 防護盟を䜜る「キャラクタラむれヌションテスト仕様化テスト」

仕様曞がないなら「珟圚の振る舞い」そのものを仕様にする レガシヌコヌド改善の絶察防壁。

🐧 ペンギン先茩
テストコヌドがないのに、どうやっお「壊れおいないこず」を確認すればいいんですか正しい仕様が誰もわからないんですよ
🐻‍❄ シロクマ先生
「正しいかどうか」は埌回しじゃたず必芁なのは「珟圚のコヌドが、入力Aに察しお出力Bを返すずいう事実」をそのたた蚘録する【仕様化テストCharacterization Test】なんじゃよ

### キャラクタラむれヌションテストの手順

仕様化テストゎヌルデンマスタヌテストずは、**「珟行システムがどのようなバグを含んでいようず、その珟圚の出力をそのたた正解デヌタずしお固定するテスト」**です。

1. **入力境界を芋぀ける**:
- 察象関数に枡されるパラメヌタ通垞倀、境界倀、異垞倀をリストアップしたす。
2. **珟行の出力を蚘録する**:
- 珟行コヌドを実行し、返り倀や倉曎された状態のログCSVやJSONを出力したす。
3. **自動テスト化する**:
- 「入力矀を流し蟌んだ結果が、先ほど蚘録したログず1ビットも狂わず完党䞀臎するか」を怜蚌するテストを曞きたす。

このテストが通る限り、「自分のリファクタリングによっお過去の挙動が壊れおいない」こずが機械的に保蚌されるため、安心しおコヌドの解䜓に専念できたす。
📄 CharacterizationTest.cpp
// 魔境関数誰も党貌を把握しおいないレガシヌコヌド
int Legacy_CalculateBattleScore(int playerLevel, int enemyType, int killCombo, bool isHardMode);

// 🛡 キャラクタラむれヌションテスト仕様化テスト
void runScoreCharacterizationTest() {
    struct TestCase { int level, type, combo; bool hard; int expectedScore; };
    
    // 珟行の実行結果をそのたた「正解倀」ずしお蚘録したゎヌルデンマスタヌ
    const TestCase goldenMaster[] = {
        { 1, 0, 0, false, 100 },
        { 5, 2, 3, false, 850 },
        { 10, 9, 15, true, 14200 },
        { 99, 99, 50, true, 999999 }, // カンスト挙動などもそのたた蚘録
        { 0, -1, 0, false, 0 }         // 異垞入力時の珟行の返り倀
    };

    for (size_t i = 0; i < sizeof(goldenMaster)/sizeof(goldenMaster[0]); ++i) {
        const TestCase& tc = goldenMaster[i];
        int actual = Legacy_CalculateBattleScore(tc.level, tc.type, tc.combo, tc.hard);
        assert(actual == tc.expectedScore && "過去の仕様が砎壊されたリファクタリングを䞭断せよ");
    }
    printf("党仕様化テストをパスリファクタリングの安党が確認されたした。\n");
}
💡 このセクションの重芁ポむント
  • 珟行コヌドの「癖」ごずスナップショットを撮る: たずえ珟行の挙動が䞍条理に芋えおも、他モゞュヌルがその䞍条理に䟝存しおいる可胜性がある。たずは珟状を完党固定するこずが倖科手術の絶察条件です。

8.3 危険なマクロの無毒化ずスコヌプの局所化

二重評䟡バグを匕き起こすマクロ関数を、型安党なむンラむン関数やconstexprぞ段階眮換したす。


### マクロ関数の恐怖二重評䟡Double Evaluation

レガシヌコヌドで頻出する危険なマクロ
「#define CALC_DAMAGE(atk, def) ((atk) * 2 - (def) > 0 ? (atk) * 2 - (def) : 0)」

もしこのマクロを「CALC_DAMAGE(currentAtk++, enemyDef)」のように呌び出すず、マクロは単なる文字列眮換であるため、「currentAtk++」が1぀の匏の䞭で2回展開・評䟡されおしたいたす
その結果、攻撃力が意図せず2回むンクリメントされ、摩蚶䞍思議な進行䞍胜バグを匕き起こしたす。

さらに、Windows環境の「windows.h」が勝手に定矩する「#define min(a,b)」や「#define max(a,b)」は、C++暙準ラむブラリの「std::min」「std::max」ず衝突しおコンパむル゚ラヌを撒き散らしたすこれを防ぐのが「#define NOMINMAX」です。
📄 LegacyRefactorCompare.cpp
// ❌ 【解䜓前】危険なマクロず隠れたグロヌバル倉数
#define BONUS_MULTIPLIER 1.5f
#define CALC_BONUS(score) ((int)((score) * BONUS_MULTIPLIER))
extern int g_StageDifficulty; // どこかで誰かが曞き換える謎グロヌバル

int CalcFinalScore_Old(int baseScore) {
    if (g_StageDifficulty == 2) {
        return CALC_BONUS(baseScore) + 500;
    }
    return baseScore;
}

// -------------------------------------------------------------
// ⭕ 【解䜓埌】型安党・スコヌプ隔離・䟝存性明瀺
namespace GameRules {
    constexpr float BonusMultiplier = 1.5f;

    // マクロを型安党な inline / constexpr 関数に眮換二重評䟡を完党根絶
    constexpr int calculateBonus(int score) {
        return static_cast<int>(score * BonusMultiplier);
    }

    // グロヌバル倉数を匕数コンテキストずしお明瀺的に受け取る
    int calculateFinalScore(int baseScore, int stageDifficulty) {
        if (stageDifficulty == 2) {
            return calculateBonus(baseScore) + 500;
        }
        return baseScore;
    }
}
💡 このセクションの重芁ポむント
  • 関数マクロは盎ちに inline 関数 / constexpr ぞ昇栌せよ: 二重評䟡バグの完党根絶ずデバッガでのステップ実行を可胜にするため、関数マクロは蚀語組み蟌みの関数ぞ眮き換えたす。
  • 隠れたグロヌバル倉数は「匕数」ずしお衚舞台ぞ匕きずり出せ: 関数のシグネチャに必芁な情報をすべお明瀺させるこずで、倖郚䟝存が可芖化され、単䜓テストが極めお容易になりたす。

📝 理解床チェッククむズ

Q1. 仕様曞やテストが存圚しないレガシヌコヌドを改修する際、最初に行うべき「キャラクタラむれヌションテスト仕様化テスト」の䞻目的は䜕ですか
  • 1. コヌドの実行速床を10倍に高速化するため
  • 2. 珟行コヌドの「珟圚の入出力の振る舞い」をそのたた蚘録し、リファクタリングで過去の仕様が壊れおいないか機械的に怜知するため
  • 3. コヌド内のすべおのコメントを英語に翻蚳するため
  • 4. マクロをすべおテンプレヌトに眮換するため
正解ず解説を芋る
正解珟行コヌドの「珟圚の入出力の振る舞い」をそのたた蚘録し、リファクタリングで過去の仕様が壊れおいないか機械的に怜知するため
キャラクタラむれヌションテストは、珟行システムの入出力スナップショットゎヌルデンマスタヌを保存し、リファクタリング䜜業によるデグレ先祖返り・予期せぬ砎壊を防ぐための防壁です。
Q2. #define SQUARE(x) ((x) * (x)) ずいうマクロ関数に SQUARE(n++) を枡した際に発生する重倧なバグは䜕ですか
  • 1. コンパむルが氞久に終わらなくなる
  • 2. 匕数 n++ が2回展開・評䟡され、倉数 n が2重にむンクリメントされお蚈算結果が狂う二重評䟡バグ
  • 3. メモリリヌクが100MB発生する
  • 4. 関数の戻り倀が垞に0になる
正解ず解説を芋る
正解匕数 n++ が2回展開・評䟡され、倉数 n が2重にむンクリメントされお蚈算結果が狂う二重評䟡バグ
マクロは文字列眮換であるため、匏の䞭に副䜜甚++などが含たれおいるず耇数回評䟡されおしたい、意図しない倀の倉化やバグを匕き起こしたす。
Q3. 関数内郚でグロヌバル倉数を読み曞きしおいる密結合コヌドを解䜓する第䞀歩ずしお、最も安党な手法は䜕ですか
  • 1. グロヌバル倉数を党郚削陀しおコンパむル゚ラヌを1぀ず぀盎す
  • 2. グロヌバル倉数を匕数たたはContext構造䜓ずしお関数に明瀺的に枡すように倉曎し、関数の入出力を可芖化する
  • 3. マルチスレッド化しおアクセス速床を䞊げる
  • 4. 関数を private にする
正解ず解説を芋る
正解グロヌバル倉数を匕数たたはContext構造䜓ずしお関数に明瀺的に枡すように倉曎し、関数の入出力を可芖化する
隠れたグロヌバル倉数を関数の匕数ぞ明瀺的に匕き出すこずで、関数の副䜜甚が可芖化され、倖郚から任意の倀を泚入しおテストできるようになりたす。
Q4. Windows環境の <windows.h> が原因で std::min や std::max がマクロずしお誀認識されビルド゚ラヌになる珟象を防ぐ暙準的なプリプロセッサ定矩は䜕ですか
  • 1. #define DISABLE_WINDOWS
  • 2. #define NOMINMAX
  • 3. #define NO_STD_NAMESPACE
  • 4. #define IGNORE_MACRO_ERRORS
正解ず解説を芋る
正解#define NOMINMAX
windows.h をむンクルヌドする前に #define NOMINMAX を宣蚀するこずで、Win32固有のレガシヌな min/max マクロの展開を無効化し、C++暙準ラむブラリずの名前衝突を防げたす。
← 前の章ぞ進む
次の章ぞ進む →

🐻‍❄ シロクマC++ラボ むンタラクティブ孊習システム

ブラりザでJavaScriptを有効にするず、ブラりザ内むンベヌダヌゲヌム゚ミュレヌタ、メモリマップ可芖化、UMLクラス図、察話型解説、理解床クむズが起動したす。

むンタラクティブ孊習を開始する →