【設計入門】おにぎりで例えるストラテジパターン

ここでは設計を考え出した方のために「ストラテジパターン(Strategy)」をとても簡単に解説します。

ストラテジパターンはふるまいを入れ替え可能にする手法ですが、ここではおにぎりで考えてみましょう。

ピクニックに行こう

皆さんはピクニックに行くなら何を持っていきますか?

サンドイッチやコーヒー、道中でパン屋に寄るのもいいですね。(あ、持っていくのは食べ物です。)今回は「おにぎり」を持っていきましょう。

ここに「ピクニックの準備をする」クラスと、”鮭おにぎり”を作るメソッドがあります。

 class PreparingForPicnic
 {
     public void MakeSalmonRiceBalls()
     {
         Console.WriteLine("おにぎりを作ります。");
         Console.WriteLine("鮭をほぐします。");
         Console.WriteLine("おにぎりができました。");
     }
 }

”鮭おにぎり”を持っていくみたいですね。ですが、私はシーチキンマヨも好きなので、”シーチキンマヨおにぎり”も作ってほしいところです。

class PreparingForPicnic
{
    public void MakeSalmonRiceBalls()
    {
        Console.WriteLine("おにぎりを作ります。");
        Console.WriteLine("鮭おにぎりを作っています。");
        Console.WriteLine("鮭おにぎりができました。");
    }

    // シーチキンマヨおにぎりも作っちゃう
    public void MakeTunaMayoRiceBalls()
    {
        Console.WriteLine("おにぎりを作ります。");
        Console.WriteLine("ツナマヨを作っています。");
        Console.WriteLine("シーチキンマヨおにぎりができました。");
    }
}

いいですね。シーチキンマヨも作れそうです。ですが、昆布やエビマヨ…と種類を増やす度にメソッドを増やさなくてはいけません。おにぎりのように中身だけ交換する方法はないでしょうか。

具を変えるように「ふるまい」を変える

ストラテジパターンとは、Interfaceを用いてその「ふるまい」を入れ替え可能にする方法です。

例えば、「おにぎり」を表す Interface を用意します。

interface IOnigiri
{
    // 名前
    string Name { get; }

  // 作り方の説明
    string GetCookingDescription();
}

「ピクニックの準備をする」クラスでは、その Inteface を受け取るようにします。「ピクニックの準備をする」クラスはそのおにぎりが鮭なのか、高菜なのか、はたまたシーチキンマヨなのかを知る必要はありません。(ちなみに私はシーチキンマヨが好物です)

 class PreparingForPicnic
 {
     private readonly IOnigiri _onigiri;

     public PreparingForPicnic(IOnigiri onigiri)
     {
         _onigiri= onigiri;
     }

     public void MakeRiceball()
     {
         Console.WriteLine("おにぎりを作ります。");
         Console.WriteLine(_onigiri.GetCookingDescription());
         Console.WriteLine($"{_onigiri.Name}ができました。");
     }
 }

「おでかけ準備」クラスは以下を担っています。

  • おでかけ準備の流れ
  • IOnigiriGetCookingDescription()メソッドの呼び出し

では、「鮭おにぎり」クラスを実装してみましょう。

class SalmonOnigiri : IOnigiri
{
    public string Name => "鮭おにぎり";

    public void GetCookingDescription()
    {
       return "鮭をほぐします。";
    }
}

実際に「ピクニックの準備」クラスを利用する時は以下のようになります。

// 今回は鮭おにぎりクラスを渡す
var salmonOnigiri = new SalmonOnigiri();

var preparingForPicnic = new PreparingForPicnic(salmonOnigiri);

preparingForPicnic.MakeRiceball();

// (出力結果)
// おにぎりを作ります。
// 鮭をほぐします。
// 鮭おにぎりができました。

もしこれでシーチキンマヨおにぎりも作りたくなったとしても、シーチキンマヨおにぎりクラスを作って、PreparingForPicnicコンストラクタに渡せばいいのです。

おにぎりの具のように、「違うところだけ」を入れ替えられますね。

// シーチキンマヨおにぎり
class TunaMayoOnigiri : IOnigiri
{
    public string Name => "シーチキンマヨおにぎり";

    public void GetCookingDescription()
    {
       return "ツナマヨを作っています。";
    }
}

// ピクニック準備クラスに渡す
var tunaMayoOnigiri = new TunaMayoOnigiri ();
var preparingForPicnic = new PreparingForPicnic(salmonOnigiri);
preparingForPicnic.MakeRiceball();

// (出力結果)
// おにぎりを作ります。
// ツナマヨを作っています。
// シーチキンマヨおにぎりができました。

ざっくりイメージできたでしょうか。

これの何が良いかって、

  • 重複コードを減らす
  • 「おにぎりを作る流れ」と「具材の用意」が分離

しているところにあります。

「重複コード」を減らす良さはもちろんですが、今後おにぎりにのりを巻いたり、ラップに包んだりするとソースが肥大化していきます。そうすると、「このクラスは何をしているんだ?」ってなってきます。具材も然りですね。ツナマヨに使うマヨネーズから自作しだすかもしれません(ちょっと面白いかも)。

分けることでクラスの責務がはっきりし、ソースも読みやすいものになります。

今回は簡単にするためにおにぎりで例えました。イメージが掴めたなら他のサイトの記事を見るとより自分のものにできると思います。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール