見出し画像

【91】【Rails】リファクタリングトレーニング_必須の実装をスーパークラス側で教える

この記事を読むと問題のあるコードや改善の余地があるコードを学び、リファクタリングする能力を養うことができます。

改善の余地があるコード

問題があるコードというわけではありません。
しかし、まだ改善の余地があります。
どの点を改善すべきか説明できますか?
そして、どのようにリファクタリングしますか?

class Bicycle
 attr_reader :size,:chain,:tire_size
 def initialize(args)
   @size = args[:size]
   @chain = args[:chain] || default_chain
   @tire_size = args[:tire_size] || default_tire_size
 end
 def spares
   {
       tire_size: tire_size,
       chain: chain
   }
 end
 def default_chain
   "10-speed"
 end
end
class RoadBike < Bicycle
 attr_reader :tape_color
 def initialize(args)
   @tape_color = args[:tape_color]
   super(args)
 end
 def spares
   super.merge({tape_color: tape_color})
 end
 def default_tire_size
   "23"
 end
end
class MountainBike < Bicycle
 attr_reader :front_shock,:rear_shock
 def initialize(args)
   @front_shock = args[:front_shock]
   @rear_shock = args[:rear_shock]
   super(args)
 end
 def spares
   super.merge({rear_shock: rear_shock})
 end
 def default_tire_size
   "2.1"
 end
end

テスト

require_relative '4_2_1_before_refactoring'


RSpec.describe "Bicycleモデル" do
 bike = RoadBike.new(size: "L",tape_color: "red")

 it '#spares' do
   expect(bike.spares)
       .to eq({:chain=>"10-speed", :tape_color=>"red", :tire_size=>"23"})
 end
 class Foo < Bicycle
   attr_reader :tape_color

   def initialize(args)
     @tape_color = args[:tape_color]
     super(args)
   end
   def spares
     super.merge({tape_color: tape_color})
   end

 end

 it 'default_tire_sizeを定義しないと、NotImplementedErrorが発生する' do
   expect{bike = Foo.new(size: "L",tape_color: "red")}
       .not_to raise_error(NotImplementedError)
 end

end

改善したい点

機能が拡張しサブクラスが増えていったとします。
テストではサブクラスFooを追加しました。
気づきましたでしょうか?
Fooサブクラスは

 def default_tire_size;end

が定義されてないため、インスタンスの作成にエラーが生じます。

     NameError:
      undefined local variable or method `default_tire_size' for #<Foo:0x00007faf84910a88 @tape_color="red", @size="L", @chain="10-speed">

実際、このようなエラーが生じましす。
サブクラスの作成で、default_tire_sizeの下記忘れでエラーが出てしまうのは、スーパークラス側の責任になります。
スーパークラス側で責任があるなら、起こり得るエラーの箇所や必須の実装をスーパークラス側で教えて上げるようにしましょう。
具体的にはNotImplementedErrorを発生させることです。

 it 'default_tire_sizeを定義しないと、NotImplementedErrorが発生する' do
   expect{bike = Foo.new(size: "L",tape_color: "red")}
       .not_to raise_error(NotImplementedError)
 end

リファクタリング前のテストでは、not_toとしているのでNotImplementedErrorは発生しない形でテストが通ります。
リファクタリングして、NotImplementedErrorが通る形にしましょう。

リファクタリング

class Bicycle
 attr_reader :size,:chain,:tire_size
 def initialize(args)
   @size = args[:size]
   @chain = args[:chain] || default_chain
   @tire_size = args[:tire_size] || default_tire_size
 end
 def spares
   {
       tire_size: tire_size,
       chain: chain
   }
 end
 def default_chain
   "10-speed"
 end
 def default_tire_size
   raise NotImplementedError,
         "This #{self.class} cannot respond do to:"
 end
end
class RoadBike < Bicycle
 attr_reader :tape_color
 def initialize(args)
   @tape_color = args[:tape_color]
   super(args)
 end
 def spares
   super.merge({tape_color: tape_color})
 end
 def default_tire_size
   "23"
 end
end
class MountainBike < Bicycle
 attr_reader :front_shock,:rear_shock
 def initialize(args)
   @front_shock = args[:front_shock]
   @rear_shock = args[:rear_shock]
   super(args)
 end
 def spares
   super.merge({rear_shock: rear_shock})
 end
 def default_tire_size
   "2.1"
 end
end

テスト

require_relative '4_2_2_refactoring'


RSpec.describe "Bicycleを継承したテスト用のサブクラス" do
 class Foo < Bicycle
   attr_reader :tape_color

   def initialize(args)
     @tape_color = args[:tape_color]
     super(args)
   end
   def spares
     super.merge({tape_color: tape_color})
   end

 end

 it 'default_tire_sizeを定義しないと、NotImplementedErrorが発生する' do
   expect{bike = Foo.new(size: "L",tape_color: "red")}
       .to raise_error(NotImplementedError)
 end
end

ポイント

 リファクタリングしたことによって、default_tire_sizeを記入漏れした場合、NotImplementedErrorが発生するようになりました。
リファクタリングしなかった場合、サブクラスの作成者がエラーの解明など時間を割く可能性があります。
この記事の例のように、必須の実装をスーパークラス側で明示する視点は、全体的なコスト削減につながります。

注意点!

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

 関連する記事

参考文献

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

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

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

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










































































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