ポリモーフィズムを使った図書館のサンプル
図書館のシステムを想定して、サンプルを考えてみましょう
継承を使う
// 実行スタート
new LibrarySystem().Run();
// 基底クラス Book:タイトルと著者を保持し、汎用的に印刷する
class Book
{
public string Title { get; set; }
public string Author { get; set; }
public virtual void Print()
{
Console.WriteLine("Title: " + Title);
Console.WriteLine("Author: " + Author);
}
}
// 紙の書籍:ページ数を追加し、Print()を拡張
class PaperBook : Book
{
public int Pages { get; set; }
public override void Print()
{
base.Print(); // 基底の出力処理を再利用
Console.WriteLine("Pages: " + Pages);
}
}
// 音声書籍:再生時間を追加し、Print()を拡張
class AudioBook : Book
{
public double Duration { get; set; }
public override void Print()
{
base.Print();
Console.WriteLine("Duration: " + Duration + " minutes");
}
}
// Library:Bookのリストを保持し、登録と印刷を管理する
class Library
{
private List<Book> books = new List<Book>();
public void AddBook(Book book)
{
books.Add(book);
}
public void PrintBooks()
{
foreach (var book in books)
{
book.Print(); // 各Book型に応じたPrint()を呼び出す
}
}
}
class LibrarySystem
{
Library library;
public void Run()
{
library = new Library();
library.AddBook(new PaperBook { Title = "The Catcher in the Rye", Author = "J.D. Salinger", Pages = 214 });
library.AddBook(new AudioBook { Title = "The Catcher in the Rye", Author = "J.D. Salinger", Duration = 5.5 });
library.PrintBooks(); // 登録した本すべてを出力
}
}
実行結果
Title: The Catcher in the Rye
Author: J.D. Salinger
Pages: 214
Title: The Catcher in the Rye
Author: J.D. Salinger
Duration: 5.5 minutes
このコードは、図書館のシステムを表しています。 Bookクラスが持つプロパティとメソッドを継承し、それぞれの本の特徴を表すPaperBookクラスとAudioBookクラスが定義されています。 Print()メソッドは、オーバーライドされて、本の特徴を出力するようになっています。 Libraryクラスは、本のリストを保持し、追加や出力を行うメソッドを持っています

インターフェイスを使う
new LibrarySystem().Run();
// IPrintableインターフェイス:Printメソッドの契約
interface IPrintable
{
void Print();
}
// Bookも印刷可能なクラスとして定義
class Book : IPrintable
{
public string Title { get; set; }
public string Author { get; set; }
public void Print()
{
Console.WriteLine("Title: " + Title);
Console.WriteLine("Author: " + Author);
}
}
// 紙の本:Printを隠蔽(new)
class PaperBook : Book
{
public int Pages { get; set; }
public new void Print()
{
base.Print();
Console.WriteLine("Pages: " + Pages);
}
}
// 音声本:同様に隠蔽してPrintを実装
class AudioBook : Book
{
public double Duration { get; set; }
public new void Print()
{
base.Print();
Console.WriteLine("Duration: " + Duration + " minutes");
}
}
// Library:IPrintable型で本を管理
class Library
{
private List<IPrintable> books = new List<IPrintable>();
public void AddBook(IPrintable book)
{
books.Add(book);
}
public void PrintBooks()
{
foreach (var book in books)
{
book.Print(); // IPrintableとして一括処理
}
}
}
実行結果
Title: The Catcher in the Rye
Author: J.D. Salinger
Pages: 214
Title: The Catcher in the Rye
Author: J.D. Salinger
Duration: 5.5 minutes
このコードでは、インターフェイスIPrintableを定義し、Bookクラス、PaperBookクラス、AudioBookクラスがそれを実装しています。 Libraryクラスは、IPrintable型のリストを保持し、追加や出力を行うメソッドを持っています。これにより、派生クラスにインターフェイスを実装することで多態性を実現することができます。
インターフェイスを使用することで、複数のクラスに共通の機能を実装することができ、多態性を実現することができます。また、インターフェイスは「契約」として扱うことができ、それを実装することでクラスが持つべき機能を明確にすることができます

