基礎から学ぶC#入門 90日コース | アプリを作りながら学ぶ - Day 71:要件定義

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

Day 72 のゴールと全体像

Day 72 では、タスク管理アプリの「心臓部分」ともいえる Task クラスの設計をじっくり育てていきます。

今日の主役は、この1つのエンティティです。

Task
 ├─ Id
 ├─ Title
 ├─ Description
 ├─ Priority
 ├─ DueDate
 ├─ IsCompleted
 └─ CreatedAt

昨日の Day 71 で「どんな機能がほしいか」を言葉にしましたが、 今日はそれを 「データの形」として、 C# のクラスに落とし込んでいくステップになります。

  • それぞれのプロパティが、何のために存在しているのか
  • 型はどうするのが自然か
  • 初期値はどうしておくと安全か

といったところを、 コード例を交えながら、ゆっくりかみ砕いていきます。

Task クラスの全体像を決める

まずはシンプルなクラス定義から

最初に、今日のゴールとなる Task クラスの全体像を ざっくりコードで見てみます。

// タスクを表すクラス
class TaskItem
{
    public int Id { get; set; }                 // 一意なID
    public string Title { get; set; }           // タスクのタイトル
    public string Description { get; set; }     // 詳細・メモ
    public int Priority { get; set; }           // 優先度(1: 高, 2: 中, 3: 低 など)
    public DateTime? DueDate { get; set; }      // 期限(ない場合もあるので ?)
    public bool IsCompleted { get; set; }       // 完了しているかどうか
    public DateTime CreatedAt { get; set; }     // 作成日時
}
C#

このあと、

  • なぜこの型なのか
  • どういう使い方を想定しているのか

を、プロパティごとに深掘りしていきます。

Id:タスクを一意に識別する

なぜ Id が必要なのか

タスク管理アプリでは、

  • 「このタスクを編集したい」
  • 「このタスクを削除したい」

といった操作が頻繁に登場します。

そのときに、

「タイトルが『買い物』のタスクを削除して」

という指定だけだと、 同じタイトルのタスクが複数あった場合に困ってしまいます。

そこで登場するのが Id(識別子)です。

型はどうする?

Id は、

  • 数値(int)で連番を振る
  • GUID(Guid)でランダムな一意IDを使う

など、いろいろな選択肢がありますが、 学習用のコンソールアプリでは int の連番が分かりやすいです。

class TaskItem
{
    public int Id { get; set; } // 1, 2, 3... といった連番を想定
}
C#

連番を振るイメージ

List<TaskItem> tasks = new List<TaskItem>();
int nextId = 1; // 次に割り当てるID

TaskItem CreateTask(string title)
{
    var task = new TaskItem
    {
        Id = nextId++,       // ここでIDを割り当てて、次の値に進める
        Title = title,
        CreatedAt = DateTime.Now,
        IsCompleted = false
    };
    tasks.Add(task);
    return task;
}
C#

このように、 「Id はアプリの中でタスクを指し示すためのラベル」 というイメージを持っておくと、扱いやすくなります。

Title:タスクの顔になる情報

タイトルは「ひと目で分かる名前」

Title は、タスクの「顔」です。

  • 「レポート作成」
  • 「買い物」
  • 「メール返信」

など、一覧画面でパッと見て内容が分かるような名前を入れます。

class TaskItem
{
    public string Title { get; set; } // タスクのタイトル
}
C#

空タイトルは許す?許さない?

設計として、

  • タイトルは必須にしたい
  • 空文字は避けたい

という気持ちが出てきます。

コンソールアプリでは、 入力時にチェックするのがシンプルです。

string ReadTitle()
{
    while (true)
    {
        Console.Write("タスク名を入力してください: ");
        string title = Console.ReadLine();

        if (!string.IsNullOrWhiteSpace(title))
        {
            return title;
        }

        Console.WriteLine("タスク名は必須です。空では登録できません。");
    }
}
C#

このように、 「データのルールを入力時に守らせる」という設計も、 Day 72 の段階で意識しておくとよいポイントです。

