コードの品質向上のため、Rubyでデザインパターンを解説した名著である Rubyによるデザインパターン で紹介されているデザインパターンを1つずつまとめており、今回が第9弾です。(毎週1つが目標です!)
前回の記事(プロキシパターンのまとめ)はこちらです。
【Rubyによるデザインパターンまとめ8】プロキシパターン - 銀行員からのRailsエンジニア
この本で紹介されているサンプルコードそのままではなく、オリジナルのコードで説明しています。
今回は デコレータ(Decorator)パターン についてまとめました。
デコレータパターンとは
既存のオブジェクトに、機能を簡単に追加するためのパターンです。
層状に機能を積み重ねることができ、状況ごとに必要な機能のみを持つオブジェクトを作ることができます。
Decorator という単語は「装飾者」という意味で、元のオブジェクトに必要な機能を装飾するイメージです。
文章の説明よりも実際のコードを見た方が分かりやすいと思うので、以下のサンプルコードをご覧ください。
サンプルコード
通知を行うプログラムがあるとします。
元々はサービス内での通知だけだったものの、ユーザーの要望で 通知のタイミングでSlack通知、メール通知、またその両方について既存の通知に追加して行う必要があるケースを考えてみます。
デコレータパターンを使うと以下のようにできます。
class Notifier # 元々存在した通知を行うクラス def send(message) puts "サービス内で「#{message}」を通知するよ" end def stop puts '通知を止めるよ' end end class NotifierDecorator # デコレータの基底クラス attr_reader :notifiler def initialize(notifiler) @notifiler = notifiler end def send(message) notifiler.send(message) end def stop notifiler.stop end end class SlackNotifier < NotifierDecorator # slack通知を行うデコレータ def send(message) puts "slackで「#{message}」を通知するよ" notifiler.send(message) end end class MailNotifier < NotifierDecorator # メール通知を行うデコレータ def send(message) puts "メールで「#{message}」を通知するよ" notifiler.send(message) end end
次のように実行します。
notifier = MailNotifier.new(SlackNotifier.new(Notifier.new)) notifier.send('投稿にいいねがついたよ') puts '---' notifier.stop
メール通知・Slack通知を追加したい時に「MailNotifier.new(SlackNotifier.new(Notifier.new))」のように層状に機能を追加していることが分かると思います。
このように、実行時に必要な機能を組み合わせることができます。
実行結果はこうなります。
メールで「投稿にいいねがつきました!」を通知するよ slackで「投稿にいいねがつきました!」を通知するよ サービス内で「投稿にいいねがつきました!」を通知するよ --- 通知を止めるよ
仮にデコレータパターンを使わずに シンプルに継承を使って実現しようとすると、組み合わせごとにクラスを作る必要が出てくるので辛くなります...
また、デコレータの基底クラス(NotifierDecoratorクラス)は、Notifierクラスに処理を移譲しているだけなので、次のようにリファクタリングすることができます。
require 'forwardable' class NotifierDecorator # デコレータの基底クラス extend Forwardable attr_reader :notifiler def_delegators :notifiler, :send, :stop def initialize(notifiler) @notifiler = notifiler end end
Forwardableモジュールの def_delegatorsクラスメソッドは、1つ目の引数にインスタンス名を取り、そのインスタンスに2つ目以降の引数にとったメソッドを移譲します。
これにより、1つ1つのメソッドに移譲の処理を書く必要がなくなりました。
実行の仕方、実行結果は先ほどと同じになります。
おわりに
ここまで読んでいただきありがとうございます。
Rubyによるデザインパターン の中では、いくつかのサンプルコードを用いた説明、またこの記事で紹介しきれなかったリファクタリングについても解説しているので、ご興味ある方は是非合わせてご覧ください。
- 作者:Russ Olsen,ラス・オルセン
- 出版社/メーカー: ピアソン桐原
- 発売日: 2009/04/01
- メディア: 単行本
次回は、シングルトン(Singleton)パターンをまとめます。
来週も頑張ります!
(追記)
シングルトンパターンについてまとめました!
是非合わせてご覧ください。
ysk-pro.hatenablog.com