抽象クラスを使う
new LibrarySystem().Run();
// 抽象クラス:Printの実装は派生クラスに任せる
abstract class Book
{
public string Title { get; set; }
public string Author { get; set; }
public abstract void Print();
}
// 紙の本:抽象メソッドをオーバーライド
class PaperBook : Book
{
public int Pages { get; set; }
public override void Print()
{
Console.WriteLine("Title: " + Title);
Console.WriteLine("Author: " + Author);
Console.WriteLine("Pages: " + Pages);
}
}
// 音声本:Printの具象実装
class AudioBook : Book
{
public double Duration { get; set; }
public override void Print()
{
Console.WriteLine("Title: " + Title);
Console.WriteLine("Author: " + Author);
Console.WriteLine("Duration: " + Duration + " minutes");
}
}
// Library:Book型として多態性を扱う
class Library
{
private List<Book> books = new List<Book>();
public void AddBook(Book book)
{
books.Add(book);
}
public void PrintBooks()
{
foreach (var book in books)
{
book.Print();
}
}
}
実行結果
Title: The Catcher in the Rye
Author: J.D. Salinger
Pages: 214
Title: The Catcher in the Rye
Author: J.D. Salinger
Duration: 5.5 minutes
このコードは、抽象クラスを使用して、同じように図書館のシステムを表しています。 Bookクラスが抽象メソッドPrint()を定義し、PaperBookクラス、AudioBookクラスがそれを実装しています。 Libraryクラスは、Book型のリストを保持し、追加や出力を行うメソッドを持っています。この方法でも、多態性を実現することができます。
抽象クラスはインターフェイスと同じように多態性を実現するために使用できますが、抽象クラスには実装を持ったメソッドやプロパティがあります。インターフェイスは抽象メソッドやプロパティだけを持つため、実装の詳細を隠蔽し、抽象的なインターフェースを提供することができます。

抽象クラスインターフェイスを組み合わせて使う
new LibrarySystem().Run();
interface IPrintable
{
void Print();
}
abstract class Book : IPrintable
{
public string Title { get; set; }
public string Author { get; set; }
public abstract void Print();
}
class PaperBook : Book
{
public int Pages { get; set; }
public override void Print()
{
Console.WriteLine("Title: " + Title);
Console.WriteLine("Author: " + Author);
Console.WriteLine("Pages: " + Pages);
}
}
class AudioBook : Book
{
public double Duration { get; set; }
public override void Print()
{
Console.WriteLine("Title: " + Title);
Console.WriteLine("Author: " + Author);
Console.WriteLine("Duration: " + Duration + " minutes");
}
}
class Library
{
private List<IPrintable> books = new List<IPrintable>();
public void AddBook(IPrintable book)
{
books.Add(book);
}
public void PrintBooks()
{
foreach (var book in books)
{
book.Print();
}
}
}
class LibrarySystem
{
Library library;
public void Run()
{
library = new Library();
library.AddBook(new PaperBook { Title = "The Catcher in the Rye", Author = "J.D. Salinger", Pages = 214 });
library.AddBook(new AudioBook { Title = "The Catcher in the Rye", Author = "J.D. Salinger", Duration = 5.5 });
library.PrintBooks();
}
}
実行結果
Title: The Catcher in the Rye
Author: J.D. Salinger
Pages: 214
Title: The Catcher in the Rye
Author: J.D. Salinger
Duration: 5.5 minutes
このコードでは、Bookクラスを抽象クラスにし、IPrintableインターフェイスを実装しています。派生クラスのPaperBookクラス、AudioBookクラスもIPrintableインターフェイスを実装しています。 抽象クラスはインスタンスを生成することができないが、仮想メソッドを持っており、派生クラスにて実装しなければならないメソッドがある。 このように抽象クラスとインターフェイスを組み合わせることで、共通の機能を抽象クラスで定義し、派生クラスにて実装することで多態性を実現することができます。
抽象クラスはインターフェイスと同じように多態性を実現するために使用できますが、抽象クラスには実装を持ったメソッドやプロパティがあります。インターフェイスは抽象メソッドやプロパティだけを持つため、実装の詳細を隠蔽し、抽象的なインターフェースを提供することができます。

ファクトリーパターンで考える
new LibrarySystem().Run();
// Bookの生成を専門にするファクトリー
abstract class Book { public abstract void Print(); }
class PaperBook : Book { /* Print override */ }
class AudioBook : Book { /* Print override */ }
class BookFactory
{
public static Book CreateBook(string type)
{
switch (type)
{
case "paper": return new PaperBook();
case "audio": return new AudioBook();
default: throw new ArgumentException("Invalid book type.");
}
}
}
class Library { /* IList<Book>, AddBook, PrintBooks */ }
class LibrarySystem
{
public void Run()
{
var library = new Library();
Book paper = BookFactory.CreateBook("paper");
paper.Title = "..."; ((PaperBook)paper).Pages = 214;
library.AddBook(paper);
Book audio = BookFactory.CreateBook("audio");
audio.Title = "..."; ((AudioBook)audio).Duration = 5.5;
library.AddBook(audio);
library.PrintBooks();
}
}
実行結果
Title: The Catcher in the Rye
Author: J.D. Salinger
Pages: 214
Title: The Catcher in the Rye
Author: J.D. Salinger
Duration: 5.5 minutes
LibrarySystemメソッドでは、BookFactoryクラスのCreateBookメソッドを使用して、PaperBookクラスとAudioBookクラスのインスタンスを生成しています。生成したインスタンスは、Bookクラスの変数に代入され、各プロパティに対して値を設定しています。最後に、LibraryクラスのAddBookメソッドを使用して、生成したインスタンスを追加し、PrintBooksメソッドを呼び出すことで書籍の一覧を出力します。
これにより、インスタンス生成の責務をBookFactoryクラスに移すことで、LibrarySystemメソッドからはインスタンス生成に関する詳細を除外することができ、コードの可読性を高めることができます。

