Javaで学ぶデザインパターン入門
結城浩「Javaで学ぶデザインパターン入門」をC#で勉強
Adapterパターン
既存のプログラムを必要な形に変換する際に使用するパターン。
Adapterパターンには以下の2種類がある。
- クラスによるAdapterパターン(継承を使ったもの)
- インスタンスによるAdapterパターン(委譲を使ったもの)
継承を使ったもの
・プログラムで重要な点はMainクラスで使用するのはあくまでPrintクラス(必要なもの)だということ。
PrintBannerクラス(アダプター)がどういった実装かはMainクラスは知らないため、PrintBannerクラスに変更を加えても、Mainクラスは変更しないでよい。
AdapterLearn.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DesignPatternLearn.AdapterLearn
{
public class AdapterLearn
{
static void Main(string[] args)
{
Print p = new PrintBanner("Hello");
p.PrintWeak();
p.PrintStrong();
Console.Read();
}
}
/// <summary>
/// 既存のプログラム
/// </summary>
public class Banner
{
private string str;
public Banner(string str)
{
this.str = str;
}
public void ShowWithParen()
{
Console.WriteLine("(" + str + ")");
}
public void ShowWithAster()
{
Console.WriteLine("*" + str + "*");
}
}
/// <summary>
/// 必要なもの
/// </summary>
public interface Print
{
void PrintWeak();
void PrintStrong();
}
/// <summary>
/// アダプター
/// </summary>
public class PrintBanner : Banner, Print
{
public PrintBanner(string str) : base(str) { }
public void PrintWeak()
{
ShowWithParen();
}
public void PrintStrong()
{
ShowWithAster();
}
}
}
委譲を使ったもの
Javaの委譲=「あるメソッドの実際の処理をほかのインスタンスのメソッドに任せること」
AdapterLearn.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DesignPatternLearn.AdapterLearn
{
public class AdapterLearn
{
static void Main(string[] args)
{
Print p = new PrintBanner("Hello");
p.PrintWeak();
p.PrintStrong();
Console.Read();
}
}
/// <summary>
/// 既存のプログラム
/// </summary>
public class Banner
{
private string str;
public Banner(string str)
{
this.str = str;
}
public void ShowWithParen()
{
Console.WriteLine("(" + str + ")");
}
public void ShowWithAster()
{
Console.WriteLine("*" + str + "*");
}
}
/// <summary>
/// 必要なもの
/// インターフェースではなく、クラス
/// </summary>
public abstract class Print
{
public abstract void PrintWeak();
public abstract void PrintStrong();
}
/// <summary>
/// アダプター
/// (((( ここが重要 ))))
/// フィールドのBannerクラスに処理を委譲している。
/// </summary>
public class PrintBanner : Print
{
public Banner banner;
public PrintBanner(string str)
{
banner = new Banner(str);
}
public override void PrintWeak()
{
banner.ShowWithParen();
}
public override void PrintStrong()
{
banner.ShowWithAster();
}
}
}
どういったときに使う?
十分にテストされ、バグが少なく、実際に使用された実績のあるプログラム → 使用したい Adapterパターンを使用することで、必要とするメソッド群を素早く作ることが可能。 またバグが出ても、既存クラスにバグがないことが分かっているっため、アダプターのクラスを重点的に調査すればよく、チェックが楽。
練習問題
問題2-1
問題2-2
propertieクラスがC#にはないため(似たようなクラスはあるようだが、手元の環境になかった)
propertieクラスを使う場所はそれっぽい動きを再現
AdapterTest2_2.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DesignPatternLearn.AdapterLearn
{
class AdapterTest2_2
{
public static void Main(string[] args)
{
FileIO f = new FileProperties();
try
{
f.ReadFromFile("file.txt");
f.SetValue("year", "2004");
f.SetValue("month", "4");
f.SetValue("day", "21");
f.WriteToFile("newfile.txt");
}
catch (InvalidOperationException e)
{
Console.WriteLine(e.StackTrace);
}
}
public interface FileIO
{
void ReadFromFile(string filename);
void WriteToFile(string filename);
void SetValue(string key, string value);
string GetValue(string key);
}
public class FileProperties : FileIO
{
Dictionary<string, string> properties;
public FileProperties()
{
properties = new Dictionary<string, string>();
}
public void ReadFromFile(string filename)
{
var properties = new Dictionary<string, string>();
using (TextReader reader = new StreamReader(filename))
{
// プロパティクラスが手元の環境にないため以下
string line;
while (string.IsNullOrEmpty(line = reader.ReadLine()))
{
string[] keyvalue = line.Split('=');
properties.Add(keyvalue[0], keyvalue[1]);
}
}
}
public void WriteToFile(string filename)
{
foreach (string k in properties.Keys)
{
File.AppendAllText(filename, k + "=" + properties[k]);
}
}
public void SetValue(string key, string value)
{
if (properties.ContainsKey(key))
{
properties[key] = value;
}
}
public string GetValue(string key)
{
if (properties.ContainsKey(key))
{
return properties[key];
}
return "";
}
}
}
}