リファクタリングは、コードを読みやすく保ち、保守しやすくするための重要なプロセスです。しかし、設計の健全性を損なうリファクタリングは、予期しないエラーやパフォーマンスの低下を引き起こす原因にもなります。
本記事では、具体例としてRPGゲームのコードを使い、避けるべきリファクタリングの対象と、良いリファクタリングの方法を解説します。
C#を例に挙げますが、ここで紹介する手法は他の言語にも応用可能です。コードの設計を守りつつ健全に改善していくための参考にしてみてください。
設計の健全性を損なうリファクタリングとは?
リファクタリングは通常、コードの品質を向上させ、メンテナンスをしやすくすることが目的です。
しかし、むやみにコードをいじり、設計の整合性を損なうことで、パフォーマンスの低下や理解しにくいコードが生まれることもあります。ここでは、避けるべきリファクタリングの例を解説します。
悪い例と良い例で学ぶリファクタリング
リファクタリングを行う際、よくある問題点として、設計を損なう悪いリファクタリングのパターンが見受けられます。
ここでは、具体例としてRPGゲームのコードを用いて、避けるべき悪いリファクタリングと、その改善方法を解説します。各リファクタリング対象ごとに、悪い例と良い例を比較しながら、適切なコード改善のポイントを見ていきましょう。
1. デッドコードの削除
- 悪い例:機能追加の名残で、不要なメソッド
SaveGameがそのまま残されています。このようなデッドコードは、コードの可読性を低下させ、他の開発者に混乱を与える可能性があります。
public void SaveGame()
{
// 未使用のデッドコード
}
- 良い例:未使用のSaveGameメソッドを削除し、無駄なコードを省くことで、コード全体がすっきりとし、可読性が向上します。
2. YAGNI原則に基づくリファクタリング
- 悪い例:将来的に必要になるかもしれないと考えて
SaveGameメソッドを実装していますが、実際には使用されていません。このような不要な機能はコードの複雑さを増し、メンテナンスが難しくなります。
public void SaveGame()
{
// まだ使われていない機能
}
- 良い例:使用されていない機能は排除し、YAGNI(You Ain’t Gonna Need It)原則に基づいて、必要な機能のみを残します。
3. マジックナンバーの除去
- 悪い例:数値
50がマジックナンバーとして直接コード内に記述されており、その意味が分かりづらく、変更にも弱い設計です。
public void AttackEnemy(int damage)
{
if (damage > 50)
{
// 大ダメージ処理
}
}
- 良い例:数値をCriticalDamageThresholdという定数に置き換え、意味を明確にし、他の箇所での変更も容易にします。
4. グローバル変数の削減
- 悪い例:
PlayerHealthがグローバル変数として公開されており、他のクラスからも変更が可能です。これにより予期せぬエラーが発生するリスクが高まります。
public int PlayerHealth = 100;
- 良い例:変数をプライベートにし、外部からの直接アクセスを制限することで、誤操作によるエラーを防ぎます。
private int _playerHealth = 100;
5. null参照の管理
- 悪い例:null参照が発生する可能性のある箇所に適切な初期化が施されていません。このため、実行時にエラーが発生する可能性があります。
public string playerName;
- 良い例:必要な場合は、変数にデフォルト値や初期化処理を行い、null参照によるエラーを回避します。
public string playerName = string.Empty;
6. 例外の握りつぶしの回避
- 悪い例:try-catchブロック内で例外が握りつぶされており、エラー発生時に何も対処していないため、問題が隠れたままになるリスクがあります。
try
{
// 処理
}
catch
{
// 何もしない
}
- 良い例:必要な場合は、変数にデフォルト値や初期化処理を行い、null参照によるエラーを回避します。
try
{
// 処理
}
catch (Exception ex)
{
Console.WriteLine($"Error occurred: {ex.Message}");
}
7. 技術駆動のパッケージング改善
以下に、技術駆動のパッケージングについてのサンプルコードとその解説を示します。この例では、RPGゲームのキャラクター管理を例に、技術的な関心ごとにパッケージングされている状態を改善して、ビジネスロジックに基づく明確なパッケージングを行います。
技術駆動パッケージングの悪い例
- 悪い例:この例では、技術ごとにパッケージが分かれており、各要素が分散しているため、プロジェクト全体の構造がわかりにくくなっています。
RPGGame
├── Controllers
│ ├── CharacterController.cs
│ ├── EnemyController.cs
│ └── InventoryController.cs
├── Models
│ ├── Character.cs
│ ├── Enemy.cs
│ └── Inventory.cs
└── Services
├── CharacterService.cs
├── EnemyService.cs
└── InventoryService.cs
この構成では、キャラクターに関連する機能がControllers、Models、Servicesに分かれているため、キャラクターに関するロジックが分散してしまい、関連するコードを見つけるのに手間がかかります。
改善例:ビジネスロジックに基づいたパッケージング
- 良い例:ビジネスロジックに基づき、キャラクターに関連する機能を1つのパッケージ内にまとめます。
RPGGame
├── Characters
│ ├── Character.cs
│ ├── CharacterController.cs
│ └── CharacterService.cs
├── Enemies
│ ├── Enemy.cs
│ ├── EnemyController.cs
│ └── EnemyService.cs
└── Inventory
├── Inventory.cs
├── InventoryController.cs
└── InventoryService.cs
この構成では、キャラクター関連のコードがすべてCharactersパッケージ内にあるため、機能がまとまり、関連するロジックが見つけやすくなります。
これらの例を通じて、リファクタリングを行う際には、コードの機能と意図が明確であり、メンテナンス性を高めるための設計を意識することが重要です。
まとめ
- デッドコードを残さない
- YAGNI原則を守り不要なコードを避ける
- マジックナンバーを意味のある定数に置き換える
- グローバル変数の使用は最小限に留める
- 例外処理をしっかり行い、エラーを可視化する
リファクタリングを行う際には、単にコードをきれいにするだけでなく、設計の健全性を保ちながら改善することが重要です。特に、技術駆動のパッケージングは、見た目には整理されていても実際にはビジネスロジックが分散し、可読性や保守性に悪影響を及ぼす可能性があります。
本記事の例のように、ビジネスロジックに基づいたパッケージングにすることで、関連するコードが一つのまとまりとして保たれ、設計が明確になります。これにより、変更や拡張が容易になり、システム全体の柔軟性と理解しやすさが向上します。
リファクタリングの対象としてデッドコードやグローバル変数、マジックナンバーの排除に加え、例外の適切な処理なども行うことで、エラーの発見が容易になり、信頼性の高いコードを実現できます。リファクタリングの際には、現在のコードの役割や今後の拡張性を意識し、設計全体の健全性を損なわないように工夫することが大切です。
