#はじめに
本題はここモジュールからです
##読者対象
読者対象はプログラミング初学者です。
といっても全くの初学者ではなく、Railsチュートリアルを終えたレベルと定義します。
##オブジェクト指向設計実践ガイド
オブエジェクト指向設計実践ガイドの記事は、他の方も書いてありますが、
一度に全部まとめたものが多かったです。
この記事は、オブエジェクト指向らしく、各章ごとに、単一責任に、シンプルさを意識して書きました。
余談ですが、とある弁護士が弁護士は六法全書を丸暗記してないが、各条文を見ただけで、関係ある条文、知識を思い出すそうです。
この記事は、オブジェクト指向設計実践ガイドってこんなこと書いてあったなーと思いだすための記事でもあります。
また、オブエジェクト指向設計実践ガイドを読んだことがなくても、こういうことが書いてあるとのかー、とさわりだけでも理解して頂けたら幸いです。
##オブジェクト指向設計実践ガイドで得た3つのポイント
リファクタリング前のコードを読み、コードの危うさを察知できる。
リファクタリングするために、より抽象的なコードの書き方を学べる。
gemなどの抽象的なコードを読解できる。
##一問一答
一問一答風の構成にしています。
リファクタリング前のコードを読み、コードの危うさ、嗅覚を養っていきましょう。
その後、リファクタリング前のコードの危うさを説明し、自分なりにリファクタリングしたコードを書いてみましょう。
最後に、もう一度、リファクタリング前のコードと、リファクタリング後のコードを読み比べ、
抽象的な思考、抽象的な書き方を共に学んでいきましょう。
#モジュール
今回はモジュールについて説明します。
下記リファクタリング前のコードはコンポジションで作られ、十分に使えるコードです。
今回は、インスタンスの処理をモジュール使ってすっきりさせましょう。
オブエジェクト指向実践ガイドの第8章 「コンポジションでオブジェクトを組み合わせる」、
この章を一部理解していると言えるでしょう。(残りの知識は別記事に書きます)
##リファクタリング前
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
chain = Part.new(name: "chain",description: "10-speed")
road_tire =
Part.new(name: "tire_size",description: "23")
tape =
Part.new(name: "tape",description: "red")
mountain_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)
road_bike_parts =
Parts.new([chain,road_tire,tape])
road_bike =
Bicycle.new(
size: "L",
parts: road_bike_parts
)
road_bike.size
road_bike.spares
mountain_bike_parts =
Parts.new([chain,mountain_tire,front_shock,rear_shock])
mountain_bike =
Bicycle.new(
size: "L",
parts: mountain_bike_parts
)
mountain_bike.size
mountain_bike.spares
###モジュールを用いる
chain = Part.new(name: "chain",description: "10-speed")
road_tire =
Part.new(name: "tire_size",description: "23")
tape =
Part.new(name: "tape",description: "red")
上記の処理をすっきりさせたいですね。
モジュールを使ってリファクタリングしてみましょう。
##リファクタリング後
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
module PartsFactory
def self.build(config,part_class = Part,parts_class = Parts)
parts_class.new(
config.collect{ |part_config|
part_class.new(
name: part_config[0],
description: part_config[1],
needs_spare: part_config.fetch(2,true)
)
}
)
end
end
road_config = [
["chain","10-speed"],
["tire_size","23"],
["tape_color","Fox"]
]
mountain_config =
[
["chain","10-speed"],
["tire_size","Manitou",false ],
["rear_shock","Fox"]
]
road_bike =
Bicycle.new(
size: 'L',
parts: PartsFactory.build(road_config)
)
road_bike.spares
mountain_bike =
Bicycle.new(
size: "L",
parts: PartsFactory.build(mountain_config)
)
mountain_bike.spares
##モジュール
モジュールを新たに作り、インスタンスの処理がすっきりしました。
##注意点!!
PartsFactoryモジュールはまだ改善の余地はあります。(後日、別記事で書きます)