コードの品質向上のため、Rubyでデザインパターンを解説した名著である Rubyによるデザインパターン で紹介されているデザインパターンを1つずつまとめており、今回が第11弾です。(毎週1つが目標です!)
前回の記事(シングルトンパターンのまとめ)はこちらです。
【Rubyによるデザインパターンまとめ10】シングルトンパターン - 銀行員からのRailsエンジニア
言葉での説明よりもコードを見た方が分かりやすいので、サンプルコードでの説明をメインにしています。
今回は ファクトリメソッド(Factory Method)パターン についてまとめました。
ファクトリメソッドパターンとは
オブジェクトの生成をファクトリメソッドに任せることで、クラス名を指定せずにオブジェクトを生成することができるデザインパターンです。
コードの責務を分割して、保守しやすいコードになることがメリットです。
ファクトリ(Factory)とは「工場」という意味で、ファクトリメソッド = オブジェクトを生産するメソッドというイメージです。
文章の説明よりも実際のコードを見た方が分かりやすので、以下のサンプルコードをご覧ください。
サンプルコード
Hashで与えられるデータを指定した形式(json または csv)で出力するプログラムを考えてみましょう。
まずはファクトリメソッドパターンを使わずに実装してみます。
require 'json' class Report def self.generate(data, type) case type when 'json' data.to_json when 'csv' data.keys.join(',') + "\n" + data.values.join(',') end end end
次のように実行すると
data = {age: 28, hobby: 'golf'} puts Report.generate(data, 'json') puts '---' puts Report.generate(data, 'csv')
実行結果はこのようになります。
{"age":28,"hobby":"golf"} --- age,hobby 28,golf
このコードの問題点は、Reportクラスが 以下の2つの責務を持ってしまっている点です。
複数の責務を持ってしまっていることは、有名なSOLID原則の「S」Single Responsibility Principle:単一責任の原則 に反しますね。
このサンプルコードでは対応する形式の種類が少なく、変換のロジックもシンプルなため問題が無いように感じますが、形式の種類が増えたりロジックが複雑になると、可読性が低く保守しづらいコードになってしまいます。
これをファクトリメソッドパターンを使って書き換えてみましょう。
class Formatter def self.type(type) # これがファクトリメソッド! case type when 'json' JsonFormatter.new when 'csv' CsvFormatter.new end end end class JsonFormatter def format(data) data.to_json end end class CsvFormatter def format(data) data.keys.join(',') + "\n" + data.values.join(',') end end class Report def self.generate(data, type) Formatter.type(type).format(data) end end
実行方法、結果は先ほどと同じです。
先ほど課題であった、Reportクラスが持ってしまっていた2つの責務を以下のように分けることができました。
- 対応する形式の定義 → Formatterクラス
- 変換ロジック → JsonFormatter/CsvFormatterクラス
対応する形式を増やす場合や、変換ロジックに修正が入った場合には、FormatterクラスやJsonFormatter/CsvFormatterクラスを修正すればよく、Reportクラスの修正は不要になりました。
Reportクラスの責務はレポートを出力することであり、レポート出力に関係ない変換ロジックなどの修正でReportクラスを変更しなくてよくなり、保守性が上がっていると思います。
またこれはGoFの原則の1つ「変わるものを変わらないものから分離する」にも則っています。
今回のサンプルコードは、こちらのサイトを参考にさせていただきました。英語ですがとても分かりやすくて勉強になりました。
Ruby - Factory Method pattern - Ruby Blog
おわりに
ここまで読んでいただきありがとうございます。
これまでのブログと形式を変えて、「デザインパターンを使っていないコードとその問題点を提示してから、デザインパターンでその問題点を解決していく」という形式で書いてみました。
個人的にはこの方が分かりやすい気がしているので、次回からもこの形で書いていこうと思います。
Rubyによるデザインパターン の中でも、ファクトリメソッドを使っていないコードをファクトリメソッドを使って改善してく流れの説明が分かりやすかったので、ご興味ある方は是非合わせてご覧ください。
- 作者:Russ Olsen,ラス・オルセン
- 発売日: 2009/04/01
- メディア: 単行本
次回は、ビルダ(Builder)パターンをまとめます。
来週も頑張ります!
(追記)
ビルダパターンについてまとめました!
是非合わせてご覧ください。
ysk-pro.hatenablog.com