C#オブジェクト指向チュートリアル――「仕事を頼む」からクラス設計を学ぶ
C#のクラスやメソッドの文法は覚えたものの、「どのクラスに、どの処理を書けばよいのか分からない」と感じることはないでしょうか。
このチュートリアルでは、オブジェクト指向を「継承を使うこと」ではなく、役割を持ったオブジェクトに仕事を頼むこととして学びます。小さな車のプログラムを段階的に作り、メッセージ、責務、委譲、インターフェース、コンポジションまでをつなげます。
このチュートリアルのゴール
最終的に、次のような関係をC#で表せるようになることが目標です。
- 利用者は
Carへ「走り始めて」と頼む Carはエンジン始動をIEngineへ任せる- ガソリンエンジンと電気モーターを差し替えられる
- 各クラスが自分の責務だけを担当する
完成形を先に考えるのではなく、「誰に、どんな仕事を頼むか」を一段ずつコードにしていきます。
準備
Visual StudioまたはVisual Studio Codeで、C#のコンソールアプリを一つ作成してください。このチュートリアルのコードは、すべてProgram.csへ書いて試せます。
最初は「クラスをきれいに分ける」ことを急がず、動作を確認しながら役割を見つけていきます。
ステップ1:オブジェクトへ仕事を頼む
まず、エンジンを表すEngineクラスを作ります。
Engine engine = new Engine();
engine.Start();
class Engine
{
public void Start()
{
Console.WriteLine("エンジンが始動しました");
}
}
engine.Start()は、単なるメソッド呼び出しとして見ることもできます。オブジェクト指向では、engineオブジェクトへ「始動して」とメッセージを送っていると考えます。
ここで確認すること
- メッセージを受け取る相手は
engine - お願いしている仕事は
Start - 始動方法を知っているのは
Engine
呼び出す側は、点火などの具体的な手順を知りません。相手に希望を伝え、実際の処理は相手へ任せています。
ステップ2:Carの責務を考える
次に、車を表すCarクラスを追加します。利用者が直接エンジンを操作するのではなく、車へ「走り始めて」と頼める形にします。
Engine engine = new Engine();
Car car = new Car(engine);
car.Start();
class Car
{
private readonly Engine _engine;
public Car(Engine engine)
{
_engine = engine;
}
public void Start()
{
Console.WriteLine("車が走り始めます");
_engine.Start();
}
}
class Engine
{
public void Start()
{
Console.WriteLine("エンジンが始動しました");
}
}
実行結果は次のとおりです。
車が走り始めます
エンジンが始動しました
Carは、車として利用者からの依頼を受け付けます。ただし、エンジンを始動する詳しい処理まで自分で抱えず、Engineへ任せます。このように、受け取った仕事の一部を別のオブジェクトへ頼むことを委譲と呼びます。

責務を一言で表す
| クラス | 責務 |
|---|---|
Car | 車として「走り始めて」という依頼を受け付ける |
Engine | エンジンを始動する |
責務を短い言葉で説明できると、処理をどこに置くべきか判断しやすくなります。
ステップ3:具体的なクラスへの依存を見直す
現在のCarは、具体的なEngineクラスを知っています。ここで「電気自動車にも同じCarを使いたい」という要求が追加されたとします。
ガソリンエンジンと電気モーターでは、始動の仕組みが異なります。しかし、Carが相手へ頼みたいことは、どちらも「始動して」です。
相手が何者かではなく、どんなメッセージに応答できるかに注目します。
ステップ4:インターフェースで役割を表す
「始動できる」という役割を、IEngineインターフェースとして表します。
interface IEngine
{
void Start();
}
class GasolineEngine : IEngine
{
public void Start()
{
Console.WriteLine("ガソリンエンジンが始動しました");
}
}
class ElectricMotor : IEngine
{
public void Start()
{
Console.WriteLine("電気モーターが静かに起動しました");
}
}
IEngineは「どのように始動するか」を決めません。「Startというメッセージに応答できる」という約束だけを表します。
これがインターフェースの大切な役割です。共通データをまとめるためではなく、相手に期待する能力を表すために使えます。
ステップ5:コンポジションで部品を差し替える
Carが受け取る型をIEngineへ変更します。
class Car
{
private readonly IEngine _engine;
public Car(IEngine engine)
{
_engine = engine;
}
public void Start()
{
Console.WriteLine("車が走り始めます");
_engine.Start();
}
}
Carは、内部で使う部品を継承によって固定するのではなく、外から受け取っています。オブジェクトを組み合わせて機能を作るこの考え方がコンポジションです。
Carは、渡された相手がIEngineの約束を守っていることだけを知っています。ガソリンなのか電気なのかを判断するif文も必要ありません。

