夏休みの読書感想文 : オブジェクト指向設計 実践ガイド
はじめまして。22Inc.にエンジニアとして中途入社しました藤原です。 実は、昔に営業代理店で「スタンプス」を販売していた経験があります。 そんな私が偶然、縁があり22incにエンジニアとして拾っていただきました。
さて、夏休みの読書感想文を書いていきたいと思います。
読んだ本:
オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方
- 作者: Sandi Metz,?山泰基
- 出版社/メーカー: 技術評論社
- 発売日: 2016/09/02
- メディア: 大型本
- この商品を含むブログ (6件) を見る
本を読む前まで
ダックタイピングってなに?
もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルである。
へ〜よくわからんけど、まだ自分には早い技術やな。
オブジェクト指向ってなに?
- クラスがあるやつやろ?
- 継承ってのが便利みたい。
- インスタンスがつくれる(クラスは設計書みたいな感じ)
恥ずかしながらそんなレベルでした。
では、オブジェクト指向を少し身に着けた軌跡をまとめていきます。
オブジェクト指向のオブジェクトの考え方
感動した言葉があったので引用
仮にオブジェクトが人間だとして、自分の関係を説明できるとしましょう。すると、図4.5では、TripはMechanicに「私は自分が何を望んでいるかを知っているし、あなたがそれをどうやるかも知っているのよ」と伝えているはずせす。図4.6では、「私は自分がなにを望んでいるかを知っていて、あなたが何をするのかも知っているよ」、図4.7では、「私は自分が何を望んでいるかを知っているし、あなたがあなたの担当部分をやってくれると信じているよ」でしょう
オブジェクト指向とそうでないコードの対比
では、具体的にオブジェクト指向なコードとそうでないコードのを比較してみます。 本書にでてくる4章のシーケンス図の例を参考にしながらRailsで自分なりにコードにしてみました。
自転車旅行会社で、旅行の前に整備士が自転車の準備をするプログラム
オブジェクト指向でないコード
Tripクラスのprepare_bicyclesメソッドは、Mechanicクラスをメソッド内で使っており、かつMechanicクラスのメソッドの引数にどのようなデータを渡すかまで知っておりそれも、使っている。 つまり、「私は自分が何を望んでいるかを知っているし、あなたがそれをどうやるかも知っているのよ」という状況
class Trip < ApplicationRecord has_many :trips def prepare_bicycles self.bicycles.each do |bicycle| Mechanic.clean_bicycle(bicycle) Mechanic.pump_tires(bicycle) Mechanic.lube_chain(bicycle) Mechanic.check_brakes(bicycle) end end end class Bicycle < ApplicationRecord belongs_to :trip, optional: true end class Mechanic < ApplicationRecord def self.clean_bicycle(bicycle) bicycle.update(is_clean: true) end def self.pump_tires(bicycle) bicycle.update(is_pump_tires: true) end def self.lube_chain(bicycle) bicycle.update(is_lube_chain: true) end def self.check_brakes(bicycle) bicycle.update(is_check_brakes: true) end end trip = Trip.find(1) trip.prepare_bicycles
オブジェクト指向なコード
Tripクラスの内には、MechanicクラスがなくMechanicに関することを何も知らない。Mechanicクラスのprepare_trip(trip)メソッドで引数としてオブジェクトが注入され、アソシエーションのbicyclesメソッドが呼び出される。Mechanicクラスに引数として渡り、どのように処理されるかはMechanicクラスに任せている。 つまり、「私は自分が何を望んでいるかを知っているし、あなたがあなたの担当部分をやってくれると信じているよ」という状況
class Trip < ApplicationRecord has_many :bicycles end class Bicycle < ApplicationRecord belongs_to :trip, optional: true end class Mechanic < ApplicationRecord def self.prepare_trip(object) object.bicycles.each do |bicycle| self.prepare_bicycle(bicycle) end end private def self.prepare_bicycle(bicycle) self.clean_bicycle(bicycle) self.pump_tires(bicycle) self.lube_chain(bicycle) self.check_brakes(bicycle) end def self.clean_bicycle(bicycle) bicycle.update(is_clean: true) end def self.pump_tires(bicycle) bicycle.update(is_pump_tires: true) end def self.lube_chain(bicycle) bicycle.update(is_lube_chain: true) end def self.check_brakes(bicycle) bicycle.update(is_check_brakes: true) end end trip = Trip.find(1) Mechanic.prepare_trip(trip)
オブジェクト指向ならどういうメリットがあるのか
新機能の実装を例に
e.g) 新しく、自転車レースをする事業が始まったため、整備士(Mechanic)は旅行のときだけではなく、自転車レースのときも自転車を準備する必要がでてきた。
オブジェクト指向でないコード
class Trip < ApplicationRecord has_many :trips def prepare_bicycles self.bicycles.each do |bicycle| Mechanic.clean_bicycle(bicycle) Mechanic.pump_tires(bicycle) Mechanic.lube_chain(bicycle) Mechanic.check_brakes(bicycle) end end end # >>>>>>>>>>>>> 新しく追加 class Race < ApplicationRecord has_many :bicyclesbicycle def prepare_bicycles self.bicycles.each do |bicycle| Mechanic.clean_bicycle(bicycle) Mechanic.pump_tires(bicycle) Mechanic.lube_chain(bicycle) Mechanic.check_brakes(bicycle) end end end # >>>>>>>>>>>>> class Bicycle < ApplicationRecord belongs_to :trip, optional: true end class Mechanic < ApplicationRecord def self.clean_bicycle(bicycle) bicycle.update(is_clean: true) end def self.pump_tires(bicycle) bicycle.update(is_pump_tires: true) end def self.lube_chain(bicycle) bicycle.update(is_lube_chain: true) end def self.check_brakes(bicycle) bicycle.update(is_check_brakes: true) end end trip = Trip.find(1) trip.prepare_bicycles race = Race.find(1) race.prepare_bicycles
オブジェクト指向なコード
class Trip < ApplicationRecord has_many :bicycles end # >>>>>>>>>>>>> 新しく追加 class Race < ApplicationRecord has_many :bicycles end # >>>>>>>>>>>>> class Bicycle < ApplicationRecord belongs_to :trip, optional: true end class Mechanic < ApplicationRecord def self.prepare_bicycles(object) object.bicycles.each do |bicycle| self.prepare_bicycle(bicycle) end end private def self.prepare_bicycle(bicycle) self.clean_bicycle(bicycle) self.pump_tires(bicycle) self.lube_chain(bicycle) self.check_brakes(bicycle) end def self.clean_bicycle(bicycle) bicycle.update(is_clean: true) end def self.pump_tires(bicycle) bicycle.update(is_pump_tires: true) end def self.lube_chain(bicycle) bicycle.update(is_lube_chain: true) end def self.check_brakes(bicycle) bicycle.update(is_check_brakes: true) end end trip = Trip.find(1) Mechanic.prepare_bicycles(trip) race = Race.find(1) Mechanic.prepare_bicycles(race)
つまり
オブジェクト指向でないコードの場合はコピペで prepare_bicycles メソッドを必要となるクラスに書く必要がある。 これは、Tripクラスのprepare_bicyclesの内容が変わると、Raceクラスのprepare_bicyclesも修正する必要がある。 オブジェクト指向なコードの場合は、Mechanicクラスのprepare_bicycleメソッドを変更するだけで大丈夫!
感想
はたして今回書いたサンプルコードはオブジェクト指向なのか? 本を読んでる最中はオブジェクト指向を「完全に理解した」が、ブログを書いていると「なにもわからない」という感じになった。 新しくGoFに関する本を買ったのでオブジェクト指向の習得していく! また、オブジェクト指向に関するブログを書いていきます。 TripクラスとRaceクラスでprepare_bicyclesの実装が違うみたいな記事を次書こうかなと思ってます。