コンポジションの理解と実装:買い物籠(ショッピングカート)を題材にしたC#およびUnityでのアプローチ

目次

  1. はじめに
  2. コンポジションとは
  3. C#コンソールアプリケーションによる実装
  4. Unity環境での実装
  5. コンポジションのメリット
  6. まとめ

はじめに

オブジェクト指向プログラミング(OOP)において、コンポジション(Composition)は、複雑なオブジェクトを構築するための基本的な手法です。本資料では、買い物籠(ショッピングカート)を題材に、C#およびUnity環境でのコンポジションの概念と実装方法を初学者向けに解説します。


コンポジションとは

コンポジションは、オブジェクト同士の「~を持つ(has-a)」関係を表現する手法です。これは、あるクラスが他のクラスのインスタンスをメンバーとして保持することで、再利用性と柔軟性の高い設計を可能にします。継承(Inheritance)とは異なり、コンポジションはクラス間の強い結合を避け、より柔軟なコード設計を実現します。

コンポジションの利点

  1. 再利用性の向上:個々のクラスが独立しているため、他のプロジェクトやクラスでも再利用が容易です。
  2. 柔軟性:メンバーオブジェクトを動的に変更・追加できるため、機能の拡張が容易です。
  3. 保守性:各クラスが独立しているため、修正や拡張が他の部分に影響を与えにくいです。

C#コンソールアプリケーションによる実装

ここでは、C#のコンソールアプリケーションを使用して、買い物籠のコンポジションを実装します。

クラス設計

買い物籠のコンポジションを実現するために、以下の2つのクラスを設計します。

  1. Itemクラス:個々の商品を表すクラス。
  2. ShoppingBasketクラス:複数のItemを含む買い物籠を表すクラス。

Itemクラスの実装

まず、個々の商品を表すItemクラスを作成します。

public class Item
{
    public string Name { get; set; }
    public decimal Price { get; set; }

    public Item(string name, decimal price)
    {
        Name = name;
        Price = price;
    }

    public override string ToString()
    {
        return $"{Name}: ¥{Price}";
    }
}
  • プロパティ
    • Name:商品の名前
    • Price:商品の価格
  • コンストラクタ:名前と価格を設定
  • ToStringメソッド:商品の情報を文字列として返す

ShoppingBasketクラスの実装

次に、ShoppingBasketクラスを作成し、複数のItemを保持できるようにします。

using System;
using System.Collections.Generic;

public class ShoppingBasket
{
    private List<Item> items;

    public ShoppingBasket()
    {
        items = new List<Item>();
    }

    // アイテムを追加するメソッド
    public void AddItem(Item item)
    {
        items.Add(item);
    }

    // アイテムを削除するメソッド
    public void RemoveItem(Item item)
    {
        items.Remove(item);
    }

    // 合計金額を計算するメソッド
    public decimal GetTotalPrice()
    {
        decimal total = 0;
        foreach (var item in items)
        {
            total += item.Price;
        }
        return total;
    }

    // 買い物籠の内容を表示するメソッド
    public void DisplayBasket()
    {
        Console.WriteLine("買い物籠の中身:");
        foreach (var item in items)
        {
            Console.WriteLine(item);
        }
        Console.WriteLine($"合計金額: ¥{GetTotalPrice()}");
    }
}
  • フィールド
    • itemsItemオブジェクトのリスト
  • コンストラクタitemsを初期化
  • メソッド
    • AddItem:アイテムを追加
    • RemoveItem:アイテムを削除
    • GetTotalPrice:合計金額を計算
    • DisplayBasket:買い物籠の内容を表示

使用例

以下に、ItemShoppingBasketクラスを使用した簡単な例を示します。

using System;

class Program
{
    static void Main(string[] args)
    {
        // 商品を作成
        Item apple = new Item("リンゴ", 100);
        Item bread = new Item("パン", 200);
        Item milk = new Item("牛乳", 150);

        // 買い物籠を作成
        ShoppingBasket basket = new ShoppingBasket();

        // アイテムを買い物籠に追加
        basket.AddItem(apple);
        basket.AddItem(bread);
        basket.AddItem(milk);

        // 買い物籠の内容を表示
        basket.DisplayBasket();

        // 商品を削除
        basket.RemoveItem(bread);

        // 再度表示
        Console.WriteLine("\nパンを削除後:");
        basket.DisplayBasket();
    }
}

