オブジェクト指向アンチパターンから美しいコードへの変換

この記事では、神クラス(God Object)のアンチパターンを美しいオブジェクト指向コードに変換する手順を説明します。初学者向けに基本概念とステップバイステップのガイドを提供します。

人の情報を管理するサンプル

1. アンチパターンのコード例

public class GodClass
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
    public double Salary { get; set; }

    public void Save() { /* 保存ロジック */ }
    public void Load() { /* 読み込みロジック */ }
    public void PrintDetails() 
    { 
        Console.WriteLine($"Name: {Name}, Age: {Age}, Address: {Address}, Salary: {Salary}"); 
    }
    public double CalculateTax() 
    {
        return Salary * 0.15;
    }
}

神クラス(God Object)の問題点

神クラスは、以下の問題を抱えています:

  1. 単一責任の原則(SRP)の違反
    • クラスが複数の責任を持つことで、変更が困難になります。たとえば、GodClassは個人情報、保存ロジック、読み込みロジック、表示ロジック、税金計算ロジックをすべて含んでいます。
  2. 保守性の低下
    • クラスが大きくなりすぎると、理解や保守が難しくなります。特に、新しい機能を追加する際に、既存のコードに影響を与える可能性が高くなります。
  3. 再利用性の欠如
    • 複数の責任を1つのクラスにまとめると、そのクラスの一部機能だけを再利用することが難しくなります。たとえば、税金計算ロジックだけを再利用したい場合、他のすべての機能も含めてしまうことになります。

このコード例では、GodClassがすべての責任を持っています。これを改善するためには、各責任を独立したクラスに分割し、それぞれのクラスが単一の責任を持つように設計します

2. 美しいオブジェクト指向コードに変換する手順

ステップ 1: クラスの分割

まず、クラスを責任ごとに分割します。

// Personクラスは個人情報を保持
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
    public Salary Salary { get; set; }  // 給与を関連付け
}

// Salaryクラスは給与情報と税計算を管理
public class Salary
{
    public double Amount { get; set; }
    public double CalculateTax()
    {
        return Amount * 0.15;
    }
}

// DataManagerクラスはデータの保存と読み込みを担当
public class DataManager
{
    public void Save(Person person)
    {
        // 保存ロジック
    }

    public Person Load()
    {
        return new Person();
    }
}

ステップ 2: メソッドの分離

各クラスにメソッドを適切に配置し、責任の分離を強化します。

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
    public Salary Salary { get; set; }

    // 個人の詳細を表示
    public void PrintDetails()
    {
        Console.WriteLine($"名前: {Name}, 年齢: {Age}, 住所: {Address}, 給与: {Salary.Amount}");
    }
}

public class Salary
{
    public double Amount { get; set; }

    // 税金を計算
    public double CalculateTax()
    {
        return Amount * 0.15;
    }
}

public class DataManager
{
    // 保存ロジック
    public void Save(Person person)
    {
        // 保存ロジック
    }

    // 読み込みロジック
    public Person Load()
    {
        return new Person();
    }
}

ステップ 3: クラスの協調

分割したクラスが協調して動作するように設計します。

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
    public Salary Salary { get; set; }

    // 個人の詳細を表示
    public void PrintDetails()
    {
        Console.WriteLine($"名前: {Name}, 年齢: {Age}, 住所: {Address}, 給与: {Salary.Amount}");
    }
}

public class Salary
{
    public double Amount { get; set; }

    // 税金を計算
    public double CalculateTax()
    {
        return Amount * 0.15;
    }
}

public class DataManager
{
    // 保存ロジック
    public void Save(Person person)
    {
        // 保存ロジック
    }

    // 読み込みロジック
    public Person Load()
    {
        return new Person();
    }
}

// メインプログラム
class Program
{
    static void Main(string[] args)
    {
        // Personオブジェクトを初期化
        Person person = new Person
        {
            Name = "山田 太郎",
            Age = 30,
            Address = "東京都渋谷区",
            Salary = new Salary { Amount = 5000000 }
        };

        // DataManagerを使用してデータを保存
        DataManager dataManager = new DataManager();
        dataManager.Save(person);

        // 個人の詳細を表示
        person.PrintDetails();
        Console.WriteLine($"税金: {person.Salary.CalculateTax()}");
    }
}

まとめ

この資料では、神クラスのアンチパターンから始めて、責任を分割し、美しいオブジェクト指向設計に変換する手順を示しました。これにより、コードの保守性が向上し、変更が容易になります。具体的には、単一責任の原則(SRP)に従い、各クラスが特定の責任を持つように設計することが重要です。

