Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

【Ruby】ストラテジーデザインパターンの使い方を実装例つきでわかりやすく解説

Rubyにおけるストラテジーパターン(Strategy Pattern)について解説します。

このデザインパターンを使うと、メソッドの中核となるアルゴリズムを柔軟に入れ替えられるようになります。

具体的には、アルゴリズムを実装したクラスを引数として渡すことで、そのロジックをクラス内にハードコーディングせずに済む、という仕組みです。

ここでいう「アルゴリズム」とは、難しいコンピュータサイエンスの理論のことではありません。何らかの結果を得るために一連の手順に従って処理を行う、ありふれたコード全般を指します。

ストラテジーパターンはいつ使うべきか

そもそも、このパターンはどんな問題を解決してくれるのでしょうか?

答えは「開放閉鎖の原則(Open/Closed Principle)」に関わる問題です。

この原則は次のように述べています。

「ソフトウェアの構成要素(クラス、モジュール、メソッド)は、拡張に対して開かれ、修正に対して閉じられていなければならない。」

つまり、クラスに新しい振る舞いを持たせたいときに、クラス本体のコードを書き換える必要がない状態が理想です。これにより、コードはより柔軟かつ堅牢になります。

では、どうすればそれを実現できるのでしょうか?

その答えの一つが、ストラテジーパターンのようなデザインパターンの活用です。

具体例:ReportGeneratorクラスの場合

例えば、同じデータから複数の形式のレポートを生成したいReportGeneratorクラスがあるとしましょう。

レポート形式ごとにメソッドを1つずつ用意することもできますが、それだと新しい形式を追加するたびにクラスへメソッドを足す必要があり、開放閉鎖の原則に反してしまいます。

もっと悪いケースとしては、あらゆる条件分岐(if文)が詰め込まれた巨大なメソッドを1つ作ってしまうパターンです。これは往々にして保守が困難になり、バグの温床にもなりがちです。

そこで発想を変えてみましょう。

もし、レポートの整形方法(フォーマット処理)をクラス自身が知らなくてもよかったら?

もし、そのアルゴリズムがクラスの外部から提供されたら?

そうすれば、クラス本体を一切変更することなく、フォーマット処理をいつでも自由に入れ替えられます。

これこそが問題の解決策であり、まさにストラテジーパターンの狙いです。

ストラテジーパターンの実装例

ストラテジー(戦略)は、通常、単一のメソッドを持つクラスとして実装されます。

以下は、ReportGeneratorクラスで使えるフォーマット戦略の例です。

require 'json'

module ReportFormatters
  class JSON
    def self.format(data)
      data.to_json
    end
  end

  class PlainText
    def self.format(data)
      data.to_s
    end
  end

  class HTML
    def self.format(data)
      html = ""

      html << "
    " data.each { |product, amount| html << "
  • #{product}: #{amount}
  • " } html << "
" html end end end

注目すべきは、すべての戦略クラスがformatメソッドを実装している点です。このメソッドが、レポート生成クラスから呼び出されることになります。

続いて、呼び出し側のコードを見てみましょう。

class ReportGenerator
  def self.generate(data, formatter)
    formatter.format(data)
  end
end

data = { onions: 31, potatoes: 24, eggs: 10 }

p ReportGenerator.generate(data, ReportFormatters::HTML)
p ReportGenerator.generate(data, ReportFormatters::JSON)
p ReportGenerator.generate(data, ReportFormatters::PlainText)

出力形式を変更したい場合(例えばHTMLからPlainTextへ)、渡す戦略クラスを変えるだけでOKです。戦略側がformatメソッドさえ実装していれば、問題なく動作します。

コンサルタントにたとえると理解しやすい

このパターンは、「特定のビジネス戦略に精通したコンサルタントを雇う」ことに例えられます。コンサルタントがやってきて戦略を実行し、その結果として具体的な成果物が生まれるイメージです。

一方で、デコレーターパターンは「クリスマスツリーに飾りをどんどん追加して華やかにする」ようなものです。

ストラテジーパターンでは一度に使える戦略は1つだけですが、デコレーターなら飾り付けはいくらでも重ねられます。この違いを押さえておくと、パターンの使い分けがぐっとしやすくなりますよ。

まとめ

今回はRubyのストラテジーデザインパターンについて学びました。クラス本体を変更せずに、クラスの振る舞いの一部を差し替えたいときに活躍するパターンです。

最後までお読みいただきありがとうございました!

この記事が役に立ったと感じたら、ぜひSNS(X/Twitterなど)でシェアしてください。より多くの人の助けになります 🙂

  1. 【Ruby】デコレータデザインパターンでクラスを変更せずにオブジェクトを拡張する方法

    デコレータ(Decorator)デザインパターンとは、どのようなものなのでしょうか? また、このパターンをRubyのプロジェクトではどのように活用すればよいのでしょうか? デコレータデザインパターンを利用すると、クラスそのものを変更することなく、オブジェクトに新しい機能を追加して拡張することができます。 それでは、具体的な例を見ていきましょう! ログ出力とパフォーマンス計測の実装例 この例では、rest-clientのようなGemを使ってHTTPリクエストを送信します。 コードは次のようになります。 require restclient data = RestClient.get(www.

  2. Rubyの配列(Array)クラスの使い方徹底解説!基本操作と便利なメソッド一覧

    配列(Array)とは? 配列はRubyに組み込まれたクラスで、0個以上の要素をリスト形式で保持することができます。さらに、要素の追加・アクセス・繰り返し処理を簡単に行えるメソッドも多数用意されています。 もし配列が存在しなければ、値ごとに個別の変数を用意しなければならず、管理が非常に大変になります。 例: a = 1 b = 2 c = 3 しかし、配列を使えば次のように書けます: numbers = [1, 2, 3] さらに嬉しいことに、配列の中には何でも入れられます! 例えば: 数値 文字列 さらに別の配列!(これは多次元配列になります) それでは、配列を最大限に活用できる