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

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

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

当ブログではアフィリエイト広告を利用しています

コンストラクタからファクトリメソッドへ【リファクタリング Rubyエディションまとめ6】

こんにちは。

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

実務でも活かせそうなパターンが多くあったので、いいなと思ったパターンを1つずつブログにまとめています。

今回はその第6弾で「コンストラクタからファクトリメソッドへ」についてまとめました。
オリジナルの Ruby のサンプルコードを使って説明しています。

第5弾の「タイプコードからポリモーフィズムへ」のまとめはこちらです。
タイプコードからポリモーフィズムへ【リファクタリング Rubyエディションまとめ5】 - 銀行員からのRailsエンジニア

f:id:ysk_pro:20200520070559p:plain

サンプルコード

リファクタリング

文章での説明よりもコードを見た方が分かりやすいので、早速サンプルコードを見ていきましょう。
まずはリファクタリング前のコードです。

class Car
  def initialize(price)
    @price = price
  end

  def guarantee?
    false
  end
end

class ForeinCar < Car
  def guarantee?
    false
  end
end

class HighGradeCar < Car
  def guarantee?
    true
  end
end

Car(車)クラス、Carクラスを継承した ForeinCar(外車)クラス・HighGradeCar(高級車)クラスがあります。

imported = false
price = 200

car1 = if imported
         ForeinCar.new(price)
       elsif price > 100
         HighGradeCar.new(price)
       else
         Car.new(price)
       end
puts car1.guarantee? # -> true

作成するオブジェクトを決めるために条件分岐を使っています。また、複数箇所でオブジェクトを作成しており、同じ条件分岐が使われているとします。

このコードの問題点としては以下の点が挙げられます。

  • 条件分岐のコードが重複していること
  • 呼び出しているクラスと、オブジェクトが作られるクラス(ForeinCarなど)の結合度が強くなり、保守性が低くなっていること(例えば、ForeinCarクラスのクラス名や呼び出し方が変わると、呼び出し元のクラスも変更しなくてはいけなくなってしまいます)

リファクタリング

「コンストラクタからファクトリメソッドへ」を使ってリファクタリングしていきます。

リファクタリングはシンプルで、先ほどの Carクラスに、オブジェクト作成のためのクラスメソッドを1つ追加するのみです。

class Car
  def self.create(imported, price) # ファクトリメソッド
    if imported
      ForeinCar.new(price)
    elsif price > 100
      HighGradeCar.new(price)
    else
      new(price)
    end
  end
end

create がファクトリメソッドです。

次のように実行すると結果はリファクタリング前と同じになります。

car2 = Car.create(imported, price)
puts car2.guarantee?

オブジェクトの作成は全てファクトリメソッドで行うようにすることで、コードの重複はなくなり、クラス間の結合度も弱くすることができました。

「コンストラクタからファクトリメソッドへ」という名前は、呼び出し元からコンストラクタを呼び出していた(newしていた)ものをファクトリメソッドを呼び出すように変える、という意味のようです。

おわりに

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

リファクタリング Rubyエディションでは、この記事とは別のサンプルコードで丁寧に説明されていて分かりやすかったので、ご興味ある方は是非ご覧ください。

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

(追記)
第7弾として「サンドイッチメソッドの抽出」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com

タイプコードからポリモーフィズムへ【リファクタリング Rubyエディションまとめ5】

こんにちは。

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

実務でも活かせそうなパターンが多くあったので、特にいいなーと思ったパターンを1つずつブログにまとめています。

今回はその第5弾で「タイプコードからポリモーフィズムへ」についてまとめました。
オリジナルの Ruby のサンプルコードを使って説明しています。

第4弾の「nullオブジェクトの導入」のまとめはこちらです。
nullオブジェクトの導入【リファクタリング Rubyエディションまとめ4】 - 銀行員からのRailsエンジニア

f:id:ysk_pro:20200513080734p:plain

サンプルコード

リファクタリング

文章での説明よりもコードを見た方が分かりやすいので、早速サンプルコードを見ていきましょう。
まずはリファクタリング前のコードです。