ゲームシーンでのサンプル

この記事では、簡易なゲームイメージを通じて神クラス(God Object)のアンチパターンを美しいオブジェクト指向コードに変換する手順を説明します。初学者向けに基本概念とステップバイステップのガイドを提供します。

1. アンチパターンのコード例

public class Game
{
    public string PlayerName { get; set; }
    public int Score { get; set; }
    public int Level { get; set; }
    public int Health { get; set; }

    public void Start() { /* ゲーム開始ロジック */ }
    public void Update() { /* ゲーム更新ロジック */ }
    public void End() { /* ゲーム終了ロジック */ }
}

神クラス(God Object)の問題点

神クラスは、以下の問題を抱えています:

  1. 単一責任の原則(SRP)の違反
    • クラスが複数の責任を持つことで、変更が困難になります。たとえば、Gameクラスはプレイヤー情報、ゲーム開始ロジック、更新ロジック、終了ロジックをすべて含んでいます。
  2. 保守性の低下
    • クラスが大きくなりすぎると、理解や保守が難しくなります。特に、新しい機能を追加する際に、既存のコードに影響を与える可能性が高くなります。
  3. 再利用性の欠如
    • 複数の責任を1つのクラスにまとめると、そのクラスの一部機能だけを再利用することが難しくなります。たとえば、たとえば、ゲーム開始ロジックだけを再利用したい場合、他のすべての機能も含めてしまうことになります。

このコード例では、Gameクラスがすべての責任を持っています。これを改善するためには、各責任を独立したクラスに分割し、それぞれのクラスが単一の責任を持つように設計します。

2. 美しいオブジェクト指向コードに変換する手順

ステップ 1: クラスの分割

まず、クラスを責任ごとに分割します。

// Playerクラスはプレイヤー情報を保持
public class Player
{
    public string Name { get; set; }
    public int Score { get; set; }
    public int Health { get; set; }

    // プレイヤーの詳細を表示
    public void PrintDetails()
    {
        Console.WriteLine($"名前: {Name}, スコア: {Score}, 体力: {Health}");
    }
}

// GameManagerクラスはゲーム全体の管理を担当
public class GameManager
{
    public int Level { get; set; }

    // ゲーム開始ロジック
    public void StartGame(Player player)
    {
        Console.WriteLine($"{player.Name} がゲームを開始しました。");
        Level = 1;
        player.Score = 0;
        player.Health = 100;
    }

    // ゲーム更新ロジック
    public void UpdateGame(Player player)
    {
        player.Score += 10;
        Console.WriteLine($"{player.Name} のスコア: {player.Score}");
    }

    // ゲーム終了ロジック
    public void EndGame(Player player)
    {
        Console.WriteLine($"{player.Name} の最終スコア: {player.Score}");
    }
}

ステップ 2: クラスの協調

分割したクラスが協調して動作するように設計します。

public class Player
{
    public string Name { get; set; }
    public int Score { get; set; }
    public int Health { get; set; }

    // プレイヤーの詳細を表示
    public void PrintDetails()
    {
        Console.WriteLine($"名前: {Name}, スコア: {Score}, 体力: {Health}");
    }
}

public class GameManager
{
    public int Level { get; set; }

    // ゲーム開始ロジック
    public void StartGame(Player player)
    {
        Console.WriteLine($"{player.Name} がゲームを開始しました。");
        Level = 1;
        player.Score = 0;
        player.Health = 100;
    }

    // ゲーム更新ロジック
    public void UpdateGame(Player player)
    {
        player.Score += 10;
        Console.WriteLine($"{player.Name} のスコア: {player.Score}");
    }

    // ゲーム終了ロジック
    public void EndGame(Player player)
    {
        Console.WriteLine($"{player.Name} の最終スコア: {player.Score}");
    }
}

// メインプログラム
class Program
{
    static void Main(string[] args)
    {
        // Playerオブジェクトを初期化
        Player player = new Player
        {
            Name = "プレイヤー1"
        };

        // GameManagerを使用してゲームを管理
        GameManager gameManager = new GameManager();
        gameManager.StartGame(player);

        // ゲームを更新
        gameManager.UpdateGame(player);

        // プレイヤーの詳細を表示
        player.PrintDetails();

        // ゲームを終了
        gameManager.EndGame(player);
    }
}

まとめ

神クラスのアンチパターンから、単一責任の原則(SRP)に従った美しいオブジェクト指向設計に変換する手順を紹介しました。これにより、コードの保守性が向上し、変更が容易になります。

C#

Posted by hidepon