出力例:

買い物籠の中身:
リンゴ: ¥100
パン: ¥200
牛乳: ¥150
合計金額: ¥450

パンを削除後:
買い物籠の中身:
リンゴ: ¥100
牛乳: ¥150
合計金額: ¥250

Unity環境での実装

Unityでは、コンポジションは主にコンポーネント(Component)を通じて実現されます。以下では、Unityプロジェクト内でコンポジションを活用して買い物籠の機能を実装する方法を解説します。

プロジェクトの準備

  1. Unity Hubを使用して新しい3Dプロジェクトを作成します。プロジェクト名は「ShoppingBasketExample」とします。
  2. 作成後、Assetsフォルダ内にScriptsフォルダを作成します。

Itemクラスの作成

Itemは個々の商品を表します。Unityでは、ScriptableObjectを使用してデータを管理するのが一般的です。これにより、エディター上で簡単にアイテムを作成・管理できます。

スクリプトの作成

  1. Scriptsフォルダ内にItem.csという名前で新しいC#スクリプトを作成します。
  2. 以下のコードをItem.csに記述します。
using UnityEngine;

[CreateAssetMenu(fileName = "NewItem", menuName = "Shopping/Item")]
public class Item : ScriptableObject
{
    public string itemName;
    public float price;

    public override string ToString()
    {
        return $"{itemName}: ¥{price}";
    }
}

スクリプトの説明

  • CreateAssetMenu属性を使用して、エディター上で簡単にItemを作成できるようにします。
  • itemNamepriceの2つのパブリック変数を定義します。
  • ToString()メソッドをオーバーライドして、アイテムの情報を表示します。

アイテムの作成

  1. Unityエディター上で、Assets > Create > Shopping > Itemを選択します。
  2. 作成されたItemアセットに名前を付けます(例:Apple, Bread, Milk)。
  3. 各アイテムのitemNamepriceを設定します。

ShoppingBasketクラスの作成

ShoppingBasketは複数のItemを保持する買い物籠を表します。

スクリプトの作成

  1. Scriptsフォルダ内にShoppingBasket.csという名前で新しいC#スクリプトを作成します。
  2. 以下のコードをShoppingBasket.csに記述します。
using System.Collections.Generic;
using UnityEngine;

public class ShoppingBasket : MonoBehaviour
{
    private List<Item> items = new List<Item>();

    // アイテムを追加するメソッド
    public void AddItem(Item item)
    {
        items.Add(item);
        Debug.Log($"{item.itemName} をカートに追加しました。");
        DisplayBasket();
    }

    // アイテムを削除するメソッド
    public void RemoveItem(Item item)
    {
        if (items.Remove(item))
        {
            Debug.Log($"{item.itemName} をカートから削除しました。");
            DisplayBasket();
        }
        else
        {
            Debug.Log($"{item.itemName} はカートに存在しません。");
        }
    }

    // 合計金額を計算するメソッド
    public float GetTotalPrice()
    {
        float total = 0f;
        foreach (var item in items)
        {
            total += item.price;
        }
        return total;
    }

    // 買い物籠の内容を表示するメソッド
    public void DisplayBasket()
    {
        Debug.Log("買い物籠の中身:");
        foreach (var item in items)
        {
            Debug.Log(item.ToString());
        }
        Debug.Log($"合計金額: ¥{GetTotalPrice()}");
    }
}

スクリプトの説明

  • itemsList<Item>を使用して複数のアイテムを保持します。
  • AddItem(Item item):アイテムを追加し、カートの内容を表示します。
  • RemoveItem(Item item):アイテムを削除し、結果を表示します。
  • GetTotalPrice():カート内のアイテムの合計金額を計算します。
  • DisplayBasket():カートの内容と合計金額をデバッグログに表示します。

テスト用スクリプトの作成

実際にShoppingBasketを動作させるために、テスト用のスクリプトを作成します。

スクリプトの作成

  1. Scriptsフォルダ内にBasketTester.csという名前で新しいC#スクリプトを作成します。
  2. 以下のコードをBasketTester.csに記述します。
using UnityEngine;

