Day 49 のゴールと全体像
Day 49 のテーマは オブジェクト指向設計演習:ユーザー管理システムです。
登場人物はこの3つです。
UserAdminUserGeneralUser
ゴールは、
「それぞれの役割をきちんと設計し、 共通部分と違いをオブジェクト指向で表現する」
ことです。
ここまで学んできた、
- 継承
- 多態性
- 抽象化
といった考え方を、 「ユーザー管理」という身近なテーマに落とし込んでいきます。
まずは役割を言葉で整理する
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を継承していることShowRoleとShowAvailableActionsを 「管理者らしい内容」でオーバーライドしていること- 管理者専用のメソッド(
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を継承しているShowRoleとShowAvailableActionsを 「一般ユーザーらしい内容」でオーバーライドしている- 一般ユーザー専用のメソッド(
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> - 中身は
AdminUserとGeneralUserが混在している - 呼び出しているメソッドは常に
ShowProfile(共通処理)ShowRole(役割ごとに違う)ShowAvailableActions(役割ごとに違う)
という形になっています。
重要ポイント:
- 「ユーザーとして扱う」コードは、
User型だけを意識して書けばよい - 実際にどう振る舞うかは、
AdminUser/GeneralUserの実装に任せられる
これが、 Day 45 で学んだ 多態性が、 ユーザー管理システムの設計に生きている瞬間です。
ステップ5:セキュリティ視点を少しだけ加える
「誰が何をできるか」をコードで表現する
セキュリティの観点から見ると、
「管理者だけができる操作」 「一般ユーザーには許可しない操作」
を、コードの構造で表現しておくことはとても大事です。
たとえば、
DeleteUserはAdminUserにだけ存在する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など) - ユーザー管理用のサービスクラスを作ってみる
- ログ出力や認証処理をインターフェースで分離してみる
といった小さな拡張を通して、 オブジェクト指向設計を 「教科書の概念」ではなく、 自分のアプリを形にするための具体的な技術として、 少しずつ育てていっていただければと思います。
