C#でのオブジェクト指向学習サンプル1(レジ基本)

2023年7月26日

スーパーのレジシステムをコンソールアプリでシミュレートして、オブジェクト指向のメリットをみていきましょう

オブジェクト指向の初学では、オブジェクト指向の書き方も大切ですが、まず、何がメリットなのか、なぜ使うのか、自分が理解しやすい例えで学習しているかに着目して進めるといいでしょう

無闇に時間をかけるより、イメージを掴むように心がけましょう

実行結果

お客(山田さん)が買い物かごに買いたい商品を入れていきます
必要なものを入れたらレジで精算(レシート表示)します

手続型でいいのでは?

この実行結果からコードを書くとすると手続型(変数、条件分岐、繰り返し)でシンプルにコードが書けますし、特に難しく感じないと思った方も多いでしょう

今回の学習の着地となるイメージ

基本のサンプルが動かないと話にならないので、山田さんだけがお客のように見えるものになっていますが、他のお客にも対応できるようにすることを想定することが大事ですね(レジですから・・・)

次のコードは難しく感じると思いますが、バラバラにお客がやってきて購入し、バラバラにレジで精算する様子を表しています

さらにお客を増やすこともできそうですね

手続型ではお客が増えると同じようにしようとするとごちゃごちゃすることが想定できます

・・・頑張って、手続型でコードを書いてみますか??? >> 大変そうですね

// レジオブジェクトを作成
Reg reg1 = new Reg();

// 山田さんを作成
Customer customer1 = new Customer("山田");
Customer customer2 = new Customer("森本");
Customer customer3 = new Customer("太田");

// 森本さんが先に購入
customer2.Buy();
// 森本さんがそのまま清算
reg1.PrintReceipt(customer2);
customer1.Buy();
customer3.Buy();

reg1.PrintReceipt(customer3);
reg1.PrintReceipt(customer1);

イメージ

クラス図

上記のイメージを図式で表したものになります
イラストはありませんが、各クラス名、属性名(フィールド名)、振る舞い名(メソッド名)が記されています

四角形は3段に分かれている上段がクラス名(©️はクラスを表します)、中段が属性下段が振る舞いを表しています

メソッドの()内は引数になります

分析の方法

UMLのクラス図を習得されていないことを前提に説明します

レジシステム(全体のコントロール)で、お客とレジのインスタンスを作成しています
Runメソッドには、お客がアイテムを購入することとレジでレシートを表示するよう指示されています

お客は、複数のアイテムを購入することができます
また、レジを使うことができます
ここで使うとは、レジのレシート表示メソッドを呼び出すことを指します

creates

インスタンスの作成を指します

has

持っている様子(保持、コンポジション)を指します

uses

利用することを表します

シーケンス図

このシーケンス図では、ProgramクラスがRegSystemRun()メソッドを呼び出すことで、Customerクラスのインスタンスを作成し、Buy()メソッドを呼び出して買い物の情報を入力します。その後、RegクラスのPrintReceipt()メソッドを呼び出してレシートを表示しています。

アクティビティ図

このアクティビティ図は、次のようなフローを表しています。

  1. プログラムが開始すると、顧客インスタンスが作成されます(Customerクラスのコンストラクタ呼び出し)。
  2. 顧客は商品の数を入力します(Buyメソッド内のループで商品名と価格が入力されます)。
  3. 入力された商品情報をもとに、レジシステムが実行されます(RegSystemクラスのRunメソッド内で顧客のインスタンスを作成し、RegクラスのPrintReceiptメソッドでレシートを表示)。
  4. レシートが表示され、プログラムが終了します。

コード

C#の初学であれば、次のポイントに注意して確認してください

クラスは中を2つの部分に分けて分析してみます

属性(データ、フィールドともいいます)と振る舞い(動作の部分でメソッドや関数とも言います)の2つです

メソッドは、ブロック内を全て理解することを目的にせず、メソッド名から何の動作をさせようとしているのかを分析対象とします

Item.cs

商品クラスになります

属性

商品名価格の2つで考えます

振る舞い

商品名と価格を表示するのみになります

// Itemクラス(設計図)
/// <summary>
/// スーパーの商品クラス
/// </summary>
class Item
{
    // (フィールド)
    // 商品名
    public string name;
    // 価格
    public int price;

    // ------------------------------------

    // (メソッド、関数、プロシジャー、ファンクション)
    // 商品名と価格を表示する
    public void Show()
    {
        Console.WriteLine($"{name}の価格は{price}円です");
    }
}

Customer.cs

お客クラスになります
商品を複数持っていることを表しています

属性