完成コード
ここまでの内容を、一つのプログラムにまとめます。
Car gasolineCar = new Car(new GasolineEngine());
Car electricCar = new Car(new ElectricMotor());
gasolineCar.Start();
Console.WriteLine();
electricCar.Start();
interface IEngine
{
void Start();
}
class GasolineEngine : IEngine
{
public void Start()
{
Console.WriteLine("ガソリンエンジンが始動しました");
}
}
class ElectricMotor : IEngine
{
public void Start()
{
Console.WriteLine("電気モーターが静かに起動しました");
}
}
class Car
{
private readonly IEngine _engine;
public Car(IEngine engine)
{
_engine = engine;
}
public void Start()
{
Console.WriteLine("車が走り始めます");
_engine.Start();
}
}
実行すると、次のように表示されます。
車が走り始めます
ガソリンエンジンが始動しました
車が走り始めます
電気モーターが静かに起動しました
Carのコードを変更しなくても、組み合わせる部品を変えるだけで振る舞いを切り替えられました。これがポリモーフィズムです。
メッセージの流れを言葉で確認する
- 利用者が
car.Start()でCarへ仕事を頼む Carが依頼を受け取り、車としての処理を行うCarが_engine.Start()でエンジンへ仕事を任せるGasolineEngineまたはElectricMotorが自分の方法で応答する
クラス図や専門用語を先に覚えなくても、この流れを説明できれば設計の中心は理解できています。
よくある書き方との違い
Carがすべての処理を持つ
Carの中へガソリンと電気の始動処理をすべて書くと、車種が増えるたびにCarを変更することになります。責務も増え、クラスが大きくなります。
型をif文で判定する
エンジンの種類を文字列や列挙型で判定すると、新しい種類を追加するたびに分岐を修正します。今回は、同じメッセージにそれぞれのオブジェクトが応答する形にしました。
最初から継承ツリーを作る
「車だから何を継承するか」から始めると、現実世界の分類をコードへ写すことが目的になりがちです。まず必要なメッセージと責務を見つけ、必要になった仕組みだけを選ぶ方が設計しやすくなります。
練習問題
問題1:停止する処理を追加する
IEngineへStopメソッドを追加し、Car.Stop()から仕事を委譲してください。ガソリンと電気で異なるメッセージを表示してみましょう。
問題2:新しいエンジンを追加する
HybridEngineを追加してください。既存のCarを変更せずに使えれば成功です。
問題3:責務を説明する
Car、IEngine、各エンジンクラスの責務を、それぞれ一文で書いてください。説明が長くなるクラスは、複数の責務を抱えていないか見直します。
最後に整理
| 用語 | このチュートリアルでの意味 |
|---|---|
| オブジェクト | 仕事をお願いできる相手 |
| メッセージ | Startなど、相手への依頼 |
| 責務 | そのオブジェクトが応答する仕事 |
| 委譲 | 受け取った仕事の一部を別の相手へ頼むこと |
| インターフェース | 相手に期待する能力の約束 |
| コンポジション | オブジェクトを組み合わせて機能を作ること |
| ポリモーフィズム | 同じメッセージに異なる方法で応答できること |
まとめ
オブジェクト指向の設計では、最初から継承やデザインパターンを使う必要はありません。まず「誰に、どんな仕事を頼みたいか」を考えます。
オブジェクト指向は、役割を持ったオブジェクト同士の会話として捉えると理解しやすくなります。
メッセージと責務を起点にすると、インターフェースやコンポジション、ポリモーフィズムも、文法上の機能ではなく「変更しやすい協力関係を作るための道具」としてつながります。










ディスカッション
コメント一覧
まだ、コメントがありません