public class BasketTester : MonoBehaviour
{
    public ShoppingBasket shoppingBasket;
    public Item apple;
    public Item bread;
    public Item milk;

    void Start()
    {
        if (shoppingBasket == null)
        {
            Debug.LogError("ShoppingBasket が割り当てられていません!");
            return;
        }

        // アイテムをカートに追加
        shoppingBasket.AddItem(apple);
        shoppingBasket.AddItem(bread);
        shoppingBasket.AddItem(milk);

        // アイテムを削除
        shoppingBasket.RemoveItem(bread);
    }
}

スクリプトの説明

  • shoppingBasket:シーン内のShoppingBasketコンポーネントを参照します。
  • apple, bread, milk:作成したItemアセットを参照します。
  • Start()メソッドで、アイテムの追加と削除を行い、カートの動作を確認します。

シーンの設定

シーンにオブジェクトを追加

  1. Hierarchyビューで、右クリック > Create Emptyを選択し、ShoppingBasketという名前の空のGameObjectを作成します。
  2. ShoppingBasketオブジェクトにShoppingBasket.csスクリプトを追加します。
  3. 同様に、HierarchyビューでBasketTesterという名前の空のGameObjectを作成し、BasketTester.csスクリプトを追加します。

スクリプトのリンク

  • BasketTesterオブジェクトを選択し、InspectorビューでBasketTesterスクリプトのパラメータを設定します。
    • Shopping Basket:シーン内のShoppingBasketオブジェクトをドラッグ&ドロップします。
    • Apple, Bread, Milk:作成したItemアセット(例:Assets > Shopping > Apple, Bread, Milk)をそれぞれ対応するフィールドにドラッグ&ドロップします。

実行と確認

  1. シーンを保存します(例:Scene1)。
  2. Unityエディター上部の再生ボタンをクリックしてプレイモードに入ります。
  3. Consoleウィンドウを確認すると、以下のようなログが表示されます。
リンゴ をカートに追加しました。
買い物籠の中身:
リンゴ: ¥100
合計金額: ¥100
パン をカートに追加しました。
買い物籠の中身:
リンゴ: ¥100
パン: ¥200
合計金額: ¥300
牛乳 をカートに追加しました。
買い物籠の中身:
リンゴ: ¥100
パン: ¥200
牛乳: ¥150
合計金額: ¥450
パン をカートから削除しました。
買い物籠の中身:
リンゴ: ¥100
牛乳: ¥150
合計金額: ¥250

これにより、ShoppingBasketItemを保持し、追加・削除・合計金額の計算が正常に行われていることが確認できます。


コンポジションのメリット

C#コンソールアプリケーションにおけるメリット

  1. 再利用性の向上Itemクラスは他のクラスでも再利用可能です。
  2. 柔軟性ShoppingBasketに含まれるItemの種類や数を簡単に変更できます。
  3. 保守性:各クラスが独立しているため、修正や拡張が容易です。

Unityにおけるメリット

  1. 再利用性の向上ItemScriptableObjectとして独立しているため、他のシステムでも再利用可能です。
  2. 柔軟性ShoppingBasketに含まれるItemの種類や数をエディター上で簡単に変更できます。
  3. 保守性:各コンポーネントが独立しているため、修正や拡張が容易です。例えば、新しい機能を追加する際にも影響範囲が限定されます。
  4. データ管理の容易さScriptableObjectを使用することで、データの管理や編集がエディター上で直感的に行えます。

まとめ

本資料では、買い物籠(ショッピングカート)を題材に、C#コンソールアプリケーションおよびUnity環境でのコンポジション(Composition)の概念と実装方法について解説しました。コンポジションを適切に活用することで、再利用性・柔軟性・保守性の高いコード設計が可能となります。

C#コンソールアプリケーションでは、ItemShoppingBasketクラスを通じてコンポジションの基本を理解しました。一方、Unity環境では、ScriptableObjectMonoBehaviourを活用し、エディター上でのデータ管理とコンポーネントの組み合わせによる柔軟な設計を実現しました。

オブジェクト指向プログラミングを学ぶ際には、コンポジションの理解と実践が非常に重要です。今後のプロジェクトにおいて、本資料を参考にコンポジションを活用し、効率的で保守性の高いコードを目指してください。


付録

コード全体一覧

