銀行員からのRailsエンジニア

銀行員からのRailsエンジニア

銀行員から転身したサービス作りが大好きなRailsエンジニアのブログです。個人で開発したサービスをいくつか運営しており、今も新しいサービスを開発しています。転職して日々感じていること、個人開発サービス運営のことなどを等身大で書いていきます。

継承から委譲へ【リファクタリング Rubyエディションまとめ3】

こんにちは。

最近 リファクタリング Rubyエディション を読みました。本書は、復刻版が最近発売された名著でリファクタリングの様々なパターンRubyのサンプルコードを使って解説しています。

紹介されているリファクタリングのパターン数は膨大で全てをまとめるのは難しいので、いいなと思ったパターンを1つずつブログにまとめています。

今回はその第3弾で「継承から委譲へ」についてまとめます。
オリジナルの Ruby のサンプルコードを使いながら説明しています。

第2弾の「モジュールの抽出」のまとめはこちらです。
モジュールの抽出【リファクタリング Rubyエディションまとめ2】 - 銀行員からのRailsエンジニア

f:id:ysk_pro:20200505075342p:plain

「継承から委譲へ」とは

概要

継承を使っていた箇所を、継承の関係を解消して委譲を使うようにするリファクタリングです。

どんな時に使えるか

継承関係にあるものの、サブクラスがスーパークラスのインターフェースの一部しか使っていなかったり、スーパークラスのメソッドの多くがサブクラスにうまくかみ合っていない時に有効となります。

どのようにリファクタリングするか

サブクラスにスーパークラスのためのフィールドを作り、スーパークラスに処理を委譲するようにして、継承構造を解消します。

メリット

不要なメソッドを継承することがなくなり、予期せぬ副作用を防ぎつつコードの共通化を維持することができます。

サンプルコード

リファクタリング

サンプルコードを見ていきましょう。
こちらはリファクタリング前のコードです。

class Tasks < Array
  def finish(task)
    puts 'Well done!'
    delete(task)
  end

  def list
    each { |task| puts task }
  end
end

Array クラスを継承した Tasks クラスを作ってみました。

tasks = Tasks.new
tasks.push('clean room')
tasks.push('study ruby')

tasks.list
tasks.finish('clean room')
tasks.list

上記のように実行すると、下記の結果になります。

clean room
study ruby
Well done!
study ruby

Array クラスを継承してしていますが、Array クラスの一部のメソッド(push, delete, each)しか使っていませんね。

Tasks クラスに必要なメソッドのみを使えるよう、委譲を使ってリファクタリングしてみましょう。

リファクタリング

「継承から委譲へ」を使ってリファクタリングすると次のようになります。

require 'forwardable'

class Tasks
  extend Forwardable

  def_delegators :@tasks, :each, :delete, :push

  def initialize
    @tasks = [] # Array.new でも同じ
  end

  def finish(task)
    puts 'Well done!'
    delete(task)
  end

  def list
    each { |task| puts task }
  end
end

実行方法、結果はリファクタリング前と同じになります。

Tasks クラスは Array クラスを継承しなくてよくなり、Array クラスで初期化したインスタンス変数である @tasks に処理を委譲することで、Array クラスのメソッドを利用できています。

これで不要なメソッドの継承は防ぎつつ、利用したい Array クラスのメソッドを使えるようになりました

委譲の部分は、Forwardable モジュールの def_delegators メソッドを使うことで実現しています。

おわりに

ここまでお読みいただきありがとうございます。

継承関係がいいのか、委譲を使った方がいいのかは難しく、コードの増加とともに変更したくなるケースもあると思うので、この方法は覚えておくと役立ちそうですね。

リファクタリング Rubyエディションでは、Hashクラスを継承しているサンプルコードで丁寧に解説されていてとても分かりやすかったので、ご興味ある方は是非ご覧ください。

次回はまた違うパターンをまとめていきます!

(追記)
第4弾として「nullオブジェクトの導入」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com