#はじめに
本題はここフックメッセージからです
##読者対象
読者対象はプログラミング初学者です。
といっても全くの初学者ではなく、Railsチュートリアルを終えたレベルと定義します。
##オブジェクト指向設計実践ガイド
オブエジェクト指向設計実践ガイドの記事は、他の方も書いてありますが、
一度に全部まとめたものが多かったです。
この記事は、オブエジェクト指向らしく、各章ごとに、単一責任に、シンプルさを意識して書きました。
余談ですが、とある弁護士が弁護士は六法全書を丸暗記してないが、各条文を見ただけで、関係ある条文、知識を思い出すそうです。
この記事は、オブジェクト指向設計実践ガイドってこんなこと書いてあったなーと思いだすための記事でもあります。
また、オブエジェクト指向設計実践ガイドを読んだことがなくても、こういうことが書いてあるとのかー、とさわりだけでも理解して頂けたら幸いです。
##オブジェクト指向設計実践ガイドで得た3つのポイント
リファクタリング前のコードを読み、コードの危うさを察知できる。
リファクタリングするために、より抽象的なコードの書き方を学べる。
gemなどの抽象的なコードを読解できる。
##一問一答
一問一答風の構成にしています。
リファクタリング前のコードを読み、コードの危うさ、嗅覚を養っていきましょう。
その後、リファクタリング前のコードの危うさを説明し、自分なりにリファクタリングしたコードを書いてみましょう。
最後に、もう一度、リファクタリング前のコードと、リファクタリング後のコードを読み比べ、
抽象的な思考、抽象的な書き方を共に学んでいきましょう。
#フックメッセージ
今回は第6章フックメッセージについて説明します。
下記コードを読んで、このコードの危うさを説明し、自分なりにリファクタリングしたコードを書けるレベルに達しているなら、
オブエジェクト指向設計実践ガイドの第6章 「継承によって振る舞いを獲得する」、
この章を理解していると言えるでしょう。
##注意点
下記コードは第6章の後半とあって、それなりにリファクタリングされています。
絶対にダメな書き方というわけではありませんので注意して下さい。
##リファクタリング前
class Bicycle
attr_reader :size,:chain,:tire_size
def initialize(args)
@size = args[:size]
@chain = args[:chain] || default_chain
@tire_size = args[:tire_size] || default_tire_size
end
def spares
{
tire_size: tire_size,
chain: chain
}
end
def default_chain
"10-speed"
end
end
class RoadBike < Bicycle
attr_reader :tape_color
def initialize(args)
@tape_color = args[:tape_color]
super(args)
end
def spares
super.merge({tape_color: tape_color})
end
def default_tire_size
"23"
end
end
class MountainBike < Bicycle
attr_reader :front_shock,:rear_shock
def initialize(args)
@front_shock = args[:front_shock]
@rear_shock = args[:rear_shock]
super(args)
end
def spares
super.merge({rear_shock: rear_shock})
end
def default_tire_size
"2.1"
end
end
bike = RoadBike.new(size: "L",tape_color: "red")
bike.size
bike.spares
###このコードの危うさ
新たにサブクラスが作られたとします。
このサブクラスでdefault_tire_sizeが書かれてなかったら、エラーが発生します。
また、superの書き忘れで思わぬエラーが発生します。
サブクラスどんどん増えていくにつれて、上記のエラー発生率が高くなります。
##リファクタリング後
class Bicycle
attr_reader :size,:chain,:tire_size
def initialize(args)
@size = args[:size]
@chain = args[:chain] || default_chain
@tire_size = args[:tire_size] || default_tire_size
post_initialize(args)
end
def post_initialize(args)
nil
end
def spares
{
tire_size: tire_size,
chain: chain
}.merge(local_spares)
end
def local_spares
{}
end
def default_chain
"10-speed"
end
# 適切なエラーを表示するため
def default_tire_size
raise NotImplementedError
end
end
class RoadBike < Bicycle
attr_reader :tape_color
def post_initialize(args)
@tape_color = args[:tape_color]
end
def local_spares
{tape_color: tape_color}
end
def default_tire_size
"23"
end
end
class MountainBike < Bicycle
attr_reader :front_shock,:rear_shock
def post_initialize(args)
@front_shock = args[:front_shock]
@rear_shock = args[:rear_shock]
end
def local_spares
{rear_shock: rear_shock}
end
def default_tire_size
"2.1"
end
end
bike = RoadBike.new(size: "L",tape_color: "red")
bike.size
bike.spares
##フックメッセージ
default_tire_sizeのエラー処理を親クラスに記載しました。
ここから本題です。
親クラスのBicycleが各サブクラスにフックメッセージを送るようにしました。(post_initialize(args))
フックメッセージを使う目的は、
サブクラスからアルゴリズムの知識を取り除き、代わりにスーパークラスに制御を戻すことです。
(オブジェクト指向実践ガイド p172)
今回実装によって、superの書き忘れによるエラーを防ぐことができます。
また各サブクラスのコードの可読性も増しました。
新しいサブクラスを作る際も、リファクタリング前より簡潔に書けるようになります。
#参考文献
オブエジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方