お客様名複数の商品の2つで考えます

振る舞い

インスタンス作成時名前を登録する購入するの2つになります

class Customer
{
    public string name;

    // カートの商品
    public Item[] items;

    public Customer(string name)
    {
        this.name = name;
    }

    public void Buy()
    {
        // Itemクラス(設計図)からインスタンスを作成する(オブジェクトを作成)
        // =>インスタンス化ともいいます

        // 商品の数を入力する
        Console.Write($"{name}さんの買い物かごの商品の数は? ");

        int itemCount = int.Parse(Console.ReadLine());

        items = new Item[itemCount];

        for (int i = 0; i < itemCount; i++)
        {
            Console.Write("商品名: ");
            string itemName = Console.ReadLine();

            Console.Write("価格: ");
            int itemPrice = int.Parse(Console.ReadLine());

            items[i] = new Item { name = itemName, price = itemPrice };
        }

    }

}

Reg.cs

レジクラスになります

属性

ありません

レシートが記されていますが、表示用のため今回は考えなくていいでしょう

振る舞い

レシートを表示するのみになります

class Reg 
{
    // レシートを表示する
    public void PrintReceipt(Customer customer)
    {
        Console.WriteLine($"{customer.name}さんのレシート");
        Console.WriteLine("----------------------------");

        // レシートの表示
        for (int i = 0; i < customer.items.Length; i++)
        {
            // 商品名を表示する
            Console.WriteLine($"商品{i + 1}: {customer.items[i].name} {customer.items[i].price}円");
        }

        // 合計金額を入れる変数を用意する
        int totalPrice = 0;

        // 商品の数だけ繰り返す
        for (int i = 0; i < customer.items.Length; i++)
        {
            // 合計金額に商品の価格を足していく
            totalPrice += customer.items[i].price;
        }

        Console.WriteLine("----------------------------");

        // 合計を表示する
        Console.WriteLine($"合計金額は{totalPrice}円です");
    }

}

RegSystem.cs

レジのシステム全体をコントロールするクラスになります

属性

ありません

振る舞い

Runメソッドからコードが始まります

ここでは、お客様の山田さんの作成買い物をするレジの作成レシートを表示するを行なっています

class RegSystem 
{
    public void Run()
    {
        Customer customer1 = new Customer("山田");

        customer1.Buy();

        Reg reg1 = new Reg();

        reg1.PrintReceipt(customer1);
    }
}

Program.cs

C#でプログラムが実行される最初のメソッドになります

属性

ありません

振る舞い

ここでは、レジシステムの作成レジの実行を行なっています

RegSystem regSystem = new RegSystem();
regSystem.Run();

おまけ

PlantUML記法


@startuml

class Program{
  -Main()
}
class RegSystem {
    + Run()
}

class Customer {
    - name: string
    - items: Item[]
    + Customer(name: string)
    + Buy()
}

class Item {
    - name: string
    - price: int
    + Show()
}

class Reg {
    + PrintReceipt(customer: Customer)
}

Program --> RegSystem : creates
RegSystem --> Customer : creates
RegSystem --> Reg : creates
Customer "1" --> "*" Item : has
Customer --> Reg : uses

@enduml

@startuml
actor Customer

Program -> RegSystem: new RegSystem()
Program -> RegSystem: Run()
RegSystem -> Customer: new Customer("山田")
Customer -> Customer: Buy()
Customer -> Console: "山田さんの買い物かごの商品の数は?"
Console -> Customer: itemCount
loop itemCount
    Customer -> Console: "商品名:"
    Console -> Customer: itemName
    Customer -> Console: "価格:"
    Console -> Customer: ItemPrice
    Customer -> Item: new Item(itemName, ItemPrice)
end
Customer -> Customer: items = Item[]
RegSystem -> Reg: new Reg()
Reg -> Reg: PrintReceipt(customer1)
Reg -> Console: "山田さんのレシート"
Reg -> Console: "----------------------------"
loop customer1.items.Length
    Reg -> Console: "商品i + 1: " + customer1.items[i].name + " " + customer1.items[i].price + "円"
end
Reg -> Reg: totalPrice = 0
loop customer1.items.Length
    Reg -> Reg: totalPrice += customer1.items[i].price
end
Reg -> Console: "----------------------------"
Reg -> Console: "合計金額は" + totalPrice + "円です"
@enduml

@startuml
skinparam shadowing false

start

:顧客インスタンスを作成;
:買い物かごの商品数を入力;
while (商品数 > 0)
    :商品名を入力;
    :価格を入力;
endwhile

:レジシステムを実行;

:レシートを表示;

stop

@enduml