26. void Class::method() { void Class::method() {
for (外側ループ) { for (外側ループ) {
for (内側ループ) { for (内側ループ) {
if (条件) { method1(…);
処理; }
} }
} }
}
} void Class::method1(…) {
if (!条件)
return;
処理;
}
Before After
制御分の内側から順番にメソッド化していきます。
27. void Class::method() { void Class::method() {
for (外側ループ) { for (外側ループ)
for (内側ループ) { method2(…);
method1(); }
}
} void Class::method2(…) {
} for (内側ループ)
method1(…);
}
Before After
28. void Class::method() { void Class::method() {
for (外側ループ) { for (外側ループ)
for (内側ループ) { method2(…);
if (条件) { }
処理; void Class::method2(…) {
} for (内側ループ)
} method1(…);
} }
} void Class::method1(…) {
if (!条件)
return;
処理;
}
Before After
制御文ごとにメソッド化され、ネストが1段階となりました。
29. for (int i = 0; i < 5; ++i) {
if (a[i] == num) {
return true;
}
}
return false;
Before
return find(&a[0], &a[5], num) != &a[5];
After
検索などのループは、標準の関数を利用しましょう。STLのfindに変更しました。
30. totalAge = 0;
totalSalary = 0;
for (int i = 0; i < 5; ++i) {
totalAge += ages[i];
totalSalary += salarys[i];
}
Before
totalAge = 0;
for (int i = 0; i < 5; ++i)
totalAge += ages[i];
totalSalary = 0
for (int i = 0; i < 5; ++i)
totalSalary += salarys[i]; After
ループの内側で2つのことを扱うと、メソッド化が困難です。ループを分割します。
31. totalAge = 0;
for (int i = 0; i < 5; ++i)
totalAge += ages[i];
totalSalary = 0;
for (int i = 0; i < 5; ++i)
totalSalary += salarys[i];
Before
totalAge = accumulate(&ages[0], &ages[5], 0);
totalSalary = accumulate(&salarys[0], &salarys[5], 0);
After
配列の合計はSTLのaccumulate関数で求められます。
32. totalAge = accumulate(&ages[0], &ages[5], 0);
totalSalary = accumulate(&salarys[0], &salarys[5], 0);
Before
int Class::totalAge() {
return accumulate(&ages[0], &ages[5], 0);
}
int Class::totalSalary() {
return accumulate(&salarys[0], &salarys[5], 0);
} After
さらにメソッド化をしてみました。
33. totalAge = 0;
totalSalary = 0;
for (int i = 0; i < 5; ++i) {
totalAge += ages[i];
totalSalary += salarys[i];
}
Before
int Class::totalAge() {
return accumulate(&ages[0], &ages[5], 0);
}
int Class::totalSalary() {
return accumulate(&salarys[0], &salarys[5], 0);
} After
Martin Fowlerの「Split Loop」というリファクタング例になります。
36. int method() { int method() {
int result; if (!条件1)
if (条件1) { return 10;
if (条件2) { if (条件2)
result = 20; return 20;
} else { return 30;
result = 30; }
}
} else {
result = 10;
}
return result;
}
Before After
左と右のコードは同等の処理を行います。早期リターンを使うと単純になります。
37. switch (actorType) { class Actor {
case PLAYER: public:
virtual void update() = 0;
updatePlayer();
};
break;
case ENEMY:
updateEnemy(); actor->update();
break;
case BULLET:
updateBullet();
break;
}
Before After
「State/Strategyによるタイプコードの置き換え」というリファクタリングです。
38. int method() { int method() {
int result; return 条件 ? 20: 30;
if (条件) }
result = 20;
else
result = 30;
return result;
}
Before After
三項演算子(?)は使いすぎると、わかりにくくなりますが、単純なケースでは有用です。
39. if (x < 0) {
x = 0;
} else if (x > 640) {
x = 640;
}
Before
x = max(0, min(640, x));
After
上記のようなif文は、よく見かけますが、STLのmin,max関数で書き直せます。
40. if (x < 0) {
x = 0;
} else if (x > 640) {
x = 640;
}
Before
x = clamp(x, 0, 640);
float clamp(float x, float bottom, float top) {
return max(bottom, min(top, x));
}
After
さらにclampという関数を作ってみます。C++であれば、関数テンプレート化しましょう。
43. class Game { class Game {
private: private:
int score; Score score;
int limitTime; LimitTime time;
}; };
class Score {
private:
int score;
};
class LimitTime {
private:
int time;
};
Before After
得点や制限時間をクラス化します。「すべてがオブジェクト」になっていきます。
57. class Player { class Player {
private: private:
float x; Vector2 position;
float y; Angle angle;
float angle; Life life;
int life; };
}; class Angle {
private:
float angle;
};
class Life {
private:
int life;
};
Before After
状態変数が2つになるまで、段階的に変更していきます。
58. class Player { class Player {
private: private:
Vector2 position; Transform pose;
Angle angle; Life life;
Life life; };
};
class Transform {
private:
Vector2 position;
Angle angle;
};
Before After
positionとangleは座標変換を扱うクラスとして、まとめられそうです。
59. class Player { class Player {
private: private:
float x; Transform pose;
float y; Life life;
float angle; };
int life;
};
Before After
状態変数が2つになりました。
60. class Player { class Player {
public: public:
Player(float x, Player(Transform& pose,
float y, Life& life);
float angle, void update(Time& time);
int life); private:
void update( Transform pose;
float time); Life life;
private: };
float x;
float y;
float angle;
int life;
};
Before After
右側のクラスは、すべてがオブジェクトと関連しています。抽象度が高くなるわけです。
63. class Game {
private:
std::list<Actor*> actors;
std::list<Particles*> particles;
};
Before
class ActorManager {
std::list<Actor*> actors;
};
class ParticleManager {
std::list<Particle*> particles;
}; After
2つのコレクションを1つのクラスで扱えば、おそらく複雑なクラスになるでしょう。
64. class Game {
private:
std::list<Actor*> actors;
std::list<Particles*> particles;
};
Before
class Game {
private:
ActorManager actors;
ParticleMananger particles;
};
After
専用のクラスを作って、そちらに委譲してしまいます。