class Fan
  attr_reader :type_code, :brand_level

  def initialize(type_code, brand_level)
    @type_code = type_code
    @brand_level = brand_level
  end

  def price
    case type_code
    when :ac_motor
      brand_level * 3_000
    when :dc_motor
      brand_level * 5_000
    when :no_wing
      brand_level * 7_000
    end
  end

  # priceメソッド以外にもtypeによる条件分岐が多くなっている
end

シンプルな Fan(扇風機)クラスです。
モーターのタイプを表す type_code(タイプコード)とブランドのレベルを表す brand_level(ブランドレベル)を渡してインスタンスを作成すると、価格を返す price メソッドが使用できます。

リファクタリングの名前にも入っている「タイプコード」というのは、この例の type_code のように、インスタンスの種類を表すために注入している要素のことを指します。

次のように実行します。

nomal_fan = Fan.new(:ac_motor, 1)
puts nomal_fan.price

dyson_fan = Fan.new(:no_wing, 5)
puts dyson_fan.price

balmuda_fan = Fan.new(:dc_motor, 10)
puts balmuda_fan.price

ブランドレベルは僕の主観です(笑)

結果は下記の通りとなります。

3000
35000
50000

バルミューダの扇風機は高いですね〜。

このコードの問題点は、case 文による条件分岐を行なっているところです。

今は price メソッドにしか case 文を使っていないので悪くないように見えるかもしれませんが、他のメソッドを追加する際に同じように case 文による条件分岐を作ると、case 文のところでコードの重複が発生してしまいます。

さらに、case 文が複数存在する状態で、新たなタイプコードを追加しようとした場合は、全ての case 文に修正を入れなければならなくなります

これらが重なってくるとかなり辛い状況になってしまいます。

それでは、このコードをポリモーフィズムを使ってリファクタリングしてみましょう。

リファクタリング

「タイプコードからポリモーフィズムへ」を使ってリファクタリングすると次のようになります。

class AcMotorFan
  attr_reader :brand_level

  def initialize(brand_level)
    @brand_level = brand_level
  end

  def price
    brand_level * 3_000
  end
end

class DcMotorFan
  attr_reader :brand_level

  def initialize(brand_level)
    @brand_level = brand_level
  end

  def price
    brand_level * 5_000
  end
end

class NoWindFan
  attr_reader :brand_level

  def initialize(brand_level)
    @brand_level = brand_level
  end

  def price
    brand_level * 5_000
  end
end

タイプコードそれぞれをクラスにしそれぞれのクラスで price メソッドを定義するようにしました。

次のように実行すると結果はリファクタリング前と同じになります。

nomal_fan = AcMotorFan.new(1)
puts nomal_fan.price

dyson_fan = NoWindFan.new(5)
puts dyson_fan.price

balmuda_fan = DcMotorFan.new(10)
puts balmuda_fan.price

case 文による条件分岐がなくなりましたね。

新たなメソッドを追加する際は、それぞれのクラスに新しいメソッドを書いていくだけでいいのでシンプルです。
また、タイプコードの追加もクラスを新たに作るだけで良くなりました。

「タイプコードからポリモーフィズム」という名前はそのままで分かりやすいですね。

おわりに

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

case文を使っている箇所を見つけたら「タイプコードからポリモーフィズム」が適用できないかをまず考えてみると良さそうですね。

リファクタリング Rubyエディションでは、様々なタイプのマウンテンバイクを題材に解説されていて分かりやすかったので、ご興味ある方は是非ご覧ください。

また、ポリモーフィズムについて本書に分かりやすい説明があったので引用します。

ポリモーフィズムの本質は、オブジェクトにどのようなタイプ(型)なのかを尋ね、その答に基づいてなんらかのメソッドを呼び出すようなことをせずに、単純にメソッドを呼び出すことである。呼び出されたオブジェクトは、自分の型に基づいて適切な処理を行う。
リファクタリング Rubyエディションから抜粋)

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

(追記)
第6弾として「コンストラクタからファクトリメソッドへ」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com

余談

最近暑くなり、扇風機選びに悩んでいたので扇風機を題材にしたサンプルコードにしてみました。
かなり悩んだ末に、個人的なブランドレベルが10の バルミューダの扇風機を購入しました!風が心地よく、コードレスが便利で大満足でした。

