こちらの記事の続きです。
6章 条件分岐
ここでは迷宮化したif
やswitch
の分岐処理を解きほぐす技法について記述されていました。
早期returnや別メソッドへの切り出しなど様々方法が記載されていましたが、特に印象深かったものをまとめます。
interfaceとmapでswitchの重複を解消する
switch
でクラスごとの処理をしているとき、それぞれを同じinterfaceでクラス化し、Mapにまとめます。そうするとswitch
を使用すること無く、Mapにわたす引き数によって分岐を表現することができます。ちなみにこのようにinterfaceを用いて処理を一斉に切り替える設計がストラテジパターンと呼ぶそうです。
だめなコードの例
def name(fruit)
case fruit
when 'apple'
puts 'りんご'
when 'banana'
puts 'バナナ'
when 'orange'
puts 'みかん'
end
end
def price(fruit)
case fruit
when 'apple'
puts 100
when 'banana'
puts 150
when 'orange'
puts 200
end
end
ここでは数が3つしか無いですが、条件分岐数が増えたときにコードの修正がしにくくなります。
以下にRubyでストラテジパターンをベースに考えた例です。これはあくまで書籍で記載されていたJavaのサンプルコードををRubyで実装するなら、と考えて記述したものです。(Rubyのinterfaceについては[0]を参考にしました。)
#------------------
# インターフェース
#------------------
module FruitIF
def name
raise NotImplementedError.new("#{self.class}##{__method__} が実装されていません")
end
def price
raise NotImplementedError.new("#{self.class}##{__method__} が実装されていません")
end
end
#------------------
# フルーツクラス郡
#------------------
class Apple
include FruitIF
def name
puts 'りんご'
end
def price
puts 100
end
end
class Banana
include FruitIF
def name
puts 'バナナ'
end
def price
puts 150
end
end
class Orange
include FruitIF
def name
puts 'みかん'
end
def price
puts 200
end
end
#-----------------------------
# フルーツの情報を表示するクラス
#-----------------------------
class FruitInfo
FRUIT_MAP = {
apple: Apple.new,
banana: Banana.new,
orange: Orange.new
}
def name(fruit)
FRUIT_MAP[fruit].name
end
def price(fruit)
FRUIT_MAP[fruit].price
end
end
FruitInfo.new().name(:apple)
=> りんご
FruetInfo.new().price(:banana)
=> 150
これでswitch(rubyではcase)を使わずに、引き数に渡す物によって出てくる情報を変えることができるようになりました。
感想
分岐がif
やswitch
を使わずに表現できるのはかなり意外で結構テンションが上りました。自分が知っているコードの表現の方法は本当に氷山の一角で、たくさん他にも自分の知らないパターンがあるとなるとこれからいろんなことを勉強するのがより楽しみになりました。ここでいろんな良い構造を知ることができて本当に良かったなぁという感想です。
7章 コレクション
この章ではコレクションのネストを解消する技法が紹介されています。
each文での早期breakに加えて「ファーストクラスコレクション」という技法が紹介されていました。
早期breakについては割愛し、ここでは「ファーストクラスコレクション」についてまとめます。
ファーストクラスコレクション
ファーストクラスコレクションとは「コレクションをインスタンス変数に持ち、それを処理するロジックがまとまったクラス」のことです。配列に対する処理を凝集することができるようになります。
また外部にコレクションを渡すときは変更できなくすることで、外部で新たな処理が書かれて凝集度が低下するのを防ぐことができます。
class FruitCollection
attr_reader :fruits
def initialize(fruits)
@fruits = fruits
end
def business_logic
.....
end
end
感想
ファーストクラスコレクションはrubyでも実装できそうですが、いまいちどこで使えばいいのかわからずこれから理解を深めていこう、という印象です。一方で配列の早期breakなどは簡単に理解でき、すぐにでも取り入れていこうと思います。
参考
[0] Rubyでインターフェースクラスを実現する
https://qiita.com/developer-kikikaikai/items/b1e2bd1c2bbc223de534
[1] Rubyで作るファーストクラスコレクション
https://qiita.com/gashiura/items/999a8c36e47a07fa4b27#%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88
[2] Rubyでファーストクラスコレクションをベースに、検索結果をオブジェクト化するhttps://tech.itandi.co.jp/entry/2022/11/08/180000