C#コンソールアプリケーション

Item.cs

public class Item
{
    public string Name { get; set; }
    public decimal Price { get; set; }

    public Item(string name, decimal price)
    {
        Name = name;
        Price = price;
    }

    public override string ToString()
    {
        return $"{Name}: ¥{Price}";
    }
}

ShoppingBasket.cs

using System;
using System.Collections.Generic;

public class ShoppingBasket
{
    private List<Item> items;

    public ShoppingBasket()
    {
        items = new List<Item>();
    }

    // アイテムを追加するメソッド
    public void AddItem(Item item)
    {
        items.Add(item);
    }

    // アイテムを削除するメソッド
    public void RemoveItem(Item item)
    {
        items.Remove(item);
    }

    // 合計金額を計算するメソッド
    public decimal GetTotalPrice()
    {
        decimal total = 0;
        foreach (var item in items)
        {
            total += item.Price;
        }
        return total;
    }

    // 買い物籠の内容を表示するメソッド
    public void DisplayBasket()
    {
        Console.WriteLine("買い物籠の中身:");
        foreach (var item in items)
        {
            Console.WriteLine(item);
        }
        Console.WriteLine($"合計金額: ¥{GetTotalPrice()}");
    }
}

Program.cs

using System;

class Program
{
    static void Main(string[] args)
    {
        // 商品を作成
        Item apple = new Item("リンゴ", 100);
        Item bread = new Item("パン", 200);
        Item milk = new Item("牛乳", 150);

        // 買い物籠を作成
        ShoppingBasket basket = new ShoppingBasket();

        // アイテムを買い物籠に追加
        basket.AddItem(apple);
        basket.AddItem(bread);
        basket.AddItem(milk);

        // 買い物籠の内容を表示
        basket.DisplayBasket();

        // 商品を削除
        basket.RemoveItem(bread);

        // 再度表示
        Console.WriteLine("\nパンを削除後:");
        basket.DisplayBasket();
    }
}

Unity環境

Item.cs

using UnityEngine;

[CreateAssetMenu(fileName = "NewItem", menuName = "Shopping/Item")]
public class Item : ScriptableObject
{
    public string itemName;
    public float price;

    public override string ToString()
    {
        return $"{itemName}: ¥{price}";
    }
}

ShoppingBasket.cs

using System.Collections.Generic;
using UnityEngine;

public class ShoppingBasket : MonoBehaviour
{
    private List<Item> items = new List<Item>();

    // アイテムを追加するメソッド
    public void AddItem(Item item)
    {
        items.Add(item);
        Debug.Log($"{item.itemName} をカートに追加しました。");
        DisplayBasket();
    }

    // アイテムを削除するメソッド
    public void RemoveItem(Item item)
    {
        if (items.Remove(item))
        {
            Debug.Log($"{item.itemName} をカートから削除しました。");
            DisplayBasket();
        }
        else
        {
            Debug.Log($"{item.itemName} はカートに存在しません。");
        }
    }

    // 合計金額を計算するメソッド
    public float GetTotalPrice()
    {
        float total = 0f;
        foreach (var item in items)
        {
            total += item.price;
        }
        return total;
    }

    // 買い物籠の内容を表示するメソッド
    public void DisplayBasket()
    {
        Debug.Log("買い物籠の中身:");
        foreach (var item in items)
        {
            Debug.Log(item.ToString());
        }
        Debug.Log($"合計金額: ¥{GetTotalPrice()}");
    }
}

BasketTester.cs

using UnityEngine;

public class BasketTester : MonoBehaviour
{
    public ShoppingBasket shoppingBasket;
    public Item apple;
    public Item bread;
    public Item milk;

    void Start()
    {
        if (shoppingBasket == null)
        {
            Debug.LogError("ShoppingBasket が割り当てられていません!");
            return;
        }

        // アイテムをカートに追加
        shoppingBasket.AddItem(apple);
        shoppingBasket.AddItem(bread);
        shoppingBasket.AddItem(milk);

        // アイテムを削除
        shoppingBasket.RemoveItem(bread);
    }
}

以上が、買い物籠を題材としたC#およびUnityでのコンポジションの技術資料です。これらの例を通じて、コンポジションの基本的な理解と実装方法を習得し、実際のプロジェクトに応用してください。