リモートワークでのデスク環境の紹介

リモートワークが始まって3ヶ月目に入りました。

自宅のデスク環境をコツコツ改善してきた結果、ある程度完成したのでブログにまとめてみます。

色々とこだわった結果、会社の環境より快適になってしまいました(笑)

全体像

f:id:ysk_pro:20200509094913j:plain

ポイントはカービィの卓上扇風機と子パンダのぬいぐるみです!かわいい!

それ以外にもこだわった部分があるので1つずつ紹介していきます。

デスク

電動の昇降デスクを使っています。

元々「立って作業するとかつらそう」派だったのですが、職場の昇降デスクで立って作業をしてみたら、思いのほか集中できて良かったので家でも買っちゃいました。

午後は(眠いので)基本立つようにしています。

足つぼを刺激するスリッパと合わせるのがおすすめです。

メガ押しふみっぱ ピンク(グイグイインソール)

メガ押しふみっぱ ピンク(グイグイインソール)

  • 発売日: 2016/09/05
  • メディア: ヘルスケア&ケア用品
 

チェア

定番のアーロンチェアを使っています。

1年半前に新宿の大塚家具にチェアの下見のつもりで行ったのですが、気がついたときには買っちゃってました。それぐらい座り心地が良かったです。

もともとよく腰を痛めていた僕が、アーロンチェアを使い始めてから1度も腰が痛くなっていないのは割と快挙です。20万円くらいしましたが買って良かったです。

リモートワークになる前は、僕と同じくエンジニアである妻と1台を順番に使っていましたが、2人ともリモートワークとなったのでもう1台買い足しました。

アーロンチェアは3サイズあり、僕は身長172cmで中間のBサイズ、妻は身長158cmで一番小さいAサイズがちょうど良いです。

モニター

右から 24インチ、21インチ、13インチの3画面構成です。

2画面でも十分だと思っていましたが、3画面にしてみるともう2画面には戻れないくらい快適です。

真ん中のモニターはモニターアームで固定しており、モニターの下のスペースが空いてスッキリしています。

HP 9YF44AA#ABJ HP 21.5インチワイドIPSモニター P224

HP 9YF44AA#ABJ HP 21.5インチワイドIPSモニター P224

  • メディア: エレクトロニクス
 
Herman Miller Flo Single Monitor

Herman Miller Flo Single Monitor

  • メディア: Personal Computers
 

キーボード

僕が一番気に入っているポイントがこのキーボードです!

BAROCCOの分離型キーボードを使っています。

軸は、このキーボードの前のモデルで茶軸を使っていて音が気になったので、静音赤軸にしました。

両手を机に置いた自然な姿勢でタイピングできるのがとっても楽です。あまり肩こりに悩んだことがないのですが、このキーボードのおかげなんじゃないかなーと思っています。

しかもこのキーボード、光らせることもできます...!光らせ方もパターンを変えることができて、僕は押したキーがカラフルに光る設定にしています。

分離キーボードの間にトラックパッドとノートを置くと、いい感じに収まって使いやすいので便利です。

Apple Magic Trackpad 2 - スペースグレイ

Apple Magic Trackpad 2 - スペースグレイ

  • 発売日: 2018/05/16
  • メディア: Personal Computers
 

ケーブルの収納

デスクの裏のケーブル収納部分

机の裏にケーブルトレーを取り付けて、その上に延長コードを置いています。

また、机の裏に複数箇所にケーブルクリップをつけて、ケーブルをそこを通してなるべく机の裏を這わせるようにしています。

こうすることで、正面からはほとんどケーブルが見えなくなります。

元々延長コードを下に置いていたのですが、ケーブルトレーとケーブルクリップを使うことで見た目もよくなり掃除もしやすくなりました。

macに接続しているtype-cケーブルは、macを持ち運ぶ時のために机の横にマグネットでつけられるようにしています。

デスクの横にケーブルをマグネットでつけている画像

プラス Garage 配線ケーブルトレー 幅40cm レッド 434665

プラス Garage 配線ケーブルトレー 幅40cm レッド 434665

  • 発売日: 2020/02/10
  • メディア: オフィス用品
 

おまけ(ホームジム環境)

