基礎から学ぶC#入門 90日コース | 継承・抽象化・インターフェース - Day 49:オブジェクト指向設計演習

C# 90日で身につけるC#
スポンサーリンク
スポンサーリンク

Day 49 のゴールと全体像

Day 49 のテーマは オブジェクト指向設計演習:ユーザー管理システムです。

登場人物はこの3つです。

  • User
  • AdminUser
  • GeneralUser

ゴールは、

「それぞれの役割をきちんと設計し、 共通部分と違いをオブジェクト指向で表現する」

ことです。

ここまで学んできた、

  • 継承
  • 多態性
  • 抽象化

といった考え方を、 「ユーザー管理」という身近なテーマに落とし込んでいきます。

まずは役割を言葉で整理する

User という「共通の概念」

最初に、「User」という言葉を分解してみます。

どんなユーザーであっても、

  • ユーザーID
  • 名前
  • メールアドレス

といった 共通の情報を持っていそうです。

また、

  • ログインする
  • 自分の情報を表示する

といった 共通の振る舞いもありそうです。

これらは、 「User という概念の共通部分」として、 基底クラスにまとめるのが自然です。

AdminUser と GeneralUser の違い

次に、管理者ユーザーと一般ユーザーの違いを考えます。

  • AdminUser
    • システム全体の設定を変更できる
    • 他のユーザーを追加・削除できる
    • 権限が強い
  • GeneralUser
    • 自分の情報を閲覧・変更できる
    • 一般的な機能だけ使える
    • 権限は限定的

つまり、

「共通部分は User にまとめ、 役割ごとの違いは AdminUser / GeneralUser に分ける」

という設計が見えてきます。

ステップ1:基底クラス User を設計する

User クラスの基本設計

まずは、共通部分を表す User クラスを作ります。

using System;

// ユーザーの共通部分を表す基底クラス
abstract class User
{
    // 共通のプロパティ
    public int Id { get; }
    public string Name { get; }
    public string Email { get; }

    // コンストラクタで共通情報を受け取る
    protected User(int id, string name, string email)
    {
        Id = id;
        Name = name;
        Email = email;
    }

    // 共通の振る舞い:ユーザー情報を表示する
    public void ShowProfile()
    {
        Console.WriteLine($"ID   : {Id}");
        Console.WriteLine($"名前 : {Name}");
        Console.WriteLine($"メール: {Email}");
    }

    // 役割ごとに違う振る舞い:権限を表示する(抽象メソッド)
    public abstract void ShowRole();

    // 役割ごとに違う振る舞い:利用可能な機能を表示する(抽象メソッド)
    public abstract void ShowAvailableActions();
}
C#

ここでの重要ポイントは、

  • User抽象クラスにしていること
    • 直接 new User(...) はさせない
    • 「ユーザーという概念」を表すためのクラス
  • 共通プロパティ(Id, Name, Email)を持っていること
  • 共通メソッド ShowProfile() を持っていること
  • 役割ごとに違う部分(権限・利用可能な機能)は abstract メソッドとして宣言していること

つまり、

「User は、ユーザーの共通仕様+共通処理をまとめた設計図」

という位置づけになります。

ステップ2:AdminUser を設計する

管理者ユーザーの役割をコードに落とす

管理者ユーザーは、

  • 権限が強い
  • システム全体を操作できる

という特徴があります。

// 管理者ユーザー
class AdminUser : User
{
    public AdminUser(int id, string name, string email)
        : base(id, name, email)
    {
    }

    // 権限の表示(管理者用)
    public override void ShowRole()
    {
        Console.WriteLine("ロール: 管理者ユーザー(AdminUser)");
    }

    // 利用可能な機能の表示(管理者用)
    public override void ShowAvailableActions()
    {
        Console.WriteLine("利用可能な機能:");
        Console.WriteLine(" - ユーザー一覧の閲覧");
        Console.WriteLine(" - ユーザーの追加・削除");
        Console.WriteLine(" - システム設定の変更");
    }

    // 管理者専用のメソッド例:ユーザーを削除する
    public void DeleteUser(User target)
    {
        Console.WriteLine($"ユーザー「{target.Name}」を削除しました。(実際の削除処理は省略)");
    }
}
C#

