Day 68 のゴールと全体像
Day 68 では、「コードを書く力」から一歩進んで 「コードを整理する力」 を育てていきます。 今日のキーワードは次の4つです。
- 名前空間
- フォルダ構成
- クラス分割
- 責務分離
どれも、アプリが少しずつ大きくなってきたときに 「ごちゃごちゃしてきた…」「どこに何を書けばいいのか分からない…」 というモヤモヤを解消してくれる、大事な考え方たちです。
ここでは、
- 小さなサンプルアプリをイメージしながら
- 名前空間 → フォルダ → クラス分割 → 責務分離
という順番で、ステップバイステップで整理の感覚をつかんでいきます。
名前空間とは何か
名前空間は「コードの住所」
C# の namespace は、ひと言でいうと 「コードの住所」 です。
namespace MyApp.Models
{
class User
{
public string Name { get; set; }
}
}
C#この User クラスは、
- アプリ名:
MyApp - 区画:
Models
という「住所」を持っているイメージです。
名前空間を使うことで、
- クラス名が同じでも、別の名前空間なら共存できる
- 「このあたりはデータモデル」「このあたりはサービス」といった “コードのエリア分け”がしやすくなる
というメリットがあります。
よくある名前空間のパターン
小さめのアプリでも、次のような名前空間構成がよく使われます。
MyApp
├─ Models … データの形(User, Product など)
├─ Services … 処理・ロジック(UserService など)
├─ Repositories… データの保存・読み込み
└─ UI … 画面やコンソール表示
これを C# のコードにすると、
namespace MyApp.Models
{
class User { /* … */ }
}
namespace MyApp.Services
{
class UserService { /* … */ }
}
namespace MyApp.UI
{
class ConsoleView { /* … */ }
}
C#のような形になります。
「名前空間は、フォルダ構成とセットで考えると分かりやすい」
という感覚を持っておくと、整理がぐっと楽になります。
フォルダ構成をどう考えるか
フォルダは「物理的な整理」、名前空間は「論理的な整理」
フォルダは、プロジェクトの中でファイルを整理するための「物理的な箱」です。 名前空間は、そのファイルの中に書かれたクラスの「論理的な住所」です。
多くの場合、
Modelsフォルダ →MyApp.Models名前空間Servicesフォルダ →MyApp.Services名前空間
というように、フォルダと名前空間を揃えておくと、 あとからコードを読む人がとても楽になります。
例:シンプルなコンソールアプリのフォルダ構成
たとえば、簡単な「商品管理アプリ」を作るとします。
MyApp
├─ Models
│ └─ Product.cs
├─ Services
│ └─ ProductService.cs
├─ UI
│ └─ ConsoleView.cs
└─ Program.cs
それぞれのファイルの中身は、こんなイメージです。
// Models/Product.cs
namespace MyApp.Models
{
// 商品を表すクラス
class Product
{
public int Id { get; set; } // 商品ID
public string Name { get; set; } // 商品名
public int Price { get; set; } // 価格
}
}
C#// Services/ProductService.cs
using System.Collections.Generic;
using MyApp.Models;
namespace MyApp.Services
{
// 商品に関する処理をまとめたクラス
class ProductService
{
private List<Product> _products = new List<Product>();
public void AddProduct(Product product)
{
_products.Add(product);
}
public IEnumerable<Product> GetAll()
{
return _products;
}
}
}
C#// UI/ConsoleView.cs
using System;
using MyApp.Models;
using MyApp.Services;
namespace MyApp.UI
{
// コンソール画面の表示を担当するクラス
class ConsoleView
{
private readonly ProductService _service;
public ConsoleView(ProductService service)
{
_service = service;
}
public void ShowAllProducts()
{
Console.WriteLine("商品一覧:");
foreach (var p in _service.GetAll())
{
Console.WriteLine($"ID: {p.Id}, 名前: {p.Name}, 価格: {p.Price}円");
}
}
}
}
C#// Program.cs
using MyApp.Models;
using MyApp.Services;
using MyApp.UI;
class Program
{
static void Main()
{
var service = new ProductService();
// サンプル商品を追加
service.AddProduct(new Product { Id = 1, Name = "ノートPC", Price = 120000 });
service.AddProduct(new Product { Id = 2, Name = "マウス", Price = 2000 });
var view = new ConsoleView(service);
view.ShowAllProducts();
}
}
C#このように、
- フォルダで「物理的に分ける」
- 名前空間で「論理的に分ける」
という二段構えで整理していくと、 コードの見通しが一気によくなります。
クラス分割の考え方
「1クラスに全部書く」は卒業していく
最初のうちは、
class Program
{
static void Main()
{
// ここに全部書く…
}
}
C#というスタイルでも動きますし、 学習の段階ではそれで十分なことも多いです。
ただ、機能が増えてくると、
- Main が長くなりすぎる
- どこに何が書いてあるのか分からない
- 修正するときに怖くなる
という状態になりがちです。
そこで大事になってくるのが クラス分割 です。
クラス分割の基本ルール
クラス分割のいちばんシンプルな考え方は、
「役割ごとにクラスを分ける」
というものです。
たとえば、
- データの形 →
Productクラス - データの操作 →
ProductServiceクラス - 画面表示 →
ConsoleViewクラス
というように、 「何を担当しているか」 でクラスを分けていきます。
責務分離(責任を分ける)という考え方
責務分離とは
責務分離(責任分離)は、 少しカッコいい言葉ですが、意味はとてもシンプルです。
「ひとつのクラスに、あれもこれも詰め込みすぎない」 「クラスごとに“何を担当するか”をはっきりさせる」
という考え方です。
よくある「責務がごちゃ混ぜ」な例
using System;
using System.Collections.Generic;
class Program
{
static List<string> products = new List<string>();
static void Main()
{
// 商品追加
products.Add("ノートPC");
products.Add("マウス");
// 商品一覧表示
Console.WriteLine("商品一覧:");
foreach (var p in products)
{
Console.WriteLine(p);
}
// ファイル保存(仮)
Console.WriteLine("ファイルに保存しました(仮)。");
}
}
C#この Program クラスは、
- データ管理
- 表示
- 保存
など、いろいろな責務を一人で抱え込んでしまっています。
責務分離してみる
これを、役割ごとに分けてみます。
// Models/Product.cs
namespace MyApp.Models
{
// 商品を表すクラス(データの形だけを担当)
class Product
{
public string Name { get; set; }
}
}
C#// Services/ProductService.cs
using System.Collections.Generic;
using MyApp.Models;
namespace MyApp.Services
{
// 商品の管理(追加・取得など)を担当するクラス
class ProductService
{
private readonly List<Product> _products = new List<Product>();
public void Add(Product product)
{
_products.Add(product);
}
public IEnumerable<Product> GetAll()
{
return _products;
}
}
}
C#// UI/ConsoleView.cs
using System;
using MyApp.Services;
using MyApp.Models;
namespace MyApp.UI
{
// コンソール表示を担当するクラス
class ConsoleView
{
private readonly ProductService _service;
public ConsoleView(ProductService service)
{
_service = service;
}
public void ShowProducts()
{
Console.WriteLine("商品一覧:");
foreach (var p in _service.GetAll())
{
Console.WriteLine($"- {p.Name}");
}
}
}
}
C#// Program.cs
using MyApp.Models;
using MyApp.Services;
using MyApp.UI;
class Program
{
static void Main()
{
var service = new ProductService();
service.Add(new Product { Name = "ノートPC" });
service.Add(new Product { Name = "マウス" });
var view = new ConsoleView(service);
view.ShowProducts();
}
}
C#この状態になると、
Productは「商品というデータの形」だけを担当ProductServiceは「商品をどう管理するか」だけを担当ConsoleViewは「どう表示するか」だけを担当Programは「全体の流れを組み立てる」だけを担当
というふうに、責務がきれいに分かれます。
「このクラスは何を担当しているのか?」 と自分に問いかけながら分割していくと、 責務分離の感覚が少しずつ育っていきます。
Day 68 用ミニテンプレート
最後に、今日の内容をそのまま使える形のテンプレートとしてまとめておきます。
名前空間 + フォルダ構成の基本形
MyApp
├─ Models
│ └─ User.cs
├─ Services
│ └─ UserService.cs
├─ UI
│ └─ ConsoleView.cs
└─ Program.cs
// Models/User.cs
namespace MyApp.Models
{
class User
{
public string Name { get; set; }
}
}
C#// Services/UserService.cs
using System.Collections.Generic;
using MyApp.Models;
namespace MyApp.Services
{
class UserService
{
private readonly List<User> _users = new List<User>();
public void Add(User user)
{
_users.Add(user);
}
public IEnumerable<User> GetAll()
{
return _users;
}
}
}
C#// UI/ConsoleView.cs
using System;
using MyApp.Services;
namespace MyApp.UI
{
class ConsoleView
{
private readonly UserService _service;
public ConsoleView(UserService service)
{
_service = service;
}
public void ShowUsers()
{
Console.WriteLine("ユーザー一覧:");
foreach (var u in _service.GetAll())
{
Console.WriteLine($"- {u.Name}");
}
}
}
}
C#// Program.cs
using MyApp.Models;
using MyApp.Services;
using MyApp.UI;
class Program
{
static void Main()
{
var service = new UserService();
service.Add(new User { Name = "Alice" });
service.Add(new User { Name = "Bob" });
var view = new ConsoleView(service);
view.ShowUsers();
}
}
C#Day 68 のまとめ
Day 68 では、
- 名前空間は「コードの住所」であり、フォルダと揃えると分かりやすいこと
- フォルダ構成は「物理的な整理」、名前空間は「論理的な整理」であること
- クラス分割は「役割ごとにクラスを分ける」ことから始めるとよいこと
- 責務分離は「ひとつのクラスに何でも詰め込まない」ための考え方であること
を、具体的なコード例とともに見てきました。
コードの整理は、 「正解がひとつだけ」というものではなく、 プロジェクトの規模やメンバー、目的によって少しずつ形が変わります。
だからこそ、
- 小さなアプリでも名前空間とフォルダを意識してみる
- クラスごとに「この子は何を担当しているのか?」と考えてみる
- 責務が増えすぎたクラスを、勇気を出して分割してみる
といった小さなチャレンジを重ねていくことが、 「読みやすくて育てやすいコード」を書く力につながっていきます。
ぜひ、Day 68 をきっかけに、 コードを「ただ動けばいいもの」から 「気持ちよく育てていけるもの」へと 少しずつ変えていっていただければうれしいです。