ジムを解約して、ホームジムを作りました!

ホームジムの写真

家にあることで必ず目に入り、毎日欠かさずやるようになって筋肉量は着実にアップしている気がします。

このダンベルは優れもので、ダイヤルを回すだけで重さが変えれて最高です。

おわりに

デスク環境整えるの楽しいですよね〜!

ちなみに最初に紹介したカービィの卓上扇風機はUFOキャッチャーでめっちゃ頑張って取った非売品です!と思ってたらAmazonに普通にありました... 

これからも継続的にデスク環境の改善をしていくつもりです。(今気になっているのは、4Kディスプレイ と zoom会議用のマイク と Bluetoothスピーカーです)

オススメのグッズなどあれば是非教えてください!

(追記)

デスクをリニューアルして記事を書いたので、ぜひ合わせてご覧ください。

ysk-pro.hatenablog.com

nullオブジェクトの導入【リファクタリング Rubyエディションまとめ4】

こんにちは。

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

本書の中でいいなと思ったパターンを1つずつブログにまとめています。

今回はその第4弾で「nullオブジェクトの導入」についてまとめました。
オリジナルの Ruby のサンプルコードを使って説明しています。

第3弾の「継承から委譲へ」のまとめはこちらです。
継承から委譲へ【リファクタリング Rubyエディションまとめ3】 - 銀行員からのRailsエンジニア

f:id:ysk_pro:20200508074431p:plain

「nullオブジェクトの導入」とは

サンプルコードを見ていく前に簡単に説明します。

概要

まず「nullオブジェクト」というのは、オブジェクトが nil だった場合の処理を集めたクラスから作られたオブジェクトを指します。

オブジェクトが nil だった場合に、代わりに null オブジェクトの同名のメソッドを呼び出すことで、メソッド呼び出しするだけで処理を分けることができ、オブジェクトの nil チェックが不要になります。

※ null と nil は同じで、一般的な名称が null、Ruby内での呼び方が nil というだけです。

どんな時に使えるか

オブジェクトが nil の場合は別の値を入れる処理を繰り返している場合に有効です。

メリット

nilチェックが不要になります。

ポリモーフィズムを利用することで、よりオブジェクト指向らしいコードになります。

ポリモーフィズムの本質は、オブジェクトにどのようなタイプ(型)なのかを尋ね、その答に基づいてなんらかのメソッドを呼び出すようなことをせずに、単純にメソッドを呼び出すことである。呼び出されたオブジェクトは、自分の型に基づいて適切な処理を行う。
リファクタリング Rubyエディションから抜粋)

サンプルコード

リファクタリング

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

class Gym
  attr_reader :name, :trainer

  def initialize(name, trainer=nil)
    @name = name
    @trainer = trainer
  end
end

class Trainer
  attr_reader :name, :muscle_quantity

  def initialize(name, muscle_quantity)
    @name = name
    @muscle_quantity = muscle_quantity
  end
end

muscle_trainer = Trainer.new('マッチョトレーナー', 100)
gold_gym = Gym.new('ゴールドジム', muscle_trainer)
joyfit = Gym.new('ジョイフィット')

# それぞれのジムのトレーナー情報を出したい
gyms = [gold_gym, joyfit]
gyms.each do |gym|
  puts "#{gym.name}: #{gym.trainer&.name || 'トレーナー不在'} / 筋肉量: #{gym.trainer&.muscle_quantity || 0}"
  # nilチェックが多くなってつらい
end

見ての通り、筋肉量100のマッチョなトレーナーがいるゴールドジムとトレーナーが不在のJOYFITがあって、それぞれのジムのトレーナー情報を出力するプログラムです。

ゴールドジムのトレーナーは本当にマッチョでした。

上記のプログラムを実行すると、結果は下記の通りとなります。

ゴールドジム: マッチョトレーナー / 筋肉量: 100
ジョイフィット: トレーナー不在 / 筋肉量: 0

トレーナー情報を出力する部分で、nil チェックが多くてつらくなっていますね。

リファクタリング

「nullオブジェクトの導入」を使ってリファクタリングすると次のようになります。

