社員インタビュー

夏休みの読書感想文 : オブジェクト指向設計 実践ガイド

はじめまして。22Inc.にエンジニアとして中途入社しました藤原です。
実は、昔に営業代理店で「スタンプス」を販売していた経験があります。
そんな私が偶然、縁があり22incにエンジニアとして拾っていただきました。

さて、夏休みの読書感想文を書いていきたいと思います。

読んだ本:

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

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

本を読む前まで

ダックタイピングってなに?

もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルである。

へ〜よくわからんけど、まだ自分には早い技術やな。

オブジェクト指向ってなに?

  • クラスがあるやつやろ?
  • 継承ってのが便利みたい。
  • インスタンスがつくれる(クラスは設計書みたいな感じ)

恥ずかしながらそんなレベルでした。

では、オブジェクト指向を少し身に着けた軌跡をまとめていきます。

オブジェクト指向のオブジェクトの考え方

感動した言葉があったので引用

仮にオブジェクトが人間だとして、自分の関係を説明できるとしましょう。すると、図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の実装が違うみたいな記事を次書こうかなと思ってます。



私たちと一緒に22世紀に残るサービスをつくりませんか?