【93】【Rails】リファクタリングトレーニング _継承よりコンポジションを使え
この記事を読むと問題のあるコードや改善の余地があるコードを学び、リファクタリングする能力を養うことができます。
見覚えのあるコード
私の記事マニアなら見覚えのあるコードですね(笑)
この記事と同じコードです。
今回は下記のコードを継承以外の方法でリファクタリングしてみましょう。
class Bicycle
attr_reader :style,:size,:tape_color,:front_shock,:rear_shock
def initialize(args)
@style = args[:style]
@size = args[:size]
@tape_color = args[:tape_color]
@front_shock = args[:front_shock]
@rear_shock = args[:rear_shock]
end
def spares
if style == :road
{
chain: "10-speed",
tire_size: "23",
tape_color: tape_color
}
else
{
chain: "10-speed",
tire_size: "2.1",
rear_shock: rear_shock
}
end
end
end
テスト
require_relative '5_1_1_before_refactoring'
RSpec.describe "Bicycleモデル" do
bike = Bicycle.new(
style: :mountain,
size: "s",
front_shock: "Manitou",
rear_shock: "Fox"
)
it '#spares' do
expect(bike.spares).to eq({:chain=>"10-speed", :rear_shock=>"Fox", :tire_size=>"2.1"})
end
end
問題点
問題山積みのコードですが、重複する内容なので割愛します。
問題点に関しては下記の記事を読んで下さい。
リファクタリング
class Bicycle
attr_reader :size, :parts
def initialize(args = {})
@size = args[:size]
@parts = args[:parts]
end
def spares
parts.spares
end
end
class Parts
attr_reader :parts
def initialize(parts)
@parts = parts
end
def spares
parts.select{|part| part.needs_spare}
end
end
class Part
attr_reader :name, :description, :needs_spare
def initialize(args)
@name = args[:name]
@description = args[:description]
@needs_spare = args.fetch(:needs_spare,true)
end
end
テスト
require_relative '5_1_2_refactoring'
RSpec.describe "Bicycleモデル" do
chain = Part.new(name: "chain",description: "10-speed")
mount_tire =
Part.new(name: "tire_size",description: "2.1")
rear_shock =
Part.new(name: "rear_shock",description: "Foc")
front_shock =
Part.new(name: "front_shock",
description: "Manitou",
needs_spare: false)
mountain_bike_parts =
Parts.new([chain,mount_tire,rear_shock,front_shock])
bike =
Bicycle.new(
size: "L",
parts: mountain_bike_parts
)
spares = bike.spares.map do |part|
{"#{part.name}".to_sym =>"#{part.description}"}
end
it '#spares' do
expect(spares).to eq [{:chain=>"10-speed"}, {:tire_size=>"2.1"}, {:rear_shock=>"Foc"}]
end
end
ポイント
今回は継承を使わずに、コンポジションという方法にしました。
では、継承かコンポジション、どちらを選択すればいいのでしょうか?
コンポジションか継承かどちらがいい?
それぞれの特徴を整理します。
継承
オブジェクト間でメッセージを自動的に送る。
オブジェクト間に階層構造があり、依存関係である。
コンポジション
オブジェクトについてお互いに知っておく必要がある。メッセージを渡す際は、受け取る側が求めている形で渡さないといけない。
コンポジション間のオブジェクトは独立している。
トレードオフの関係
つまり、メッセージを送ることと、オブジェクト間を独立させることはトレードオフの関係です。
結局どっちがいいの?先人の声を聞きましょう
継承とは特殊化です。
継承が最も適しているのは、過去のコードの大部分を使いつつ、新たなコードの追加が比較的少量の時に、既存のクラスに機能を追加する時です。
振る舞いがそれを構成するパーツの総和を上回るならコンポジションを使いましょう。
結論、コンポジションを使いましょう
結論は、プログラミング初学者はコンポジションを使いましょう。
プログラミング初学者は経験も知識も足りません。
コンポジションよりも継承の方が利点があるという判断ができません。
コンポジションを採用すると、小さなオブジェクトが自然に多く作られる傾向があるようです。
そして、これらの小さなオブジェクトは、振る舞いが限定的で、単一の責任を果たしているという、メリットがあります。
継承かコンポジションかの判断ができないなら、コンポジションを使いましょう。
注意点!
リファクタリング後のコードが完全な形になっていっているわけではありません。前よりかは良くなったと捉えてください。
関連する記事
参考文献
オブジェクト指向設計実践ガイド
~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方
最後に
私がブログを書く目的は、素晴らしい本や、素晴らしい方々の技術記事を知って頂きたいからです。ぜひ、上記の参考文献を見て下さい。(noteなので広告とかは一切ありません。)
現在、株式会社grabssに行くために最後の悪あがきをしています!!
現在の進行状況
この記事は93件目の投稿。目標達成!!!!
目標再設定
20日までに100件目指す!!(93件目)
20日超えたが、寝るまでは20日営業日!!(まだ寝てない、ちょっと寝##峠を超え目が覚めた!)
気持ちは20日!
よろしければ、スキボタン及びサポートお願いします。勉強の励みになります。
この記事が気に入ったらサポートをしてみませんか?