class Gym
  attr_reader :name, :trainer

  def initialize(name, trainer=nil)
    @name = name
    @trainer = trainer || NoTrainer.new # trainerがnilの時にnullオブジェクト生成 
  end
end

class Trainer
  attr_reader :name, :muscle_quantity

  def initialize(name, muscle_quantity)
    @name = name
    @muscle_quantity = muscle_quantity
  end
end

class NoTrainer
  def name
    'トレーナー不在'
  end

  def muscle_quantity
    0
  end
end

muscle_trainer = Trainer.new('マッチョトレーナー', 100)
gold_gym = Gym.new('ゴールドジム', muscle_trainer)
joyfit = Gym.new('ジョイフィット')

# それぞれのジムのトレーナー情報を出したい
gyms = [gold_gym, joyfit]
gyms.each do |gym|
  puts "#{gym.name}: #{gym.trainer.name} / 筋肉量: #{gym.trainer.muscle_quantity}"
end

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

Gymクラスのコンストラクタで、trainer が nil の場合に @trainer に null オブジェクト(NotTrainer.new)を入れることによって、nil チェックではなくシンプルなメソッド呼び出しで nil の場合の条件分岐が実現できていますね。

おわりに

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

Rubyのコードで nil チェックを行なっている箇所は多いと思うので、「nullオブジェクトの導入」というリファクタリングを覚えておくとリファクタリングの引き出しが増えますね。

リファクタリング Rubyエディションでは、もう少しリアルなサンプルケースを使って説明していてとても分かりやすかったので、ご興味ある方は是非ご覧ください。

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

(追記)
第5弾として「タイプコードからポリモーフィズムへ」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com

継承から委譲へ【リファクタリング 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

モジュールの抽出【リファクタリング Rubyエディションまとめ2】

こんにちは!

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

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

今回はその第2弾で「モジュールの抽出」についてまとめます。

第1弾の「委譲の隠蔽」のまとめはこちらです。
委譲の隠蔽【リファクタリング Rubyエディションまとめ1】 - 銀行員からのRailsエンジニア

f:id:ysk_pro:20200502084917p:plain

リファクタリング前のコード

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

ItemクラスとUserクラスで、インスタンスの保存の前にデフォルトの名前を設定しています。

class Item < ApplicationRecord
  #・・・
  before_save :set_default_name

  def set_default_name
    self.name = default_name
  end
  #・・・
end

class User < ApplicationRecord
  #・・・
  before_save :set_default_name

  def set_default_name
    self.name = default_name
  end
  #・・・
end

このコードの問題点は、言うまでもなくコードが重複している点です。

ただ、set_default_name というメソッドは、Itemクラス・Userクラスそれぞれのインスタンスを更新しているため、別のクラスに共通のメソッドを作ってそのクラスのインスタンスに処理を委譲するというやり方ではうまくいきません。

今回のような、通化したいコードがそれぞれのクラスでしか意味を持たないケースで「モジュールの抽出」は有効です。

モジュールの抽出を使ったリファクタリング

モジュールの抽出を使ったリファクタリング後のコードはこのようになります。
Itemクラス、Userクラスでは新しく作った SetDefaultNameモジュールをincludeしているだけでとてもシンプルですよね。

module SetDefaultName
  def self.included(klass) # ①
    klass.class_eval do # ②
      before_save :set_default_name
    end
  end

  def set_default_name
    self.name = default_name
  end
end

class Item < ApplicationRecord
  #・・・
  include SetDefaultName
  #・・・
end

class User < ApplicationRecord
  #・・・
  include SetDefaultName
  #・・・
end

includedメソッド は、モジュール(今回だとSetDefaultName)が include された時に、include をしたクラス(今回だと Item, User)を引数に実行されるメソッドです。

class_evalメソッド は、ブロックに記載したメソッドをレシーバ(このコードだと klass)内に定義してくれるメソッドです。

つまり self.included メソッドでは、SetDefaultName モジュールが include されるタイミングで、include したクラスに before_save :set_default_name を定義しているのです。

「モジュールの抽出」という名前は、単に共通の処理をモジュールにして抽出していることを表しており、シンプルで覚えやすいですね。

おわりに

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