オブザーバーパターンで考える
new LibrarySystem().Run();
// 通知を受け取るオブジェクトのインターフェイス
interface IObserver { void Update(); }
interface IPrintable { void Print(); }
abstract class Book : IPrintable { /* ... */ }
class Library
{
private List<IObserver> observers = new List<IObserver>();
private List<IPrintable> books = new List<IPrintable>();
public void AddObserver(IObserver ob) { observers.Add(ob); }
public void AddBook(IPrintable book)
{
books.Add(book);
NotifyObservers(); // 本の追加を通知
}
void NotifyObservers() { foreach (var o in observers) o.Update(); }
public void PrintBooks()
{
foreach (var b in books) b.Print();
}
}
// UIなどに使える表示用オブザーバー
class PrintDisplay : IObserver
{
public void Update()
{
Console.WriteLine("New book added to the library");
}
}
実行結果
New book added to the library
New book added to the library
Title: The Catcher in the Rye
Author: J.D. Salinger
Pages: 214
Title: The Catcher in the Rye
Author: J.D. Salinger
Duration: 5.5 minutes
このコードは、図書館の図書を表すクラスを使用しています。オブザーバーパターンに準拠しているため、図書館が図書を追加するたびに、登録されているオブザーバーに通知が送られます。
インターフェイス IObserver
は、通知を受け取るオブジェクトが実装する必要があるメソッド Update()
を定義しています。
インターフェイス IPrintable
は、印刷可能なオブジェクトが実装する必要があるメソッド Print()
を定義しています。
抽象クラス Book
は、タイトルと著者を持つ印刷可能なオブジェクトを表し、 Print()
メソッドを抽象メソッドとして定義しています。
クラス PaperBook
は、抽象クラス Book
を継承し、ページ数を追加し、 Print()
メソッドをオーバーライドしています。
クラス AudioBook
は、抽象クラス Book
を継承し、再生時間を追加し、 Print()
メソッドをオーバーライドしています。
クラス Library
は、オブザーバーを登録するためのAddObserver()
メソッドと、図書館に図書を追加するための AddBook()
メソッドを持ち、オブザーバーに通知を送る NotifyObservers()
メソッドを持っています。
クラス PrintDisplay
は、 IObserver
インターフェイスを実装し、 Update()
メソッドをオーバーライドして、新しい本が追加されたことを表示するようにしています。
Main()
メソッドでは、Library
クラスのインスタンス、PrintDisplay
クラスのインスタンスを作成し、それらを使用して、本を追加し、表示しています。
※上記のコードは新しい本が追加された際に一覧表示が出るだけなので、実際には不要な部分もあります。

DIパターンで考える
インターファイスを使って説明しています
上記インターフェイスパターンと同様なことがわかると思います
DIパターンに変更するためには、以下のようにします。
依存性の注入に使用するインターフェイスを定義します。
// IBookインターフェイス:Printの契約
interface IBook
{
void Print();
}
Book, PaperBook, AudioBookクラスは、IBookインターフェイスを継承します。
class Book : IBook
{
public string Title { get; set; }
public string Author { get; set; }
public void Print()
{
Console.WriteLine("Title: " + Title);
Console.WriteLine("Author: " + Author);
}
}
class PaperBook : IBook
{
public int Pages { get; set; }
public void Print()
{
Console.WriteLine("Title: " + Title);
Console.WriteLine("Author: " + Author);
Console.WriteLine("Pages: " + Pages);
}
}
class AudioBook : IBook
{
public double Duration { get; set; }
public void Print()
{
Console.WriteLine("Title: " + Title);
Console.WriteLine("Author: " + Author);
Console.WriteLine("Duration: " + Duration + " minutes");
}
}
Libraryクラスには、IBookインターフェイスを使用して、依存性を注入します。
class Library
{
private List<IBook> books = new List<IBook>();
public void AddBook(IBook book)
{
books.Add(book);
}
public void PrintBooks()
{
foreach (var book in books)
{
book.Print();
}
}
}
LibrarySystemで、実際のBook, PaperBook, AudioBookオブジェクトを注入します
new LibrarySystem().Run();
class LibrarySystem
{
Library library;
public void Run()
{
library = new Library();
library.AddBook(new PaperBook { Title = "The Catcher in the Rye", Author = "J.D. Salinger", Pages = 214 });
library.AddBook(new AudioBook { Title = "The Catcher in the Rye", Author = "J.D. Salinger", Duration = 5.5 });
library.PrintBooks();
}
}
このように、DIパターンを使用することで、Libraryクラスが依存しているオブジェクトを実装に関係なく、外部から注入することができるようになります。
実行結果
Title: The Catcher in the Rye
Author: J.D. Salinger
Title: The Catcher in the Rye
Author: J.D. Salinger

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