#オブジェクト指向
オブジェクト指向のアプリケーションは部品から構成される
その部品そのものがオブジェクト。
部品には相互作用があり、その相互作用を定義するのがメッセージ
メッセージを送るには、送り手が、受け手のことを知っている必要がある
↓
「知る」ことにより、そのコードの挙動そのものに「依存」が生まれる
↓
この依存関係こそが、アプリケーションの変更を妨げるいちばんの要因となるために、依存関係そのものを適切に管理する必要がある。
↓
そこで使用する道具が「オブジェクト思考設計」
##なぜオブジェクト思考設計が大切なのか?
###オブジェクト思考設計に従えば、変更が容易になる
依存関係を適切に管理することにより、変更のコストは、その変更の大きさに、見合ったものとする事ができる
###変更が簡単とは?
・変更による副作用がない
・変更の大きさと変更のコストが一致する
・既存のコードは簡単に再利用可能
・変更の中には「追加」も含まれるが、このコードそのものも変更が容易なものである事
求められるコードの性質
・見通しがいい => 変更が与える影響範囲が明確にわかる
・合理的 => 変更にかかるコストが、変更によってもたらされる利益に等しい
・利用性が高い =>
新しい、または、予期していなかった環境下でも再利用可能
・模範的 => 変更を加える人が、自ずと、コードの品質を保つようなコード
#1、クラスはシンプルであれ
変更に強いコードを書くためには、クラスの責任を最小にする事が大切
##シンプルであるかの指標
クラスを説明する際に、端的に説明できない or 「それと」「または」という言葉が出てくる
↓
こうなるとそのクラスは複数の責任を持ってしまっていることになる、
##何が起こるか
複数の責任を持つ事で、クラスの再利用が難しくなる→シンプルでないため、振る舞いを部分的に再利用したいという事が出てきても、利用したいコードだけを複製してしまったり、必要な振る舞いのみにアクセスできる仕組みを作るしかない
これはクラスだけでなくメソッドも共通で柔軟で再利用可能なものにするには、単一責任にするしかない
・利用したいコードを複製するリスク
コードを複製するということは、そもそも変更があった場合、複製した箇所全てに変更を加えなければ、ならない可能性がある。
よって、メンテナンス性の低下(ミスも増える)、思わぬバグを生む
・必要な振る舞いにのみアクセスできる仕組みを作り、再利用を実現するリスク
そもそも再利用した先のクラスと元のクラスとでは、振る舞いが一致しているわけがないので、大元のクラスに変更が加わった途端に、絡み合っているクラスが全て壊れる可能性がある
#2、変更を歓迎するコードを書く
・データではなく、「振る舞い」に依存する
特にクラスの中に、クラスメソッドを置くだけで、インスタンスを生成せずにコードを,書いてしまうと、メソッドの使い回しが効かなくなり、クラス自体が再利用不可能なものになる
・インスタンス変数を隠匿する
例
class Car
#読み出し専用のアクセサを定義できる。
attr_reader :japan_car, :germany_car
def initialize(japan_car, germany_car)
@japan_car = japan_car
@germany_car = germany_car
end
end
result = Car.new("GT-R", "i8")
puts(result.japan_car) #=>GT-R
puts(result.germany_car) #=>i8
・メソッドの引数にキーワード引数を使う
例
class Exile
class << self
def greeting(msg: 'hello', name: 'TAKAHIRO')
p "#{msg}, #{name}"
end
end
Exile.greeting #=>"hello, TAKAHIRO"
Exile.greeting(msg: 'Hi', name: 'ATSUSHI') #=> "Hi, ATSUSHI"
Exile.greeting(name: 'ATSUSHI', msg: 'Hi') #=> "Hi, ATSUSHI"
# 引数の順番を気にせずに渡せる
end
#3、ダックタイピングを理解する
「オブジェクトがアヒルのように歩き、アヒルのように鳴くメソッドを実装しているなら、
そのオブジェクトのクラスが何であれ、アヒルと同じ役割を果たせる」
どの特定のクラスにも依存しないインターフェースを定義すること
メソッドの具体的な実行内容は、そのメソッドに応答できるオブジェクトが知っている
↓
どんなメソッドもそれに応答できるオブジェクトを渡せば、あとは良しなにやってくれるということを理解する
case文などを使用し、分岐をどんどん増やすことで正しいメッセージを正しいオブジェクトに送ることは解決するが、依存を増やすことになる。
ダック信頼することにより、依存関係を減らす事ができる
例
class Car
def car_maintenance(cars)
cars.each do |car|
# carの所属するクラスのインスタンスメソッドを呼び出す
# selfを渡して、処理に必要なデータは呼び出し先で良しなに使ってもらう
car.car_maintenance(self)
end
end
class Nissan
def car_maintenance(car_instance)
# 処理
end
end
class Honda
def car_maintenance(car_instance)
# 処理
end
end
class Toyota
def car_maintenance(car_instance)
# 処理
end
end
メリット
・オブジェクトの属性条件により分岐をしなくて良くなる
・コードの見通しが良くなる
・変更に柔軟になる
4、継承
継承の一番の強みは、スーパークラスに定義された、メソッドに依存することであり
同時に、弱みにもなる。スーパークラスへ変更が施された場合に、修正の被害を、
大きく受けることになる。
まずサブクラスを作る際の大前提として、
「サブクラスは、スーパークラスを特化したものであるべき」だということ。
そのため、スーパークラスは抽象度が大切で、スーパークラス内のコードを使わない
サブクラスは存在してはならない。
またサブクラス内でスーパークラスと同名のメソッドを定義すると、「super()」を使用する事ができるが、
依存関係が増えてしまうため、なるべく、書かなくても良いように実装する。