ここでのポイントは、

  • User を継承していること
  • ShowRoleShowAvailableActions を 「管理者らしい内容」でオーバーライドしていること
  • 管理者専用のメソッド(DeleteUser)を持っていること

重要ポイント:

  • 共通部分は User にまとめ、 管理者ならではの振る舞いは AdminUser に閉じ込める

という役割分担ができています。

ステップ3:GeneralUser を設計する

一般ユーザーの役割をコードに落とす

一般ユーザーは、

  • 自分の情報を扱う
  • 一般的な機能だけ使える

という特徴があります。

// 一般ユーザー
class GeneralUser : User
{
    public GeneralUser(int id, string name, string email)
        : base(id, name, email)
    {
    }

    // 権限の表示(一般ユーザー用)
    public override void ShowRole()
    {
        Console.WriteLine("ロール: 一般ユーザー(GeneralUser)");
    }

    // 利用可能な機能の表示(一般ユーザー用)
    public override void ShowAvailableActions()
    {
        Console.WriteLine("利用可能な機能:");
        Console.WriteLine(" - 自分のプロフィールの閲覧");
        Console.WriteLine(" - 自分のプロフィールの編集");
        Console.WriteLine(" - 一般機能の利用");
    }

    // 一般ユーザー専用のメソッド例:プロフィールを更新する
    public void UpdateProfile(string newName, string newEmail)
    {
        Console.WriteLine($"プロフィール更新(仮): 名前を「{Name}」から「{newName}」へ、メールを「{Email}」から「{newEmail}」へ変更します。");
        // 実際にはプロパティを変更する処理などを書く
    }
}
C#

ここでも、

  • User を継承している
  • ShowRoleShowAvailableActions を 「一般ユーザーらしい内容」でオーバーライドしている
  • 一般ユーザー専用のメソッド(UpdateProfile)を持っている

という構造になっています。

ステップ4:多態性で「ユーザーとしてまとめて扱う」

User 型のリストで AdminUser と GeneralUser を混ぜて扱う

オブジェクト指向の設計が生きてくるのは、 「共通の型としてまとめて扱う」場面です。

using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        // AdminUser と GeneralUser をまとめて User 型のリストに入れる
        List<User> users = new List<User>
        {
            new AdminUser(1, "管理者 太郎", "admin@example.com"),
            new GeneralUser(2, "一般 花子", "user@example.com"),
            new GeneralUser(3, "一般 次郎", "user2@example.com")
        };

        Console.WriteLine("=== ユーザー一覧 ===");

        foreach (var user in users)
        {
            // 共通情報の表示
            user.ShowProfile();
            // 役割ごとの違い(多態性)
            user.ShowRole();
            user.ShowAvailableActions();
            Console.WriteLine();
        }

        Console.ReadLine();
    }
}
C#

ここでの流れはこうです。

  • リストの型は List<User>
  • 中身は AdminUserGeneralUser が混在している
  • 呼び出しているメソッドは常に
    • ShowProfile(共通処理)
    • ShowRole(役割ごとに違う)
    • ShowAvailableActions(役割ごとに違う)

という形になっています。

重要ポイント:

  • 「ユーザーとして扱う」コードは、 User 型だけを意識して書けばよい
  • 実際にどう振る舞うかは、 AdminUser / GeneralUser の実装に任せられる

これが、 Day 45 で学んだ 多態性が、 ユーザー管理システムの設計に生きている瞬間です。

ステップ5:セキュリティ視点を少しだけ加える

「誰が何をできるか」をコードで表現する

セキュリティの観点から見ると、

「管理者だけができる操作」 「一般ユーザーには許可しない操作」

を、コードの構造で表現しておくことはとても大事です。

たとえば、

  • DeleteUserAdminUser にだけ存在する
  • GeneralUser には DeleteUser が存在しない

という設計にしておくと、

  • 一般ユーザーのインスタンスからは、 そもそも削除メソッドを呼べない
  • 「誤って一般ユーザーから危険な操作を呼んでしまう」 というバグを防ぎやすくなる

というメリットがあります。