Description:タスクの裏側を支えるメモ

詳細は「自分のためのメモ」

Description は、

  • 「どこまでやるのか」
  • 「注意点は何か」
  • 「関連する情報」

などを自由に書ける、 自分のためのメモ欄のような位置づけです。

class TaskItem
{
    public string Description { get; set; } // 詳細・メモ
}
C#

必須ではないけれど、あると便利

Description は、

  • 空でもいい
  • 長くてもいい

という、わりと自由度の高い項目です。

一覧表示では、

  • タイトルだけ表示する
  • 詳細は「詳細表示」機能で見せる

など、UI側の工夫で見やすさを調整できます。

Priority:優先度をどう表現するか

数値で持つか、列挙型で持つか

Priority は、

といった優先度を表現するための項目です。

最初の一歩としては、 数値(int)で持つのがシンプルです。

class TaskItem
{
    public int Priority { get; set; } // 1: 高, 2: 中, 3: 低 を想定
}
C#

表示するときに、

string priorityText = task.Priority switch
{
    1 => "高",
    2 => "中",
    3 => "低",
    _ => "不明"
};
C#

のように変換してあげれば、 ユーザーには「高・中・低」として見せられます。

もう一歩進んだ設計:enum を使う

少し設計をリッチにするなら、 enum を使う方法もあります。

// 優先度を表す列挙型
enum TaskPriority
{
    High = 1,
    Medium = 2,
    Low = 3
}

class TaskItem
{
    public TaskPriority Priority { get; set; } // enum で管理
}
C#

こうしておくと、

  • コード上で TaskPriority.High のように書ける
  • 間違った値(4とか99とか)が入りにくくなる

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

Day 72 では、

「最初は int で始めて、慣れてきたら enum にしてみる」

というステップを意識しておくと、 設計の成長を楽しめます。

DueDate:期限をどう扱うか

期限は「あるかもしれないし、ないかもしれない」

すべてのタスクに期限があるとは限りません。

  • 「今週中にやりたい」
  • 「いつかやりたい」

というタスクもありますよね。

そこで、期限は 「あるかもしれないし、ないかもしれない」 という前提で設計します。

class TaskItem
{
    public DateTime? DueDate { get; set; } // 期限。ない場合は null
}
C#

DateTime?? は、

「このプロパティは null を許します」

という意味で、 **「期限なし」という状態を表現するために使っています。

期限の入力イメージ

DateTime? ReadDueDate()
{
    Console.Write("期限を入力してください (yyyy-MM-dd、未入力なら期限なし): ");
    string text = Console.ReadLine();

    if (string.IsNullOrWhiteSpace(text))
    {
        return null; // 期限なし
    }

    // 簡易的に Parse を使う(本格的には TryParse を使うと安全)
    return DateTime.Parse(text);
}
C#

表示するときは、

string dueText = task.DueDate.HasValue
    ? task.DueDate.Value.ToString("yyyy-MM-dd")
    : "期限なし";
C#

のように、 「期限あり」と「期限なし」を分けて表示してあげると親切です。

IsCompleted:完了状態の管理

true / false でシンプルに

IsCompleted は、

  • true → 完了
  • false → 未完了

という、シンプルなフラグです。

class TaskItem
{
    public bool IsCompleted { get; set; } // 完了しているかどうか
}
C#

初期値はどうする?

タスクを作成した瞬間は、 当然まだ完了していません。

なので、初期値は false にしておきます。

var task = new TaskItem
{
    Id = nextId++,
    Title = title,
    Description = description,
    Priority = priority,
    DueDate = dueDate,
    IsCompleted = false,          // 追加時点では未完了
    CreatedAt = DateTime.Now
};
C#

完了状態の切り替えは、

task.IsCompleted = !task.IsCompleted; // true/false を反転
C#

のように、 トグル(切り替え)操作で扱うと分かりやすいです。

CreatedAt:いつ作られたタスクなのか

作成日時は「履歴の軸」になる

CreatedAt は、

  • いつ追加されたタスクなのか
  • 古いタスクから順に並べたい
  • 最近追加したタスクだけ見たい

