基礎から学ぶC#入門 90日コース | 実践力を高める - Day 68:コードの整理

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

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 をきっかけに、 コードを「ただ動けばいいもの」から 「気持ちよく育てていけるもの」へと 少しずつ変えていっていただければうれしいです。

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