役割ごとの責務を明確にする

  • AdminUser → 「システム全体を管理する責務」を持つ
  • GeneralUser → 「自分の情報を扱う責務」を持つ

というふうに、 「責務(何をする役割か)」をクラスごとに分けることは、 セキュリティ的にも設計的にも、とても重要です。

ステップ6:ユーザー管理システムの基本テンプレート

最後に、 今回の設計をテンプレートとしてまとめておきます。

抽象基底クラス User

abstract class User
{
    public int Id { get; }
    public string Name { get; }
    public string Email { get; }

    protected User(int id, string name, string email)
    {
        Id = id;
        Name = name;
        Email = email;
    }

    public void ShowProfile()
    {
        Console.WriteLine($"ID   : {Id}");
        Console.WriteLine($"名前 : {Name}");
        Console.WriteLine($"メール: {Email}");
    }

    public abstract void ShowRole();
    public abstract void ShowAvailableActions();
}
C#

管理者ユーザー AdminUser

class AdminUser : User
{
    public AdminUser(int id, string name, string email)
        : base(id, name, email)
    {
    }

    public override void ShowRole()
    {
        Console.WriteLine("ロール: 管理者ユーザー");
    }

    public override void ShowAvailableActions()
    {
        Console.WriteLine("利用可能な機能:");
        Console.WriteLine(" - ユーザー一覧の閲覧");
        Console.WriteLine(" - ユーザーの追加・削除");
        Console.WriteLine(" - システム設定の変更");
    }

    public void DeleteUser(User target)
    {
        Console.WriteLine($"ユーザー「{target.Name}」を削除しました。(仮処理)");
    }
}
C#

一般ユーザー GeneralUser

class GeneralUser : User
{
    public GeneralUser(int id, string name, string email)
        : base(id, name, email)
    {
    }

    public override void ShowRole()
    {
        Console.WriteLine("ロール: 一般ユーザー");
    }

    public override void ShowAvailableActions()
    {
        Console.WriteLine("利用可能な機能:");
        Console.WriteLine(" - 自分のプロフィールの閲覧");
        Console.WriteLine(" - 自分のプロフィールの編集");
        Console.WriteLine(" - 一般機能の利用");
    }

    public void UpdateProfile(string newName, string newEmail)
    {
        Console.WriteLine($"プロフィール更新(仮): 名前を「{Name}」から「{newName}」へ、メールを「{Email}」から「{newEmail}」へ変更します。");
    }
}
C#

使う側(簡易ユーザー管理)

using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        List<User> users = new List<User>
        {
            new AdminUser(1, "管理者 太郎", "admin@example.com"),
            new GeneralUser(2, "一般 花子", "user@example.com"),
            new GeneralUser(3, "一般 次郎", "user2@example.com")
        };

        Console.WriteLine("=== ユーザー一覧 ===");
        foreach (var user in users)
        {
            user.ShowProfile();
            user.ShowRole();
            user.ShowAvailableActions();
            Console.WriteLine();
        }

        // 管理者による削除操作の例
        var admin = (AdminUser)users[0];
        admin.DeleteUser(users[2]);

        Console.ReadLine();
    }
}
C#

Day 49 のまとめ

Day 49 では、

  • User → ユーザーの共通仕様と共通処理をまとめた抽象基底クラス
  • AdminUser → システム全体を管理する役割を持つユーザー
  • GeneralUser → 自分の情報を扱う一般的なユーザー

という3つのクラスを通して、

  • 共通部分を基底クラスにまとめる
  • 役割ごとの違いを派生クラスに分ける
  • 多態性で「ユーザーとしてまとめて扱う」

というオブジェクト指向設計の流れを実践しました。

ユーザー管理システムは、 実際のアプリでもよく登場するテーマです。

ぜひ、

  • 自分で「権限の種類」を増やしてみる(例:ReadOnlyUser など)
  • ユーザー管理用のサービスクラスを作ってみる
  • ログ出力や認証処理をインターフェースで分離してみる

といった小さな拡張を通して、 オブジェクト指向設計を 「教科書の概念」ではなく、 自分のアプリを形にするための具体的な技術として、 少しずつ育てていっていただければと思います。

タイトルとURLをコピーしました