Closed7

Head Firstデザインパターン 第2版 1章を筆者と対話をしながら読んでみた

bun913bun913

1章の前にこの本に向いている人や、実践してほしい読み方、本書で工夫されたことなどが書いてある。

その工夫の中に「講義ではなく、物語を語るような砕けた文体をわれわれは使っています。あまり深刻に受け取らないでください。ディナーパーティーで仲間と会話する方が講義よりも身が入りますよね?」とあった。

確かに書籍を読みながら筆者と会話しているように疑問とか、ツッコミとかを入れた方がみに入りやすいかもしれないので、自分も本書に返事してみようと思った

bun913bun913

簡単な SimUDuck アプリから始まった

こんな感じのクラス図の例がある

displayメソッドが絶対にサブクラスごとで表示方法が異なるので、display() メソッドは抽象メソッドにしているらしい。

displayをサブクラスでオーバーライドして、具体的な実装にするのかな?

ところで、僕は よっしゃ。差分プログラミングや。 みたいな感じで、こんなコードを残してきているのだけど、やっぱり悪いことなのかな?

この先を読むのが怖くて仕方ないよ。

【参考】
そういえば、fukabori.fm でtwadaさんがこのことについて、語られていたような気がする。

https://fukabori.fm/episode/48

bun913bun913

カモが飛ぶようにする必要が出てきた

もうこの時点で嫌な予感がするけど、「なーんだ。じゃあ、親クラスに flyメソッドを追加すれば良いんだね」という感じでこんな感じにしたらしい

まぁ。嫌な予感。

ミノ駆動さんのクソコードでみたことあるような気がするけど、やっぱりそんな感じで、 飛ばないサブクラス が出てきたりすることを考慮していなかったみたい・・・

https://twitter.com/minodriven/status/1353251239237095430

bun913bun913

ここで考えるべき設計原則 「 .アプリケーション内の変化する部分を特定し、不要な部分と分離する。 」を考えるらしい。

不変な部分から変化する部分を分離するということで、「飛ぶ振る舞い」を抽出するんですって。

そんな感じの原則は聞いたことあるけど、具体的に何をするのかわからんのですよ。 私は。

そこで更に出てくる設計原則。

実装に対してではなく、インターフェースに対してプログラミングする。」が登場。

その言葉100万回位聞いた気がするけど、良く分からんのですよ。

Javaはよく知らんけど Interface のことかと思ったけど、必ずしも言語レベルで出てくる Interface とは異なるみたい。

抽象スーパータイプとして振る舞いを抽出し、多様な振る舞いの実装をクラスとして実装するらしい。

こんな感じ。

要するに

  • 「サブクラスごとに実装を書く」としてしまえば、サブクラスA と サブクラスBで同じ動作をするのに、サブクラスごとに振る舞いを実装する必要があるので、 再利用性が低い
  • 他にもサブクラスごとの全ての振る舞いを把握するのが激ムズ

とかの都合の悪い部分を解消するのに使えるっていうことかな。

bun913bun913

で・・・抽出したけどどうすんの・・・

というのをDuckクラス側も使って解決するらしい。

まずは 振る舞いをするクラスをプロパティとして保有する。

そして、 fly というメソッドを削除。飛ばないカモもいるからね。

そして、 perfomFly() というメソッドを追加するらしい。

以下みたいな感じで。

でこんな感じで Duckのサブクラスで以下のように振る舞いを設定することができる

public class MallarDuck extends Duck {
  public MallarDuck() {
    flyBehavior = new FlyWithWings():
  }
}

でもこのままだと、「実装に対してプログラミングするな」とか言っているくせに 「各サブクラスで、 具象クラスをインスタンス化しとるやんけ 」となる。

けど、これを後の章でスマートに解決していくらしい。

bun913bun913

更に言えば、こんな感じでセッタークラスを用意することで、

呼び出し側が 実際のクラス側での実装に注意を払い過ぎることなく、動的に実際の振る舞いを変更することができる

Duck model = new ModelDuck();
model.setFlyBehavior(new DummyFly());
model.performFly();
bun913bun913

こんなふうに 一覧の振る舞い(アルゴリズム)を定義して、カプセル化して交換できるようにすることを Strategy パターン と表現するらしい。

わしは思った。

なぜDuckはInterfaceではなく、普通にクラスの実装なのだろう 」と。

それも作者によると

Duckをインターフェースにせずに MallardDuckなどの具体的なカモのクラスに共通のプロパティとメソッドを継承させればメリットを享受できます

とある。

つまり

  • 変わらない部分 => 親クラスに定義しておいて、再利用ができる
  • 変わる部分 => 別にインターフェースとして切り出すことで、再利用もできるし、うまいこと分離できている

から、素直に継承の良い部分だけ使えるんだよ。ってことなのかと受け取った。

インターフェースに対して実装するという意味が少し分かった気がする。本当に少しだけ。

このスクラップは2022/10/16にクローズされました