#はじめに
これは某社内で実施するデザインパターン勉強会で使用する資料です。
書籍「Java言語で学ぶデザインパターン」をベースに学習を進めますが、サンプルプログラムはC#に置き換えて解説します。
第一回:Iteratorパターン
第三回:Template Methodパターン
#Adapterパターンとは
Adapterパターンとは、目的のクラスに対して既存のクラスがそのまま利用できない場合に、間に入ってズレを埋めるようなデザインパターンです。
Adapterパターンには以下の2種類があります。
- クラスによるAdapterパターン(継承を使ったもの)
- インスタンスによるAdapterパターン(委譲を使ったもの)
ここでは文字列を変換して表示するサンプルプログラムで紹介します。
#クラスによるAdapterパターン(継承を使ったもの)
##サンプルプログラムクラス図
文字列を変換して表示するプログラムを実装したモデルは次の通りです。
##クラスの役割
クラス名 | 役割 |
---|---|
Banner | あらかじめ提供されているクラス |
IPrint | 必要とされているインターフェース |
PrintBanner | アダプターの役目を果たすクラス |
Program | PrintBannerを使って文字列を表示するクラス |
##Banner
あらかじめ提供されていることを想定したクラスです。
文字列を表示するShowWIthParenメソッド、ShowWithAsterメソッドが実装されていますが、今回はこのまま使用することができません。
public class Banner
{
private readonly string str;
public Banner(string str)
{
this.str = str;
}
public void ShowWithParen()
{
Console.WriteLine($"({str})");
}
public void ShowWithAster()
{
Console.WriteLine($"*{str}*");
}
}
##IPrint
今回必要とされているインターフェースです。
public interface IPrint
{
void PrintWeak();
void PrintStrong();
}
##PrintBanner
アダプターの役目を果たすクラスです。
Bannerクラスを継承して、ShowWIthParenメソッド、ShowWithAsterメソッドを継承します。
IPrintインターフェースを実装して、PrintWeakメソッド、PrintStrongメソッドを実装します。
public class PrintBanner : Banner, IPrint
{
public PrintBanner(string str) : base(str)
{
}
public void PrintStrong()
{
ShowWithAster();
}
public void PrintWeak()
{
ShowWithParen();
}
}
##Program
アダプターの役目であるPrintBannerクラスを利用して、Helloという文字列をカッコ付き、また、*で挟んで表示します。
class Program
{
static void Main(string[] args)
{
IPrint print = new PrintBanner("Hello");
print.PrintWeak();
print.PrintStrong();
Console.ReadLine();
}
}
##Program実行結果
Programを実行した結果が以下の通りです。
Helloという文字列がカッコ付き、また、*で挟んで表示されています。
(Hello)
*Hello*
#インスタンスによるAdapterパターン(委譲を使ったもの)
##サンプルプログラムクラス図
文字列を変換して表示するプログラムを実装したモデルは次の通りです。
先ほどのクラスによるAdapterパターンと異なるのはPrintBannerクラスのみです。
##PrintBanner
アダプターの役目を果たすクラスです。
先ほどと異なるのは、Bannerクラスを継承せず、bannerフィールドでBannerクラスのインスタンスを保持していることです。
PrintWeakメソッド、PrintStrongメソッドの処理はフィールドを経由してBannerクラスのインスタンスが行います。
public class PrintBanner : Print
{
private Banner banner;
public PrintBanner(String str)
{
banner = new Banner(str);
}
public override void PrintWeak()
{
banner.ShowWIthParen();
}
public override void PrintStrong()
{
banner.ShowWithAster();
}
}
#Adapterパターンのメリット
Adapterパターンは既存のクラスには手を加えずに目的のインターフェースに合わせるものです。
そのため、既存のクラスが十分にテストされていれば、新規に作成したAdapter役のクラスをテストするのみで良いです。
#継承と委譲
ところで、継承と委譲(コンポジション)が登場しましたがどちらを使用するのが良いでしょうか?
私は、基本的に委譲を使うのが良いのではと考えています。
理由は以下の通りです。
- 継承の場合、子クラスの実装は親クラスを意識して行う必要があり、
意図せずにメンバーの隠匿を行ってしまった場合には動作が変わってしまう。 - 委譲の場合、既存クラスのインスタンスをフィールドに持ち、
必要なメソッドを呼び出すため、継承のように他のクラスを意識して実装する必要がない。
#サンプルコード
以下に公開しています。
クラスによるAdapterパターン(継承を使ったもの)
https://github.com/Nanakusajp/AdapterPattern-Inheritance
インスタンスによるAdapterパターン(委譲を使ったもの)
https://github.com/Nanakusajp/AdapterPattern-Delegation