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