見出し画像

【89】【Rails】リファクタリングトレーニング_ダック・タイピングは凄い!!

この記事を読むと問題のあるコードや改善の余地があるコードを学び、ダック・タイピングというテクニックを使ってリファクタリングする能力を養うことができます。

問題のあるコード

下記のコードを見て、危険なコードだ!とすぐに分かりましたか?
また、どのような点が危険かをすぐに説明することが出来ますか?
そして、どのようにリファクタリングしますか?

class Trip
 attr_reader :bicycles,:customers,:vehicle

 def initialize(args)
   @bicycles = args[:bicycles]
   @customers = args[:customers]
   @vehicle = args[:vehicle]
 end

 def prepare(prepares)
   prepares.each do |prepare|
     prepare.prepare_bicycles(bicycles) if prepare == Mechanic
     prepare.buy_foods(customers) if prepare == TripCoordinator
     prepare.gas_up(vehicle) if prepare == Driver
     prepare.fill_water_tank(vehicle) if prepare == Driver
   end
 end


end

class Mechanic
 def self.prepare_bicycles(bicycles)
   puts "#{bicycles}の準備が出来ました"
 end
end
class TripCoordinator
 def self.buy_foods(customers)
   puts "#{customers}様の食事を用意しました"
 end
end
class Driver
 def self.gas_up(vehicle)
   puts "#{vehicle}の補給をしました。"
 end
 def self.fill_water_tank(vehicle)
   puts "#{vehicle}内の飲料水を準備しまし

テスト

require_relative '3_2_1_before_refactoring'

RSpec.describe  do
 prepares = [Mechanic,TripCoordinator,Driver]
 args = {
     :bicycles => "ママチャリ",
     :customers => "稲葉浩志",
     :vehicle => "Crazy Rendezvous"
 }
 text = <<TEXT
ママチャリの準備が出来ました
稲葉浩志様の食事を用意しました
Crazy Rendezvousの補給をしました。
Crazy Rendezvous内の飲料水を準備しました
TEXT
 it '#prapare' do
   trip = Trip.new(args)
   expect {trip.prepare(prepares)}.to output(text).to_stdout
 end
end

改善したい点

 def prepare(prepares)
   prepares.each do |prepare|
     prepare.prepare_bicycles(bicycles) if prepare == Mechanic
     prepare.buy_foods(customers) if prepare == TripCoordinator
     prepare.gas_up(vehicle) if prepare == Driver
     prepare.fill_water_tank(vehicle) if prepare == Driver
   end
 end

問題山積みのコードですね。
他のクラスに依存しすぎています。
引数で他のクラスを受け取り、該当するクラスがあれば、該当したクラス内のメソッドを実行する形です。
依存しているクラス名の変更、メソッドの変更、などでバグが十分に起こりえます。
また、機能が拡張がすれば、このメソッドも変更しなければいけません。
そして、拡張した分だけコードが長くなっていきます。

機能が数十、数百となった場合のコードはどうなるでしょうか?
何十、何百と連なるコードとなっていしまいます。
この問題山積みのコードをどのように改善しますか?

リファクタリング

class Trip
 attr_reader :bicycles,:customers,:vehicle

 def initialize(args)
   @bicycles = args[:bicycles]
   @customers = args[:customers]
   @vehicle = args[:vehicle]
 end

 def prepare(prepares)
   prepares.each do |prepare|
     prepare.prepare_trip(self)
   end
 end


end

class Mechanic
 def self.prepare_trip(trip)
   self.prepare_bicycles(trip.bicycles)
 end
 def self.prepare_bicycles(bicycles)
   puts "#{bicycles}の準備が出来ました"
 end
end
class TripCoordinator
 def self.prepare_trip(trip)
   self.buy_foods(trip.customers)
 end

 def self.buy_foods(customers)
   puts "#{customers}様の食事を用意しました"
 end
end
class Driver
 def self.prepare_trip(trip)
   self.gas_up(trip.vehicle)
   self.fill_water_tank(trip.vehicle)
 end

 def self.gas_up(vehicle)
   puts "#{vehicle}の補給をしました。"
 end
 def self.fill_water_tank(vehicle)
   puts "#{vehicle}内の飲料水を準備しました"
 end
end

テスト

require_relative '3_2_2_refactoring'

RSpec.describe  do
 prepares = [Mechanic,TripCoordinator,Driver]
 args = {
     :bicycles => "ママチャリ",
     :customers => "稲葉浩志",
     :vehicle => "Crazy Rendezvous"
 }
 text = <<TEXT
ママチャリの準備が出来ました
稲葉浩志様の食事を用意しました
Crazy Rendezvousの補給をしました。
Crazy Rendezvous内の飲料水を準備しました
TEXT
 it '#prapare' do
   trip = Trip.new(args)
   expect {trip.prepare(prepares)}.to output(text).to_stdout
 end
end

ポイント

新たに、prepare_tripメソッド作り、依存関係のある各クラスに置きました。このメソッドは依存関係のあるクラス間で共通したパブリックインターフェースとなっています。
劇的に改善しましたね。まずコードがかなりスッキリしました。

 def prepare(prepares)
   prepares.each do |prepare|
     prepare.prepare_trip(self)
   end
 end

依存関係もリファクタリング前より緩やかになり、疎結合になりました
この例のようにクラスをまたぐパブリックインターフェースを用意して、依存関係のあるコードを改善しました。
このようなテクニックをダック・タイピングと言います。

ダック・タイピングは定義のみ説明だと理解しづらいですが、実際にダック・タイピングを使ってリファクタリングしてみると、このテクニックの凄さを実感できると思います。

注意点!

リファクタリング後のコードが完全な形になっていっているわけではありません。前よりかは良くなったと捉えてください。 

関連する記事

参考文献

オブジェクト指向設計実践ガイド
~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方

最後に
私がブログを書く目的は、素晴らしい本や、素晴らしい方々の技術記事を知って頂きたいからです。ぜひ、上記の参考文献を見て下さい。(noteなので広告とかは一切ありません。)

現在、株式会社grabssに行くために最後の悪あがきをしています!!
現在の進行状況
この記事は89件目の投稿。目標達成!!!!
目標再設定
20日までに100件目指す!!(89件目)
20日超えたが、寝るまでは20日営業日!!(まだ寝てない、ちょっと寝##峠を超え目が覚めた!)

よろしければ、スキボタン及びサポートお願いします。勉強の励みになります。



この記事が気に入ったらサポートをしてみませんか?