といったときに役立つ情報です。

class TaskItem
{
    public DateTime CreatedAt { get; set; } // 作成日時
}
C#

初期値は「今この瞬間」

タスクを作成するときに、

task.CreatedAt = DateTime.Now;
C#

としておけば、 「このタスクはいつ生まれたか」が記録されます。

一覧表示で、

Console.WriteLine($"作成日時: {task.CreatedAt:yyyy-MM-dd HH:mm}");
C#

のように表示してあげると、 タスクの「時間軸」が見えてきて、 管理が少し楽しくなります。

Task クラス + 簡易リポジトリのテンプレート

Task クラスの完成形

ここまでの内容をまとめた Task クラスのテンプレートです。

// タスクを表すクラス
class TaskItem
{
    public int Id { get; set; }                 // 一意なID
    public string Title { get; set; }           // タスク名
    public string Description { get; set; }     // 詳細・メモ
    public int Priority { get; set; }           // 優先度(1: 高, 2: 中, 3: 低)
    public DateTime? DueDate { get; set; }      // 期限(任意)
    public bool IsCompleted { get; set; }       // 完了フラグ
    public DateTime CreatedAt { get; set; }     // 作成日時
}
C#

簡単な「タスク管理クラス」のイメージ

TaskItem を使う側として、 小さな管理クラスを用意しておくと、 Day 73 以降の実装がスムーズになります。

using System;
using System.Collections.Generic;
using System.Linq;

// タスク一覧を管理するクラス
class TaskRepository
{
    private readonly List<TaskItem> _tasks = new List<TaskItem>();
    private int _nextId = 1;

    // タスク追加
    public TaskItem Add(string title, string description, int priority, DateTime? dueDate)
    {
        var task = new TaskItem
        {
            Id = _nextId++,
            Title = title,
            Description = description,
            Priority = priority,
            DueDate = dueDate,
            IsCompleted = false,
            CreatedAt = DateTime.Now
        };

        _tasks.Add(task);
        return task;
    }

    // 全タスク取得
    public IEnumerable<TaskItem> GetAll()
    {
        return _tasks;
    }

    // IDで検索
    public TaskItem FindById(int id)
    {
        return _tasks.FirstOrDefault(t => t.Id == id);
    }

    // タイトルで検索(部分一致)
    public IEnumerable<TaskItem> SearchByTitle(string keyword)
    {
        return _tasks.Where(t =>
            !string.IsNullOrEmpty(t.Title) &&
            t.Title.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0);
    }

    // 削除
    public bool Remove(int id)
    {
        var task = FindById(id);
        if (task == null) return false;

        _tasks.Remove(task);
        return true;
    }
}
C#

このように、

  • TaskItem は「データの形」
  • TaskRepository は「データの集まりをどう扱うか」

という役割分担をしておくと、 アプリ全体の設計がぐっと整理されていきます。

Day 72 のまとめ

Day 72 では、

  • Task クラスのプロパティをひとつひとつ丁寧に見ていき
  • Id・Title・Description・Priority・DueDate・IsCompleted・CreatedAt それぞれが「何のために存在しているのか」を言葉にし
  • 型の選び方(int / string / DateTime? / bool)や 初期値の考え方をコードで確認し
  • TaskItem を使うための簡易リポジトリのイメージまで描きました。

設計は、 「ただクラスを作る作業」ではなく、

このアプリのタスクは、 こういう顔をしていて、 こういう時間軸を持っていて、 こういう優先度や期限を抱えているんだ

と、データに物語を与えていく作業でもあります。

Day 72 を通して、

  • 「プロパティひとつひとつに意味がある」こと
  • 「型の選び方が、アプリの性格を決めていく」こと

を少し感じてもらえたなら、 この先の実装フェーズが、きっと楽しくなっていきます。

次の Day では、 今日設計した Task クラスを実際に動かしながら、 タスク管理アプリを少しずつ形にしていきます。 そのとき、今日の設計が「頼れる土台」として しっかり支えてくれるはずです。

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