こんにちは。
「綺麗なコードを書く」って難しくないですか?僕は結構悩んでます。
そんな「コードの品質向上」という僕の課題に対し、マネージャーから「デザインパターンを勉強してみては」とアドバイスをいただき、「Rubyによるデザインパターン」で紹介されているデザインパターンを毎週1つずつアウトプットしはじめました。
やりはじめてから5ヶ月経ち、 Rubyによるデザインパターン で紹介されている16パターン全てをアウトプットし終えたので、まとめておこうと思います。
ほとんどの記事の中でオリジナルのコードを使って説明しているので、気になるデザインパターンがあれば見てみてください。文章での説明よりもコードを見た方がわかりやすいと思います。
Template Method パターン
基底クラスに骨格となる抽象的な処理を書き、サブクラスに具体的な処理を定義するパターンです。
アルゴリズムに多様性を持たせたい場合に便利なパターンで、不変となる部分は基底クラスに書き、変わる部分はサブクラスのメソッドに定義します。
Javaなどでサポートされている抽象メソッドや抽象クラスはRubyではサポートされていないので、呼び出した時に例外を投げるようにして擬似的に抽象メソッドを実装します。
最近ハマっている筋トレネタのサンプルコードで説明しています → 【Rubyによるデザインパターンまとめ1】テンプレートメソッド / Template Method - 銀行員からのRailsエンジニア
Strategyパターン
アルゴリズムの変化する部分をクラスに閉じ込めて、アルゴリズムを実行する際はそのクラスに処理を委譲するパターンです。
アルゴリズムに多様性を持たせたい場合に利用するのはTemplateメソッドパターンと同じですが、Strategyパターンは継承を使わずに実現することができます。(Templateメソッドパターンは継承を使っており、継承にはサブクラスがスーパークラスに依存してしまうというデメリットがあります)
Strategyパターンの「Strategy(戦略)」は、アルゴリズムのことです。Strategyパターンという名前は、変化するアルゴリズム(=Strategy)を、クラスに閉じ込めてそのアルゴリズムを使うオブジェクトに引き渡すことからこの名が付けられたそうです。
僕の好きなスポーツネタのサンプルコードで説明しています →【Rubyによるデザインパターンまとめ2】ストラテジーパターン - 銀行員からのRailsエンジニア
Observerパターン
あるオブジェクトの状態が変化した時に、そのオブジェクトが変化したことを知る必要があるオブジェクトに通知をするデザインパターンです。
Rubyによるデザインパターン では次のように説明されていました。
GoFは「何らかのオブジェクトが変化した」というニュースの発信者と消費者の間に綺麗なインターフェイスを作るアイデアを、Observerパターンと呼んでいます。
(GoFとは、ギャング オブ フォーの略でデザインパターンを広めた「オブジェクト指向における再利用のためのデザインパターン」の4人の著者のことです)
変化したという通知を受け取るオブジェクトのことを「Observer」(観察者)と呼ぶことから、Observerパターンと名付けられました。
ECサイトの通知を題材にしたサンプルコードで説明しています →【Rubyによるデザインパターンまとめ3】オブザーバーパターン - 銀行員からのRailsエンジニア
Compositeパターン
あるものと、それが集まってできたものを同じように扱うことができるデザインパターンです。
例えば、会社は人が集まって課ができており、課が集まって部ができており、部が集まって会社ができています。人が会社から給料を受け取っているのと同じように、人の集まりである課や部も会社から給料を受け取っていると考えることができ、人・課・部が受け取っている給料を共通のインターフェースで扱うことができるイメージです。
Rubyによるデザインパターン のこちらの説明はわかりやすかったです。
GoFは「全体が部分のように振る舞う」という状況を表すデザインパターンを、Compositeパターンと呼んでいます。Compositeパターンは、階層構造やツリー構造のオブジェクトを作りたいとき、そしてそのツリーを利用するコードが1つの単純なオブジェクトを扱っているのか、それともごちゃごちゃした枝全体を扱っているのかを考えさせたくないときに利用できます。
親となるオブジェクトのメソッドを呼び出すことで、子オブジェクトの同じメソッドを再帰的に呼び出すケースでよく使われます。会社の例だと、部の給料を求めるメソッドを呼び出すことで、課・人の給料を求めるメソッドを呼び出し、それを集計して値を返すイメージです。
Composite(コンポジット)とは、「複合的」という意味の単語です。
最近ハマっているゴルフネタのサンプルコードで説明しています →【Rubyによるデザインパターンまとめ4】Compositeパターン - 銀行員からのRailsエンジニア
一言で言うと、オブジェクトの集まりがあった時に、そのオブジェクト1つずつに順番にアクセスする方法を提供するデザインパターンです。
GoFは、以下のように説明しています。
集約オブジェクトがもとにある内部表現を公開せずに、その要素に順にアクセスする方法を提供する
配列で与えられたデザインパターンを1つずつ出力するというシンプルなサンプルコードで説明しています → 【Rubyによるデザインパターンまとめ5】イテレータパターン - 銀行員からのRailsエンジニア
Commandパターン
処理の内容をオブジェクトに閉じ込めて、実行する際はそのオブジェクトのメソッドを呼び出すパターンです。
処理の内容が書いたオブジェクトのことを、命令という意味の「コマンド」と呼んでいます。
これにより複数のコマンドをキューに入れて順に実行するようにしたり、処理の取り消しなどをシンプルに実装できるようになります。
当時していた勉強内容ネタのサンプルコードで説明しています →【Rubyによるデザインパターンまとめ6】コマンドパターン - 銀行員からのRailsエンジニア
Adapterパターン
必要なインターフェースと既存のオブジェクトのインターフェースの違いを吸収するデザインパターンです。
以下の「アダプタ」という言葉の説明そのままの役割です。
アダプタとは、異なる複数の機器に接続する際に用いられる中間装置の総称である。(IT用語辞典バイナリ より)
RailsのActiveRecord の中でアダプターパターンが使われている箇所があったので、その箇所をコードリーディングしながら説明しています → 【Rubyによるデザインパターンまとめ7】アダプターパターン - 銀行員からのRailsエンジニア
Proxyパターン
本来呼び出したいオブジェクトとの間にオブジェクトを挟むことで、様々な処理を差し込むことができる デザインパターンです。
言い換えると、本来呼び出したいオブジェクトを別のオブジェクト経由で呼び出し、別のオブジェクトに様々な処理を追加することができます。
インターフェースは、本来呼び出したいオブジェクトと同じにします。
よく使用されるケースとしては、アクセス制御 や 生成コストのかかるオブジェクトのインスタンス化遅延 などがあり、これらをプロキシ(挟んだオブジェクト)側で実装することで、関心事を分離することができます。(アクセス制御などが本来呼び出したいオブジェクトの関心事ではない場合に、それをプロキシ側に切り出すことができます)
プロキシ(Proxy)は「代理」という意味であり、中継サーバである「プロキシサーバ」と同じイメージです。
商品の購入、商品へのコメントができるサンプルコードで説明しています → 【Rubyによるデザインパターンまとめ8】プロキシパターン - 銀行員からのRailsエンジニア
Decoratorパターン
既存のオブジェクトに、機能を簡単に追加するためのパターンです。
層状に機能を積み重ねることができ、状況ごとに必要な機能のみを持つオブジェクトを作ることができます。
Decorator という単語は「装飾者」という意味で、元のオブジェクトに必要な機能を装飾するイメージです。
様々な方法で通知を行うサンプルコードで説明しています → 【Rubyによるデザインパターンまとめ9】デコレータパターン - 銀行員からのRailsエンジニア
Singletonパターン
ただ1つのインスタンスしか持てないクラスを作り、その1つのインスタンスへのアクセスをグローバルに提供するパターンです。
Singletonという単語は、トランプの一枚札(唯一存在するカード)という意味で、インスタンスが1つしか存在しないことを表しています。
ジムでダンベルの貸し借りを管理するサンプルコードで説明しています → 【Rubyによるデザインパターンまとめ10】シングルトンパターン - 銀行員からのRailsエンジニア
Factory Methodパターン
オブジェクトの生成をファクトリメソッドに任せることで、クラス名を指定せずにオブジェクトを生成することができるデザインパターンです。
コードの責務を分割して、保守しやすいコードになることがメリットです。
ファクトリ(Factory)とは「工場」という意味で、ファクトリメソッド = オブジェクトを生産するメソッドというイメージです。
Hashで与えられるデータを指定した形式で出力するサンプルコードで説明しています →【Rubyによるデザインパターンまとめ11】ファクトリメソッドパターン - 銀行員からのRailsエンジニア
Buildパターン
オブジェクトを作るのに大量のコードを書かなければ行けない場合などに、オブジェクトの生成のためのコードを別のクラス(ビルダクラス)に分離するパターンです。
つまり、オブジェクトの生成手順(コード上ではコンストラクタ)を外出しします。複雑なオブジェクトの生成手順を分離することで、構造がシンプルになり、再利用性も高めることができます。
ユーザーオブジェクトを作るサンプルコードで説明しています → 【Rubyによるデザインパターンまとめ12】ビルダパターン - 銀行員からのRailsエンジニア
クラスで表現した文法規則で構文を解析し、その結果得られた手順に基づいて処理を実行するパターンです。
「Interpreter」は「通訳」という意味です。
文字列をプラスマイナスできるサンプルコードで説明しています → 【Rubyによるデザインパターンまとめ13】インタプリタパターン - 銀行員からのRailsエンジニア
ある特定の問題を解決するために専用の言語を定義するパターンです。
Railsでは、Validations・ActiveRecord・Rake・RSpecなど幅広く使われています。
Railsのコードで実際に使われている箇所を題材に説明しています → 【Rubyによるデザインパターンまとめ14】DSL(ドメイン特化言語) - 銀行員からのRailsエンジニア
必要なコードを全て書いておくのではなく、実行時にプログラムに基づいて作り出します。
「attr_reader」と同じ機能をメタプログラミングで実装しながら説明しています → 【Rubyによるデザインパターンまとめ15】メタプログラミング - 銀行員からのRailsエンジニア
Convention over Configuration
日本語訳すると「設定より規約」で、規約に従うことで不必要な設定のコードを書く必要がなくなります。
頭文字を取って「CoC」と略されます。
例えば、Rails の ActiveRecord では、users というテーブルは、 models ディレクトリにある user.rb というファイルにある User クラスで処理されます。さらに、users テーブルの name というカラムは、user オブジェクトの name フィールドに自動的に割り当てられます。
このように、規約に従うことでコードの記述量を減らすことでき、また誰が見ても分かりやすいコードになります。
Railsのscaffoldに似た機能を実装しながら説明しています → 【Rubyによるデザインパターンまとめ16】Convention over Configuration - 銀行員からのRailsエンジニア
選択肢をいくつか持っていることによって状況に一番合った実装を選べる確率が上がるのかなあ、と思いました。自分の実装の幅を広げてくれるのでデザインパターンを知っているといいですね。学んで正解でした。
なんてことを、「あるコードを何種類かの方法でリファクタリングする」という内容の記事を書きながら思ったので、良ければ合わせて読んでみてください。
ストラテジーパターンを使ったリファクタリング例【Ruby】 - 銀行員からのRailsエンジニア
学んで終わりだと全く意味はないので、これから実務でのプロダクションコードにガンガンデザインパターンを入れていこうと思いますー!(先日も初めて1つ入れました!)
1冊で多くのことを学べたこの本は本当に良書だと思うので、Rubyをやっていてデザインパターンを学んでみたい方はぜひ読んでみてくださいー!(現在は新品を売っていなくて中古が高騰しちゃってるのがちょっとつらいですが..)