includedメソッドを使ってモジュールが include されたタイミングでメソッドを定義する方法は、知っておかないとこの方法を使っているコードを読むのが難しいので、覚えておいて損はないと思います。

このブログのサンプルコードは、全く同じではないもののリファクタリング Rubyエディションのサンプルコードを参考にさせていただきました。本書の中では、さらに丁寧に解説されていてとても分かりやすいので、ご興味ある方は是非ご覧ください。

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

(追記)
第3弾として「継承から委譲へ」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com

委譲の隠蔽【リファクタリング Rubyエディションまとめ1】

こんにちは。ゆうすけです。

先日ついに Rubyによるデザインパターン を読み終えた(!)ので、次は リファクタリング Rubyエディション を読み始めました。

リファクタリング Rubyエディションは、復刻版が最近発売された名著でリファクタリングの様々なパターンをRubyのサンプルコードと共に紹介していく形式です。

パターンの数は膨大なので、いいなと思ったパターンを1つずつブログにまとめていこうと思います。

今回はその第一弾で「委譲の隠蔽」についてまとめます。

問題のあるコード

例えば、こんなコードがあったとします。

class Article
  attr_accessor :blog
end

class Blog
  attr_reader :writer

  def initialize(writer)
    @writer = writer
  end
end

前提として、ブログ(Blog)の中にたくさんの記事(Article)があり、同じブログに属している記事の作者は同じとします。

ある記事の作者(writer)が知りたい場合は、下記の実行コードの「article1.blog.writer」のように呼び出す必要があります。

article1 = Article.new
blog1 = Blog.new('ブロガーくん')
article1.blog = blog1

puts article1.blog.writer # -> ブロガーくん

このコードだと、呼び出す側が「作者の管理はブログが行なっている」という知識を持っている必要があります。(言い換えると、article1.blog.writer という呼び出し方を知っている必要があるということ)

知識が必要だということは、呼び出す側からブログクラスへの依存が生じており、ブログクラスに変更があった際には呼び出す側にも影響が生じてしまいます。辛いですね。。

委譲の隠蔽を使ったリファクタリング

これを、記事クラスに簡単な委譲メソッド(writer メソッド)を作って改善します。

class Article
  attr_accessor :blog

  def writer
    blog.writer
  end
end

class Blog
  attr_reader :writer

  def initialize(writer)
    @writer = writer
  end
end

こうすることによって、下記実行コードのように「article1.writer」と呼び出せるようになり(ブログクラスへの処理の委譲を記事クラスが隠蔽している!)、「作者の管理はブログが行なっている」という知識を呼び出す側が持つ必要がなくなります。(言い換えると、article1.writer と呼び出せばいいことだけを知っていればよいということ)

先ほどの問題点をクリアしていますね!

article1 = Article.new
blog1 = Blog.new('ブロガーくん')
article1.blog = blog1

puts article1.writer # -> ブロガーくん

図で表すとこのようなイメージです。
矢印の向いた先に依存していることを示しています。

依存関係を表した図

図から分かるように、呼び出し側からブログクラスへの依存がなくなり、ブログクラスの変更の影響を呼び出し側が受けなくなりました。これは嬉しいですね。

「委譲の隠蔽」とは、元々は記事クラスが作者を取得するための処理をブログクラスに委譲していたところを、記事クラスの writer メソッドに隠蔽した(呼び出す側から分からなくした)という意味です。

Forwardableモジュールを使うと

やっていることは同じなのですが、Ruby標準ライブラリに含まれている Forwardable モジュールを使うことで以下のように書くこともできます。

require 'forwardable'

class Article
  extend Forwardable

  def_delegator :blog, :writer

  attr_accessor :blog
end

class Blog
  attr_reader :writer

  def initialize(writer)
    @writer = writer
  end
end

def_delegator は、2つ目の引数のメソッド(この場合は writer)を1つ目の引数のオブジェクト(この場合は blog)へ委譲します。

よりスッキリしていい感じですね。

おわりに

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

サンプルコードと依存関係の図は リファクタリング Rubyエディションを参考にさせていただきました。本書の中では、Person クラスと Department(部門)クラスの関係を例に説明しています。

「委譲の隠蔽」使えそうですね。

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

(追記)
第2弾として「モジュールの抽出」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com