付録I オブジェクト指向(後半)課題集 — 自習日用
この付録の位置づけ
Section titled “この付録の位置づけ”この付録は、第 12 章〜第 15 章(オブジェクト指向の後半) を一通り終えた後の 自習日(課題日) に取り組むための課題集です。
第 12 章で学んだ List<T> と LINQ、第 13 章の継承・ポリモーフィズム、第 14 章のインターフェイス、第 15 章の例外処理を、社員系以外の題材で組み合わせて定着させます。
自習とはいえ講師は常駐しているので、詰まったら質問してください。提出は 任意 ですが、レベル 1・レベル 2 は全員に取り組んでもらうことを推奨します。
本編・付録 H との違い
Section titled “本編・付録 H との違い”| 観点 | 本編(各章末) | 付録 H(前半) | 本付録 I(後半) | 付録 F(発展) |
|---|---|---|---|---|
| 対象範囲 | その章の内容 | 第 7〜11 章の定着 | 第 12〜15 章の定着 | 第 0〜31 章を走り切った人向け |
| 提出 | 必須(タイマー方式) | 任意 | 任意 | 任意 |
| 完全なコード | 本文にあり | なし | なし(仕様・実行例・ヒントのみ) | なし |
| 題材 | 社員系が中心 | 業務寄り + 身近 | 図書・図形・楽器・動物・銀行 など | 業務系 |
| 難度 | 章ごとに段階的 | 段階的(3 レベル) | 段階的(3 レベル) | 上級寄り |
「同じ仕組み(継承・インターフェイス・LINQ・例外処理)を 違う題材 で使う」体験が、この付録のいちばんの狙いです。
I-1 取り組み方の原則
Section titled “I-1 取り組み方の原則”本付録には完全なコードを載せません。掲載しているのは:
- 仕様(何を作るか)
- 実行結果例(出力イメージ)
- ヒント(詰まりやすい箇所への指針)
だけです。本編のように「コピペで動く」状態にはしていません。自分で組み立てる練習 のための場だと割り切ってください。
進め方の目安
Section titled “進め方の目安”| レベル | 目安時間 | 取り組み方 |
|---|---|---|
| レベル 1(I-3-1〜I-3-3) | 各 30〜45 分 | 全員推奨。第 12〜14 章の典型例を別題材で素振り |
| レベル 2(I-4-1, I-4-2) | 各 60〜90 分 | 時間が許せば全員。例外処理と複数章の組み合わせ |
| レベル 3(I-5-1) | 2〜3 時間以上 | 挑戦したい人。ミニアプリ規模。完成しなくても OK、設計の経験を積む |
1 日(7 時間)の自習日なら、レベル 1 + レベル 2 で一通り が標準ペース。レベル 3 は時間が余った人や、もう一段挑戦したい人向けです。
ソリューションの分け方
Section titled “ソリューションの分け方”本編と同じく、課題ごとに別プロジェクト を作成してください。
本付録用のソリューション名は KadaiI で、各課題のプロジェクト名は次のとおりです。
| 課題 | プロジェクト名 |
|---|---|
| I-3-1 書籍リストの集計 | KdI_3_1_BookStats |
| I-3-2 図形の面積と周長 | KdI_3_2_Shapes |
| I-3-3 楽器のインターフェイス | KdI_3_3_Instruments |
| I-4-1 銀行口座の安全な入出金 | KdI_4_1_BankAccount |
| I-4-2 動物園シミュレータ | KdI_4_2_Zoo |
| I-5-1 図書館貸出管理アプリ | KdI_5_1_LibraryApp |
各プロジェクトの csproj 設定(<Nullable>disable</Nullable> など)は本編と同じです。
質問のしかた
Section titled “質問のしかた”詰まったときは、講師に丸投げせず、次の 3 点を整理してから相談してください。
- 何をしようとしているか(課題のどの仕様に取り組み中か)
- どこまで動いているか(画面ショット or 入力したコード)
- 何が起きているか(エラーメッセージ、期待した動きと実際の差)
これは配属後にも効く、現場の質問の型 です。
I-2 準備:共通の前提
Section titled “I-2 準備:共通の前提”| 必要なもの | 用途 |
|---|---|
| Visual Studio 2022 | 全課題 |
| 第 15 章まで履修済み | 全課題 |
KadaiI ソリューション(新規) | 全課題 |
データベースは使いません(本付録は OOP 後半の「LINQ・継承・インターフェイス・例外処理」の練習に集中)。
全課題共通のルール
Section titled “全課題共通のルール”| 作業 | 内容 | 参照 |
|---|---|---|
| プロジェクト作成時の設定 | 「最上位レベルのステートメントを使用しない」にチェック | 第 1 章 1-1 |
| csproj の編集 | <Nullable>disable</Nullable> に変更 | 第 1 章 1-1 |
namespace の書き方 | ファイルスコープ形式(namespace XXX;) | 第 7 章 冒頭 |
| クラスファイルの追加 | プロジェクト右クリック → 追加 → クラス | 第 7 章 7-7 |
クラスファイルを自動生成すると、ブロック形式の namespace プロジェクト名 { ... } で作られます。ファイルスコープ形式に書き換えて ください(例:namespace KdI_3_1_BookStats;)。
提出について
Section titled “提出について”自習日なので 厳密なタイマーや必須提出はありません。ただし、終わった分は 任意で講師にレビュー依頼 ができます。次のいずれかで提出してください。
- Git が使える場合:本編と同じく
git add→git commit→git push- コミットメッセージ例:
AppendixI: I-3-1〜I-4-1完成 / I-4-2は途中
- コミットメッセージ例:
- Git が使えない場合:
KadaiIフォルダをサーバへコピー +提出メモ.txt
I-3 レベル 1:ウォームアップ
Section titled “I-3 レベル 1:ウォームアップ”第 12〜14 章の典型例を、社員以外の題材で素振りします。全員に推奨 のレベルです。
I-3-1 書籍リストの集計(目安 30〜45 分)
Section titled “I-3-1 書籍リストの集計(目安 30〜45 分)”関連章:第 12 章(List<T>・LINQ)、第 10 章(コンストラクター)
図書館の蔵書 8 件を List<Book> で持ち、LINQ で 絞り込み・並び替え・集計 を行ってください。
クラス仕様 Book
- プロパティ(
public { get; set; }):Title(string)Author(string)Category(string)(例:"技術書"、"小説"、"歴史")Price(int)PublishedYear(int)
- コンストラクター:
Book(string title, string author, string category, int price, int publishedYear)
- メソッド:
PrintShort():"[技術書] C# 入門 / 山田太郎 / 2020年 / 2800円"の形式で 1 行表示
Main メソッド仕様
List<Book>で 8 冊のサンプルを作る(カテゴリは 3〜4 種類混在、年は 1995〜2024 の間で散らす)- 次の出力をそれぞれ行う:
- 全件表示(
PrintShortをforeach) - カテゴリ「技術書」の冊数(
Count(条件)) - 全書籍の平均価格(
Average) - 価格降順の Top 3(
OrderByDescending+Take(3)) - 最も古い本(
OrderBy+FirstOrDefault) - 著者「山田太郎」の本一覧(
Where)
- 全件表示(
[技術書] C# 入門 / 山田太郎 / 2020年 / 2800円[小説] 春の旅 / 佐藤花子 / 2015年 / 1500円[歴史] 江戸の暮らし / 田中一郎 / 1998年 / 1800円...
技術書の冊数: 3冊平均価格: 2050円
価格降順 Top3:1位: 機械学習入門 (4500円)2位: 詳解C# (3200円)3位: C# 入門 (2800円)
最も古い本: 江戸の暮らし (1998年)
著者「山田太郎」の本:- C# 入門- LINQ 入門Take(3)は LINQ の便利メソッド。using System.Linq;が必要(暗黙的 using で自動)- 平均価格を
doubleで得たいならbooks.Average(b => b.Price)の戻り値がdoubleになる(整数で表示したいなら(int)でキャスト、または書式{value:F0}) - 「最も古い本」は
OrderBy(b => b.PublishedYear).FirstOrDefault()が素直。本が空の可能性があるならFirstOrDefaultで null チェック
I-3-2 図形の面積と周長(目安 30〜45 分)
Section titled “I-3-2 図形の面積と周長(目安 30〜45 分)”関連章:第 13 章(継承、抽象クラス、virtual/override、ポリモーフィズム)、第 10 章(コンストラクター)
図形を表す 抽象クラス と、3 つの具体クラスを作ります。面積と周長は子クラスで計算します。Shape[] にまとめて ポリモーフィズム を体験してください。
抽象クラス仕様 Shape
- プロパティ:
Name(string、public { get; private set; }):図形名
- コンストラクター:
Shape(string name)
- 抽象メソッド:
GetArea()(doubleを返す)GetPerimeter()(doubleを返す)
- 通常メソッド:
PrintInfo():"[円] 面積 78.54 / 周長 31.42"のような行を表示(NameとGetArea/GetPerimeterを組み合わせる)
子クラス仕様
| クラス | コンストラクター | 形 |
|---|---|---|
Circle | Circle(double radius)(name = "円") | 面積 = π r² / 周長 = 2 π r |
Rectangle | Rectangle(double width, double height)(name = "長方形") | 面積 = 幅 × 高さ / 周長 = 2(幅 + 高さ) |
Triangle | Triangle(double a, double b, double c)(name = "三角形"、3 辺の長さ) | 面積はヘロンの公式、周長 = a+b+c |
ヘロンの公式:
s = (a+b+c) / 2、面積 =Math.Sqrt(s*(s-a)*(s-b)*(s-c))。3-4-5 の三角形なら面積は 6 になります。
Main メソッド仕様
Shape[]で 5 つの図形(Circle 2 つ + Rectangle 2 つ + Triangle 1 つなど)を作るforeachで全員のPrintInfo()を呼ぶ- 全図形の 面積の合計 を表示
- 最も面積の大きい図形の Name と面積 を表示
[円] 面積 78.54 / 周長 31.42[円] 面積 28.27 / 周長 18.85[長方形] 面積 24.00 / 周長 20.00[長方形] 面積 60.00 / 周長 32.00[三角形] 面積 6.00 / 周長 12.00
合計面積: 196.81最大面積: 円 (78.54)- 円周率は
Math.PI、平方根はMath.Sqrt - 面積・周長は
doubleで計算。表示は$"{value:F2}"で小数点 2 桁固定 - 子クラスのコンストラクターから親へは
: base("円")のように渡す(第 13 章 13-3) - 最大を探すのはポリモーフィズム経由で OK。
shapes.OrderByDescending(s => s.GetArea()).First()が素直(LINQ で型をShapeのまま扱えること自体がポイント) Shape[]の代わりにList<Shape>でも構いません
I-3-3 楽器のインターフェイス(目安 30〜45 分)
Section titled “I-3-3 楽器のインターフェイス(目安 30〜45 分)”関連章:第 14 章(インターフェイス、複数インターフェイス、インターフェイス型として扱う)、第 12 章(LINQ)
楽器を表すインターフェイスを 2 つ作り、3 種類の楽器クラスに実装します。List<IPlayable> でまとめて ポリモーフィズムでの演奏 と、「チューニング可能なもの」だけを取り出す 操作を体験してください。
インターフェイス仕様 IPlayable
- プロパティ:
Name(string、getのみ):楽器名
- メソッド:
Play()(stringを返す):"ギターを弾いた:ジャラーン"のような演奏結果Stop()(戻り値なし):"演奏停止"を表示
インターフェイス仕様 ITunable
- メソッド:
Tune()(戻り値なし):"ギターをチューニングしました"のような表示
クラス仕様
| クラス | 実装するインターフェイス | Play() の例 |
|---|---|---|
Guitar | IPlayable, ITunable | "ギターを弾いた:ジャラーン" |
Piano | IPlayable | "ピアノを弾いた:ポロロン" |
Drum | IPlayable, ITunable | "ドラムを叩いた:ドンドン" |
PianoはITunableを実装しません(調律は専門家がやるという設計判断)。 どの楽器に対してもTune()を呼んでいいわけではない、という インターフェイスの「契約」の感覚 を体験してください。
Main メソッド仕様
List<IPlayable>に 4 件(例:Guitar / Piano / Drum / Guitar)を入れる- 全員に
Play()→ 結果を 1 行ずつ表示 - 全員に
Stop()を順次呼ぶ List<IPlayable>の中からITunableを実装しているものだけ を取り出してTune()を呼ぶ
[Guitar] ギターを弾いた:ジャラーン[Piano] ピアノを弾いた:ポロロン[Drum] ドラムを叩いた:ドンドン[Guitar] ギターを弾いた:ジャラーン
演奏停止演奏停止演奏停止演奏停止
チューニング:ギターをチューニングしましたドラムをチューニングしましたギターをチューニングしましたNameプロパティをインターフェイスで宣言する書き方は第 14 章 14-2 を参照- インターフェイスは複数実装できる。クラス宣言で
class Guitar : IPlayable, ITunableのようにカンマで並べる(第 14 章 14-5) - ある楽器が
ITunableも持っているかはif (instrument is ITunable tunable) { tunable.Tune(); }のようにis演算子 + パターンマッチ で判定可能(第 11 章でis nullの形は学習済み、ここでは型判定の形に拡張) - LINQ で書きたいなら
instruments.OfType<ITunable>()で「ITunableを実装しているものだけ」が取り出せる
I-4 レベル 2:統合課題
Section titled “I-4 レベル 2:統合課題”例外処理を主軸にした堅牢性、そして継承 + インターフェイス + LINQ の組み合わせを扱います。時間が許せば全員 に取り組んでもらいたいレベルです。
I-4-1 銀行口座の安全な入出金(目安 60〜90 分)
Section titled “I-4-1 銀行口座の安全な入出金(目安 60〜90 分)”関連章:第 15 章(例外処理、throw、try-catch-finally、TryParse、ファイル例外)、第 10 章(カプセル化・private set)
銀行口座を表すクラスを作り、入金・出金・履歴保存ができる CUI アプリにします。残高不足や無効な金額は独自例外で throw、入力検証は TryParse、ファイル保存は try-catch-finally を使って堅牢に書いてください。
独自例外仕様 InsufficientBalanceException
Exceptionを継承- コンストラクター:
InsufficientBalanceException(string message) : base(message) { }
クラス仕様 BankAccount
- プロパティ(
public { get; private set; }):Owner(string):口座名義人Balance(int):残高
- 内部状態:
List<string>で履歴を保持(例:"2026/05/27 10:00 入金 5000円 残高 15000円")
- コンストラクター:
BankAccount(string owner, int initialBalance):initialBalanceが 0 未満ならArgumentExceptionをthrow
- メソッド:
Deposit(int amount):amountが 0 以下ならArgumentException、OK なら残高加算 + 履歴追加Withdraw(int amount):amountが 0 以下ならArgumentException、amount > BalanceならInsufficientBalanceException、OK なら残高減算 + 履歴追加PrintHistory():履歴を全件表示SaveHistoryToFile(string path):履歴をファイルに保存。IOException系は呼び出し元に伝える(catch は呼び出し側で行う)
Main メソッド仕様
メニューループ:
=== 銀行口座 ===1: 預け入れ 2: 引き出し 3: 残高表示 4: 履歴表示 5: 履歴ファイル保存 0: 終了> _- 起動時に口座名義人と初期残高を入力 →
BankAccountを生成(初期残高はint.TryParse、コンストラクター内例外は捕まえて再入力) - 金額入力は
int.TryParseで。失敗したら「数字を入れてください」で再入力 Deposit/Withdrawの呼び出しはtry-catchで囲み、ArgumentException/InsufficientBalanceExceptionを捕まえてメッセージ表示- 履歴ファイル保存は
try-catch-finallyで囲み、finallyで「保存処理を完了しました」を必ず表示
=== 銀行口座 ===口座名義人: 山田二郎初期残高: 10000
> 1預け入れ額: 5000入金完了 残高: 15000円
> 2引き出し額: 30000エラー: 残高不足です(残高: 15000円 / 要求: 30000円)
> 2引き出し額: -100エラー: 金額は 1 以上を指定してください
> 5保存ファイル名 (既定: history.txt): [Enter]保存処理を完了しました2件の履歴を history.txt に保存しました
> 0- 独自例外はクラスを 1 つ作るだけ:
public class InsufficientBalanceException : Exception { public InsufficientBalanceException(string message) : base(message) { } } Balanceをprivate setにすると、Main から直接書き換えできない(これがカプセル化の効果。第 10 章 10-5)try-catchで複数の例外型を分けて捕まえる場合、子クラス(具体的な例外)を先に書く(第 15 章 15-3)- ファイル保存は
File.WriteAllLines(path, history)(List<string>も渡せる) finallyは 例外があってもなくても必ず実行 されるので、「保存処理を完了しました」のような後片付けに向く- 履歴行のタイムスタンプは
DateTime.Now.ToString("yyyy/MM/dd HH:mm")
I-4-2 動物園シミュレータ(目安 60〜90 分)
Section titled “I-4-2 動物園シミュレータ(目安 60〜90 分)”関連章:第 13 章(継承、抽象クラス)、第 14 章(インターフェイス)、第 12 章(LINQ)
動物園にいる動物を 抽象クラス + インターフェイス で表現し、List<Animal> で管理して LINQ で集計します。
抽象クラス仕様 Animal
- プロパティ(
public { get; private set; }):Name(string)Age(int)
- コンストラクター:
Animal(string name, int age)
- 抽象メソッド:
Speak()(stringを返す):鳴き声を返す
- 通常メソッド:
PrintProfile():"[Dog] ポチ 3歳 ワン!"の形式で表示(クラス名はGetType().Nameでも、子クラスごとに別途プロパティを持たせても OK)
インターフェイス仕様 IFeedable
- プロパティ:
FoodCostPerDay(int、getのみ):1 日あたりの餌代
- メソッド:
Feed()(戻り値なし):"ポチに餌をあげた"のような表示
子クラス仕様
| クラス | 継承 + 実装 | Speak() | FoodCostPerDay |
|---|---|---|---|
Dog | Animal, IFeedable | "ワン!" | 500 円 |
Cat | Animal, IFeedable | "ニャー" | 400 円 |
Bird | Animal, IFeedable | "ピヨピヨ" | 200 円 |
Fish | Animal のみ | "…" | (餌代なし) |
FishはIFeedableを実装しません(餌のあげ方が違う、という設計判断)。 どの動物に対してもFeed()を呼んでいいわけではない、という インターフェイスの「契約」の感覚 を体験してください。
Main メソッド仕様
List<Animal>に 7〜8 件入れる(Dog 2, Cat 2, Bird 2, Fish 1〜2)- 次の出力を行う:
- 全員の
PrintProfile()(Speak()含む) IFeedableを実装する動物だけFeed()を呼ぶ- 全
IFeedableのFoodCostPerDayの合計 Age >= 5の動物の数(LINQ)- 「Bird」だけの一覧(
OfType<Bird>またはis Bird)
- 全員の
[Dog] ポチ 3歳 ワン![Dog] ハチ 7歳 ワン![Cat] タマ 5歳 ニャー[Cat] ミケ 2歳 ニャー[Bird] チュンチュン 2歳 ピヨピヨ[Bird] ピーちゃん 4歳 ピヨピヨ[Fish] キン 1歳 …
餌やり:ポチに餌をあげたハチに餌をあげたタマに餌をあげたミケに餌をあげたチュンチュンに餌をあげたピーちゃんに餌をあげた
1日の餌代合計: 2200円5歳以上の動物: 2匹鳥類: チュンチュン, ピーちゃん- 抽象クラスは
abstract class Animal、抽象メソッドはpublic abstract string Speak();(第 13 章 13-6) - 1 クラスで「継承 + インターフェイス実装」を両方やる書き方は
class Dog : Animal, IFeedable(第 14 章 14-5)。継承を先、インターフェイスを後 に書くのが慣例 IFeedableだけ取り出すのは LINQ でanimals.OfType<IFeedable>()が素直Birdだけ取り出すのはanimals.OfType<Bird>()、またはanimals.Where(a => a is Bird)でも可GetType().Nameを使うと、Dogのインスタンスから文字列"Dog"が得られる(リフレクションの入口だが、ここでは「表示用にクラス名を取る便利機能」と割り切って OK)
I-5 レベル 3:ミニアプリ
Section titled “I-5 レベル 3:ミニアプリ”ここからは 目的を持った小さなアプリ を作ります。完成しなくても OK、設計の経験を積む ことを目的としてください。 レベル 1・レベル 2 を全部終わってから挑戦するのがおすすめです。
I-5-1 図書館貸出管理アプリ(目安 2〜3 時間以上)
Section titled “I-5-1 図書館貸出管理アプリ(目安 2〜3 時間以上)”関連章:第 12 章(List<T>・LINQ)、第 14 章(インターフェイス)、第 15 章(例外処理・独自例外)、第 10 章(カプセル化)、第 9 章(File 入出力)
メニュー駆動の CUI アプリで、本の登録・貸出・返却・延滞リスト表示・CSV 保存/読込ができる 簡易図書館システム を作ります。Repository パターン(インターフェイス + 実装) と 独自例外 を取り入れてください。
本アプリは第 14 章 14-7「Service と Repository のイメージ」の発想を、社員以外の題材で実装する場でもあります。第 27 章以降の Web MVC + DB でも同じ構造が出てきます。
クラス仕様 Book
- プロパティ:
BookId(int、get; private set;)Title(string、get; private set;)Author(string、get; private set;)IsLent(bool、get; private set;):貸出中かBorrowerName(string、get; private set;):借りている人の名前(未貸出時はnull)DueDate(DateTime?、get; private set;):返却期限(未貸出時はnull)
- コンストラクター:
Book(int bookId, string title, string author)(初期状態は未貸出)
- メソッド:
Lend(string borrowerName, DateTime dueDate):貸出処理。既に貸出中ならBookNotAvailableExceptionをthrowReturnBook():返却処理。貸出されていなければInvalidOperationExceptionをthrowPrintShort():"#1 [貸出中] C# 入門 / 山田太郎 / 借主:佐藤花子 / 期限:2026/06/10"の形式で 1 行表示
独自例外仕様
BookNotFoundException : Exception:本が見つからないときBookNotAvailableException : Exception:貸出中の本を借りようとしたとき
それぞれ Exception を継承して、メッセージを受け取るコンストラクターを 1 つ用意するだけで OK。
インターフェイス仕様 IBookRepository
- メソッド:
Add(Book book):1 件追加GetAll():List<Book>を返すFindById(int bookId):見つからなければBookNotFoundExceptionをthrowLend(int bookId, string borrowerName, int loanDays):本をFindByIdで取得してBook.Lendを呼ぶReturnBook(int bookId):返却GetOverdue(DateTime today):期限切れの貸出中の本一覧SaveToCsv(string path)/LoadFromCsv(string path)
クラス仕様 InMemoryBookRepository : IBookRepository
List<Book>を内部に持って各メソッドを実装MainからはIBookRepository repo = new InMemoryBookRepository();と受けると、後で実装を差し替えやすい構造になる
Main メソッド仕様
メニューループ:
=== 図書館貸出管理 ===1: 本を追加 2: 全件表示 3: 貸出 4: 返却 5: 延滞リスト6: CSV保存 7: CSV読込 0: 終了> _操作中の各種例外は try-catch で捕まえてメッセージ表示、メニューを抜けないこと。
CSV 入出力は try-catch-finally で。
=== 図書館貸出管理 ===> 1タイトル: C# 入門著者: 山田太郎追加しました: #1 C# 入門
> 1タイトル: LINQ 入門著者: 山田太郎追加しました: #2 LINQ 入門
> 3本ID: 1借主: 佐藤花子貸出日数 (既定 14): [Enter]貸出完了: #1 C# 入門 / 借主:佐藤花子 / 期限:2026/06/10
> 3本ID: 1借主: 田中一郎貸出日数: 14エラー: その本は既に貸出中です(#1 C# 入門)
> 3本ID: 99借主: 田中一郎貸出日数: 14エラー: ID 99 の本は見つかりません
> 5延滞中の本(0件):なし
> 6保存先 (既定: books.csv): [Enter]保存処理を完了しました2件を books.csv に保存しました
> 0BookIdは手で渡してもいいし、Repository側で自動採番してもよい(設計次第。_nextId++で十分)IBookRepositoryを切ることで、後でSqlBookRepositoryのような実装を追加する余地ができる(第 27〜30 章で扱う Repository パターンと同じ発想)- 各独自例外は 小さなクラス 2 つ で済む。
Exceptionを継承し、コンストラクター 1 本だけでよい - CSV の 1 行は
bookId,title,author,isLent,borrowerName,dueDateのような並びで OK。nullの代わりに空文字列を使うと簡単 - 入力検証は
int.TryParse/DateTime.TryParse、未入力 Enter は既定値で進める GetOverdue(DateTime today)はbooks.Where(b => b.IsLent && b.DueDate.HasValue && b.DueDate.Value < today)のような LINQ- メニューループは
while (true) { ... }+case "0": return;のような形
発展(余裕があれば)
Section titled “発展(余裕があれば)”- 貸出履歴(
LoanRecordクラス +List<LoanRecord>)を残し、「誰がこれまでに何冊借りたか」を集計 - 1 人が同時に借りられる上限(3 冊など)を設けて、超過時は例外を投げる
- CSV だけでなく JSON でも保存できるよう、
IBookSerializerインターフェイスを追加してCsvSerializerを実装する(JSON は標準ライブラリ範囲で実装するか、実装は CSV 1 種類のままで「設計だけ」考えてもよい)
I-6 仕上げ:振り返り
Section titled “I-6 仕上げ:振り返り”時間が残ったら、書いたコードを次の観点で見直してください。
| 観点 | 確認内容 |
|---|---|
| クラスの責任 | Repository と Book の責務が分かれているか。1 クラスがあれもこれもしていないか |
| 継承 vs インターフェイス | 「is-a」なら継承、「機能の約束」ならインターフェイスを選んでいるか(第 14 章 14-6) |
| 抽象化の階層 | 抽象クラスのメソッドのうち、共通実装が書けるものは virtual、書けないものは abstract にしているか |
| 例外処理 | 想定外を throw、想定内の入力検証は if か TryParse で返しているか |
| LINQ の使いどき | foreach でループしながらフラグを立てるより、Where / Count / Sum の方が短く書けないか |
| マジックナンバー | 貸出日数の 14 のような数字に名前(定数 or プロパティ)を付けているか |
完璧を目指す必要はありません。「書いた後に自分で読み直して、別の書き方も考えてみる」 習慣そのものが、配属後に効きます。
この付録のまとめ
Section titled “この付録のまとめ”- レベル 1(I-3-1〜I-3-3):第 12〜14 章の典型例を 別題材で素振り(LINQ / 継承+ポリモーフィズム / インターフェイス)
- レベル 2(I-4-1, I-4-2):例外処理を主軸にした 堅牢性、継承 + インターフェイス + LINQ の 組み合わせ
- レベル 3(I-5-1):Repository パターン + 独自例外 + LINQ + CSV を備えたミニアプリ
- 完成しなくても OK。書いて、読み直して、質問する サイクルを回すのがいちばんの収穫
次は第 16 章「SQLServer 環境構築」へと進みます。ここまで身につけた List<T> / LINQ / インターフェイス / 例外処理は、DB 接続後も Repository クラスや結果のフィルタ・集計でそのまま使い続けます。