【Ruby】デコレータデザインパターンでクラスを変更せずにオブジェクトを拡張する方法
デコレータ(Decorator)デザインパターンとは、どのようなものなのでしょうか?
また、このパターンをRubyのプロジェクトではどのように活用すればよいのでしょうか?
デコレータデザインパターンを利用すると、クラスそのものを変更することなく、オブジェクトに新しい機能を追加して拡張することができます。
それでは、具体的な例を見ていきましょう!
ログ出力とパフォーマンス計測の実装例
この例では、rest-clientのようなGemを使ってHTTPリクエストを送信します。
コードは次のようになります。
require 'restclient'
data = RestClient.get("www.rubyguides.com")
さて、ここからが本題です。
一部のリクエストにロギング機能を追加したいけれど、RestClientモジュール自体は変更したくない——そんな場面を想定してみましょう。
このようなときこそ、デコレータパターンの出番です。
まずはロギング用モジュールを作成します。
module LogHTTPRequest
def get(url)
puts "Sending Request For #{url}"
super
end
end
このモジュールは、必要な情報を画面に出力した後、RestClientの元のgetメソッドを呼び出します。
リクエストにロギング機能を追加するには、extendメソッドを使用します。
次のように記述します。
class HTTPClient
def initialize(client = RestClient)
@client = client
end
def get(*args)
@client.get(*args)
end
end
client = HTTPClient.new
client.extend(LogHTTPRequest)
client.get("rubyguides.com")
ここでHTTPClientというラッパークラスを作成しているのは、RestClientがモジュールであり、モジュールからは直接オブジェクトを生成できないためです。
デコレータパターンを使うべき場面
このパターンの優れた点は、1つのオブジェクトだけを拡張しているところにあります。つまり、ログを出力しないクライアントと、ログを出力するクライアントを簡単に使い分けることができます。
さらに、デコレータは複数重ねて適用することも可能です。パフォーマンス計測用のデコレータ、ロギング用のデコレータなど、目的に応じて自由に組み合わせられます。
もし継承を使ってHTTPClientWithLoggingやHTTPClientWithPerformanceTestingといったクラスを作っていたら、このような柔軟な組み合わせは実現できません。

WithLoggingとWithPerformanceTestingを同時に使いたい場合、継承では次のようなクラスを作るしかありません。
class HTTPClientWithLoggingAndPerformanceTesting # ... end
デコレータパターンは、この問題を解決しながら、コードの重複や冗長なクラス名を避けることができます。
まとめ
今回はデコレータデザインパターンについて学びました。このパターンを使えば、クラスを変更することなくオブジェクトに新しい機能を追加できます。
読むだけで終わりにせず、ぜひご自身でもこの概念を実践してみてください。実際に手を動かすことで、必要なときにすぐ思い出せるようになります。
この記事をシェアして、より多くの人に読んでもらいましょう 🙂
-
Rubyで学ぶSOLID設計原則:変更に強いコードを書くための5つの原則
すべてのソフトウェアは時間とともに変化します。そして、その変更は予期しない連鎖的な問題を引き起こすことがあります。しかし、変化しないソフトウェアを作ることはできないため、変更そのものは避けられません。ソフトウェアが成長するにつれて要件も変わり続けるからです。私たちにできるのは、変更に強い(レジリエントな)設計をすることです。適切な設計には最初に時間と労力がかかりますが、長期的には多大な時間と手間を節約してくれます。密結合なソフトウェアは壊れやすく、変更によって何が起きるかを予測できません。 設計の悪いソフトウェアには、次のような悪影響があります。 コードが移動・再利用できない(不動性)。
-
AASM Gemで学ぶRubyステートマシン入門|Stateデザインパターンから実践的な使い方まで
今回は、ステートマシン(状態遷移マシン)の仕組みと、RubyプロジェクトでAASM gemを使ってステートマシンを活用する方法を解説します。 まずは身近な例から考えてみましょう。 信号機を想像してください。信号機は「赤」「黄」「青」のいずれかの状態を取ります。 そして、色が変わるとき、次に何色になるかは現在の色によって決まっています。 ここでは、視覚障害のある方が渡れるタイミングを音で知らせるタイプの信号機だとしましょう。 さて、ここからが本題です。 あなたがこの信号機のソフトウェアを開発することになったとします。 毎回どの音を鳴らすべきか、次はどの色に変わるべきか——それをどう判断すればよ