Day 39 のゴールと全体像
Day 39 のテーマは 「複数クラスの連携」 です。 ここまでで、1つのクラスの中に「データ」と「処理」をまとめる感覚はだいぶ育ってきたと思います。
今日はそこから一歩進んで、
- Program
- Service
- Model
という 3 つの役割に分けて、 クラス同士を連携させる「小さな設計」を体験していきます。
ざっくり言うと、
Program:アプリの入口・流れを決める Service:具体的な仕事を担当する Model:データの形を表す
という役割分担を、 シンプルな例で感じてもらう回です。
3つの役割をざっくりイメージする
Model:データの「型」を表すクラス
まずは Model からいきます。
Model は、
「アプリの中で扱うデータの形を表すクラス」
です。
たとえば、前回の Product クラスは、 まさに「商品というデータのモデル」でした。
シンプルな例として、 ユーザー情報を表す User モデルを作ってみます。
// Model:データの形を表すクラス
class User
{
// ユーザーID
public int Id { get; }
// 名前
public string Name { get; }
// メールアドレス
public string Email { get; }
public User(int id, string name, string email)
{
Id = id;
Name = name;
Email = email;
}
// 自分の情報を表示するメソッド(Model によくある補助的な処理)
public void PrintInfo()
{
Console.WriteLine($"ID: {Id}, 名前: {Name}, メール: {Email}");
}
}
C#ここでは、
Userは「ユーザーというデータの形」を表している- データの整合性(ID・名前・メール)をコンストラクタで保証している
という役割を持っています。
Service:具体的な仕事を担当するクラス
次に Service です。
Service は、
「アプリの中で行いたい具体的な処理を担当するクラス」
です。
例えば、
- ユーザーを登録する
- ユーザー一覧を表示する
- ユーザーを検索する
といった「仕事」をまとめて持つクラスが、Service になります。
先ほどの User モデルを使って、 UserService を作ってみます。
using System;
using System.Collections.Generic;
using System.Linq;
// Service:ユーザーに関する処理を担当するクラス
class UserService
{
// ユーザー一覧(簡易的にメモリ上の List で管理)
private readonly List<User> users = new List<User>();
// ユーザーを追加する
public void AddUser(User user)
{
users.Add(user);
Console.WriteLine($"ユーザー「{user.Name}」を追加しました。");
}
// 全ユーザーを表示する
public void PrintAllUsers()
{
Console.WriteLine("ユーザー一覧:");
foreach (var user in users)
{
user.PrintInfo();
}
}
// ID でユーザーを検索する
public User? FindById(int id)
{
return users.FirstOrDefault(u => u.Id == id);
}
}
C#ここでは、
UserServiceは「ユーザーに関する仕事」を担当している- データの実体(
User)は Model に任せている - 複数の
Userをまとめて扱う処理を持っている
という構造になっています。
Program:アプリの入口と全体の流れ
最後に Program です。
Program クラス(Main メソッドがあるところ)は、
「アプリの入口であり、全体の流れを組み立てる場所」
です。
ここでは、
- どの Service を使うか
- どの Model を作るか
- どんな順番で処理を呼び出すか
といった「シナリオ」を書いていきます。
先ほどの User と UserService を使って、 Program を書いてみます。
class Program
{
static void Main(string[] args)
{
// Service を用意する
UserService userService = new UserService();
// Model を作って Service に渡す
User user1 = new User(1, "太郎", "taro@example.com");
User user2 = new User(2, "花子", "hanako@example.com");
userService.AddUser(user1);
userService.AddUser(user2);
// 一覧表示
userService.PrintAllUsers();
// 検索してみる
User? found = userService.FindById(2);
if (found != null)
{
Console.WriteLine("ID 2 のユーザーが見つかりました:");
found.PrintInfo();
}
else
{
Console.WriteLine("ID 2 のユーザーは見つかりませんでした。");
}
Console.ReadLine();
}
}
C#ここでは、
Programは「アプリの流れ」を書いているUserServiceに「ユーザーを追加して」「一覧を表示して」「検索して」とお願いしているUserは「ユーザーというデータの形」として使われている
という役割分担になっています。
Program → Service → Model の流れを整理する
矢印の意味を具体的にイメージする
Day 39 のキーワードは、
Program ↓ Service ↓ Model
でした。
先ほどの例を、この矢印に当てはめてみると、
- Program
Mainメソッドでアプリの流れを決めるUserServiceを作って使うUserを作ってUserServiceに渡す
- Service
UserServiceが「ユーザーに関する処理」を担当するUser(Model)を使って仕事をする
- Model
Userが「ユーザーというデータの形」を表す
という関係になっています。
矢印の向きは、
- Program が Service を使う
- Service が Model を使う
という「上から下への依存関係」を表しています。
重要ポイント:
- Program は「全体の流れ」を書く場所
- Service は「具体的な仕事」をまとめる場所
- Model は「データの形」を表す場所
という役割分担を意識すると、 コードの見通しがぐっと良くなります。
なぜ役割を分けると楽になるのか
1つのクラスに全部詰め込むとどうなるか
もし、Program の中に、
- ユーザーのデータ
- ユーザー一覧の管理
- 検索処理
- 表示処理
を全部書いてしまうと、 Main メソッドがどんどん長くなり、
「どこで何をしているのか分からない巨大メソッド」
になりがちです。
役割を分けることで、
- データのことは Model に聞けばいい
- ユーザーに関する処理は UserService を見ればいい
- アプリ全体の流れは Program を見ればいい
というふうに、 「見るべき場所」がはっきりします。
修正や拡張がしやすくなる
例えば、
- ユーザーの保存先を「メモリ上の List」から「ファイル」や「データベース」に変えたい
- ユーザーの項目を増やしたい(住所や電話番号など)
- ユーザー一覧の表示方法を変えたい
といった変更が入ったとき、
- 保存方法の変更 → Service を中心に直す
- 項目の追加 → Model を中心に直す
- 表示方法の変更 → Service や Program を中心に直す
というふうに、 「どこを直せばいいか」が分かりやすくなります。
重要ポイント:
- 役割分担は「後から直しやすくするための工夫」でもあります。
- 小さなプログラムでも、早めにこの感覚を持っておくと、 大きなプログラムに進んだときに楽になります。
Day 39 用の練習テンプレート
最後に、Program → Service → Model の流れを練習するための、 シンプルなテンプレートを紹介します。
商品管理のミニ例
using System;
using System.Collections.Generic;
// Model:商品データの形
class Product
{
public int Id { get; }
public string Name { get; }
public int Price { get; }
public int Stock { get; private set; }
public Product(int id, string name, int price, int stock)
{
Id = id;
Name = name;
Price = price;
Stock = stock;
}
public void PrintInfo()
{
Console.WriteLine($"ID: {Id}, 名前: {Name}, 価格: {Price} 円, 在庫: {Stock}");
}
public void AddStock(int amount)
{
Stock += amount;
}
public void ReduceStock(int amount)
{
if (amount > Stock)
{
Console.WriteLine("在庫不足です。");
return;
}
Stock -= amount;
}
}
// Service:商品に関する処理を担当
class ProductService
{
private readonly List<Product> products = new List<Product>();
public void AddProduct(Product product)
{
products.Add(product);
Console.WriteLine($"商品「{product.Name}」を追加しました。");
}
public void PrintAllProducts()
{
Console.WriteLine("商品一覧:");
foreach (var p in products)
{
p.PrintInfo();
}
}
public Product? FindById(int id)
{
foreach (var p in products)
{
if (p.Id == id)
{
return p;
}
}
return null;
}
}
// Program:アプリの入口と流れ
class Program
{
static void Main(string[] args)
{
ProductService productService = new ProductService();
// Model を作って Service に渡す
Product notebook = new Product(1, "ノート", 100, 50);
Product pen = new Product(2, "ボールペン", 150, 30);
productService.AddProduct(notebook);
productService.AddProduct(pen);
productService.PrintAllProducts();
// ID 2 の商品を探して在庫を減らす
Product? found = productService.FindById(2);
if (found != null)
{
Console.WriteLine("ID 2 の商品が見つかりました。1 個販売します。");
found.ReduceStock(1);
found.PrintInfo();
}
Console.ReadLine();
}
}
C#このテンプレートを眺めながら、
- Model(
Product)は「商品というデータの形」を表している - Service(
ProductService)は「商品一覧の管理や検索」を担当している - Program は「どの商品を作って、どう扱うか」という流れを書いている
という役割分担を意識してみると、 複数クラスの連携のイメージがぐっとクリアになります。
Day 39 のまとめ
Day 39 では、
- Model:データの形を表すクラス
- Service:具体的な仕事を担当するクラス
- Program:アプリの入口と全体の流れを組み立てるクラス
という 3 つの役割を通して、
Program ↓ Service ↓ Model
という「小さな設計の階層」を体験しました。
この考え方は、
- Webアプリ
- デスクトップアプリ
- モバイルアプリ
など、どんな種類のアプリでもよく使われる基本パターンです。
ぜひ、
- 自分で小さなテーマ(タスク管理、メモ帳、図書管理など)を決めて
- Model・Service・Program に分けてクラスを作ってみる
- 「この処理はどのクラスの責務か?」を考えながらコードを書く
という練習を通して、 複数クラスの連携を 「難しい設計」ではなく、 役割分担を考える楽しい作業として、少しずつ体に馴染ませていってください。
