オブジェクト指向設計実践ガイド5章を読んだ感想

この章は、ダックタイピングの話。これまでの章もそうだったけど、いかに抽象化して、再利用可能にして、変更コストを下げるかというのがたくさん述べられてた。やはりそれだけ重要ってことですね。ダックタイピングは、動的型付け言語の抽象化を促してくれるやり方の一つで、ダックタイピングを使って抽象化しましょうという感じの内容。めちゃめちゃ勉強になった。ここまで読み進めて、抽象化、再利用、インターフェース、変更コストなど、オブジェクト指向設計で重要と思われるキーワードを何度も目にして思ったことなんだけど、この概念たちは、プログラミングの世界に限らず、世の中一般的なことで考えても、当てはまる気がした。仕事とか、日常生活の中で起こる何かしらの具体的な行動、成功体験を、自分なりに解釈して、抽象度をあげて整理して、それをまた別の行動にも応用、再利用するみたいなことは、割と一般的に考えてる人は多いと思う。プログラミングの世界でも、具体的なコードがいくつかあって、それらの共通する部分を切り出して、抽象的に考えて、コードに落とし込むことによって、応用の効く、再利用可能なコードが出来あがるんじゃないかなと思ったりした。

イントロ

  • 次の2つの考えを組み合わせるのが強力な設計テクニック
    • メッセージ中心の設計
    • 厳密に定義されたパブリックインターフェースの構築
  • これはダックタイピングとして知られてるテクニック。この章では、ダックタイピングについて勉強する

5.1 ダックタイピングを理解する

  • プログラミング言語での「型」は、変数の中身の分類を示す
  • オブジェクトは、ただ一つのインターフェースだけに応答するってわけじゃない
    • Rubyのオブジェクトは、テーマにそってマスクを変える仮面武道会の参加者みたいな感じ(型を明示的に示さない言語仕様)
  • アプリケーションによっては、特定の1つのクラスに関連しないパブリックインターフェースをいくつも定義することもある
    • 4章では、クラス「内」のインターフェースに注目した話たっだけど、ここではそれ以外のインターフェースの話。
    • ポイントは、オブジェクトが何で「ある」かではなく、何を「する」か。
    • あらゆるオブジェクトが他のオブジェクトを常に信頼して、それでいてどんな種類のオブジェクトになれる場合、設計の可能性は無限大になる。めちゃめちゃ柔軟な設計を作れる
  • 柔軟性を賢く利用する方法
    • クラスをまたいだ型を認識すること
      • クラスをまたいだ型というのは、ダックタイプのパブリックインターフェースのこと
    • クラス内で作るパブリックインターフェースの型パターンと同じくらい入念に作る(第4章でのパターンの話)
  • ダックタイプとは
    • オブジェクトのクラスが何であろうとそのメソッドが呼び出せれば良しとするプログラミングスタイルのことをダックタイピングという
    • ダックタイプを説明する最善の方法は、ダックタイプを使わないとどうなるかを検討してみると良い
    • この本の言い回しだと、ダックタイプは、「クラスをまたいだ型、インターフェース」を表してるっぽい
  • ダックを見逃してるパターン1(以下、サンプルコードとシーケンス図)
    • 4章の図4.6に近い例

  • ダックを見逃してるパターン2(以下、サンプルコードとシーケンス図)
    • 要件が変わって旅行の準備がさらに複雑になった場合を想定したもの
    • このサンプルコードは、自身を出口のない苦境に陥れるプロセスの最初の一歩。重要なメッセージを見つけられなかったときに書いてしまうようなコードで、依存満載。よくない。
    • このようなコードを書いちゃう原因は、prepareがMechanicインスタンスを想定しているという考えからこうなちゃってる。(心の奥底では、引数はMechanicであると思っちゃってる)
    • caseとかで、引数となるインスタンスのパターンを切り替えるようにしてるけど、これだと依存が爆発的に増える書き方
    • シーケンス図をみても、複雑な感じになってる

  • ダックを見つける
    • ダックを見つけられず依存満載のコードから、依存を取り除く鍵となるのは、「Tripのprepareメソッドは単一の目的を果たすためにあるので、その引数も単一の目的を共に達成するために渡されてくるということを認識すること」です
    • 共通の目的を考えると、prepareメソッドが何を必要とするかという話になって、それは「旅の準備をすること」だと言える。
    • 次の図は、ダックを生み出した場合を表した図
      • これまは、prepaeメソッドは、引数のクラス別で処理方法を変えてたけど、ここでは、prepare_tripに応答できる複数のPreparerインスタンスが引数にくることを想定するようになった

  • 上記の図を表したサンプルコード
  • ダックタイピングの影響
    • もともとprepareメソッドは具象クラスに依存してた。変更後は、ダックタイプに依存するようになった
    • 最初のコードはの具象性は、コードを理解しやすいが、拡張に危険が伴う
    • ダックタイプ化されたコードは、抽象的で理解力が求められるけど、拡張が容易になった
    • 具象化と抽象化のコストの緊張は、オブジェクト指向設計で基本的なこと
  • ポリモーフィズム
    • 多様性、多相性
    • 同じメッセージに応答できる能力のこと
    • メッセージの送り手は、受けてのクラスを気にする必要が無く、受け手は、それぞれが独自化した振る舞いを提供する

5.2 ダックを信頼するコードをかく

  • ダックタイプの実装自体は、比較的簡単
  • ダックタイプが必要であるかを見つけることと、そのインターフェースを抽象化するのがむずい
  • 隠れたダックを認識する。次のものはダックで置き換えれる
    • クラスで分岐するcase文
    • kind_of?とis_a?
    • responds_to?
  • もっとも簡単なダックタイプは、単純にパブリックインターフェースの取り決めとしてだけ存在してるもの(この章でダックタイプ化したサンプルコードもこの類に入る)
    • いくつか、異なるクラス(Mechanic,Driver,TripCoordinator)にprepare_tripを実装することで、異なるクラスたちをPreparerとして抽象的にフル回せるようにした
  • ダック間でコードを共有する
    • この章でのPreparerダックたち(Mechanic,Driver,TripCoordinator)は、それぞれが各クラスで独自の振る舞いを用意してる。実装は共有していない
    • ダックタイピに慣れてくると、ダックタイプを実装するクラスは振る舞いも幾らか共有する必要があることに気づく。
      • このあたりは7章で説明する(コードを共有するダックタイプ)
  • 賢くダックを選ぶ
    • kind_of?やis_a?、responds_to?を使われてても、あえてダックにしないほうが良い場合もある
    • 例えば、HashやIntegerのような、Rubyのコア機能へ依存してるとかだと、コアクラス自体が安定してるものと考えて良いので、あえてダックにしなくてもいいという感じ

5.3 ダックタイピングへの恐れを克服する

  • 静的型付け言語と動的型付け言語の比較
  • 静的型付け言語は、それぞれの変数の型とメソッドのパラメーターを明示的に宣言する
  • 動的型付け言語だと、この要求を省く
  • 静的型付け支持者があげる静的型付けのメリット
    • コンパイラコンパイル時にかたのエラーを発見してくれる
    • 可視化された型情報は、文書の役割も果たしてくれる
    • コンパイルされたコードは最適化され、高速に動作する
  • 動的型付け支持者があげる動的型付けのメリット
  • ダックタイピングは、動的型付けの上に成り立つ

ダックタイピングまとめ

  • メッセージこそ、オブジェクト指向アプリケーションの中心にあるもの
  • メッセージはパブリックインターフェースを介して、オブジェクト間で交換される
  • ダックタイピングは、これらのパブリックインターフェースを特定のクラスから切り離し、何であるかではなく、何をするかによって定義される。仮想の型を作る
  • ダックタイピングは、根底にある抽象を表し、柔軟性を高めてくれる

全体的な感想

  • この章の説明を読むとダックタイプについてわかった気になるけど、実際のコードをかくときとかでこの考えを利用できるかは、また別の話のように思えるので、実務レベルで使えるようになるため、この考えが含まれてるコードをたくさん読む、そしてたくさん手を動かす、ってことを進めていきたい

  • ここまで読むのに時間かかり過ぎだー

オブジェクト指向設計実践ガイド4章を読んだ感想

4章読み終えた。この章は「柔軟なインターフェースを作る」話。今回もかなり読み応えがあって、特に 4.3のパブリックインターフェースを見つける、のところはすごく勉強になった。また読み返したい。特に勉強になったところは、オブジェクト指向を考えるとき、オブジェクト同士が、どうメッセージのやりとりするかを考えると、より柔軟なアプリケーショを書けけるようになると述べられてた部分で、これができるようになると、設計者のキャリアとしても転機が起きてることがいえるらしく、すごく重要な考えなんだと思った。

内容的には、この本で挙げられてる例を踏まえると、一応わかった気にはなるのだけど、実際にこのメッセージ的な設計の考えを、今やってる開発(Laravel)とかで当てはめようとすると、いまいちピンとこなかった。いまいちピンとこなかったところとしては、この本でオブジェクトのメッセージについて説明している例(Tripクラスが、自転車の準備状態のチェックを、Mechanicクラスにメッセージを送って確認するっていう例)を、コントローラやモデル、サービスクラスなどで、どんな感じで当てはめたらいいのかわからず、難しいというような感じ。そもそも、どういうクラスが必要で、各クラスたちにどういう責任を持たせておくべきか、みたいなとこから考え切れてないので、そういうとこも原因の一つだと思うけど、それでもむずそう。頑張って理解していけるようにする。

あと、メッセージに基づく設計の方が、柔軟性があるアプリになる理由を少し考えてみた。オブジェクト同士のメッセージって、依存関係が発生するような場所になりやすそうな感じなので、そこを中心にするってことは、依存関係の管理とかをより中心的に見れるようになり、それでより柔軟性のあるアプリになる感じなのかなと思ったりした。概念はわかったので、あとは実践していけるようにする。実践していくにあたり、オブジェクト間のメッセージを見つけることもすごく重要で、シーケンス図を使って探すのがめちゃめちゃ良さそう。なので、シーケンス図もっと使っていけるようにする。設計初心者がたどる「オブジェクト」指向なコードの動きを表した図が紹介されてて、そこでの設計初心者の状態を早く抜けれるよう頑張りたい

以外、本読んで、気になったところや重要そうだと思ったところ抜粋

4.1 インターフェースを理解する

  • 丸は、オブジェクトを表してる
  • 上記図の左側のやつは、いろんなものを明らかにしすぎてて、いろんなとこにアクセスできてる。上記図の右側のやつは、制約をつけてるので、特定のとこにしかアクセスできないようになってる
  • 左側のやつは、もはや再利用不可能。依存性注入や、単一責任などをちゃんと設計できててても、このような状態になる可能性があって、大事なポイントとしては、クラスが何を「する」かではなく、何を「明らかにする」かを設計しないといけない
  • 設計で考慮したいこと(明らかにしたいこと)

    • オブジェクトが何を知っているのか(責務)
    • オブジェクトが誰を知っているのか(依存関係)
    • オブジェク同士がどうやって会話するのか(メッセージをやりとりするのか)
      • オブジェクトの会話は、オブジェクトの「インターフェース」を介して行われる
  • オブジェクトにアクセスする際に必要なのがインターフェース。インターフェースの概念は色々あるけど、この章では、クラス「内」にあるインターフェースの話

    • クラス内にあるインターフェース
      • クラスは、メソッドを実装し、そのうちいくつかは他のオブジェクトから使われることが意図されている。
      • それらのメソッドがそのクラスのパブリックインターフェースを構成する
    • クラス内にあるインターフェース、以外にもインターフェースの概念はいくつかあって、例えば、複数クラスにまたがって、どの単一のクラスからも独立してるもの、があげられたりする
      • これは5章のダックタイピングでコストを削減するのところで話す

4.2 インターフェースを定義する

  • インターフェースとは、オブジェクトにアクセスするための入り口
  • レストランで例えると、メニューはパブリックなインターフェースと書いてあるのがわかりやすかった(お客さんは、選んだメニューがどうやって作られるかは知らなくていい)
  • パブリックインターフェースを作るメソッドによって、クラスが外の世界に顔をだす
  • パブリックインターフェースの特性
    • クラスの主要な責任を明らかにする
    • 外部から実行されることが想定される
    • 気まぐれに変更されない
    • 他者がそこに依存しても安全
    • テストで完全に文書化されてる
  • プライベートインターフェースの特性
    • 実装の詳細に関わる
    • 他のオブジェクトから送られてくることは想定されていない
    • どんな理由でも変更されえる
    • 他者がそこに依存するのは危険
    • テストでは言及されないこともある

4.3 パブリックインターフェースを見つける

  • パブリックインターフェースを見つけて定義する
    • クラスの主要な責任を見つけて定義する
    • パブリックインターフェースの設計が良いと、変更に対するコストが下がる
      • なぜ?
  • アプリケーションを新規で作成するときに、いい感じのインターフェースを発見する方法
    • ツールを使う
      • シーケンス図
      • ユースケース(要件)を満足させるために、必要なオブジェクトとメッセージの両方を検討する際、まずコードを書かなくても良い
    • ドメインオブジェクト
      • アプリケーションの性質から、なんとなくどんなクラスが必要なのか検討をつくものがある
      • それはデータと振る舞いの両方を兼ね揃えた名詞だったりする。これをドメインオブジェクトという
      • ドメインオブジェクトは、最終的にデータベースに表されるものだといえる
      • ドメインオブジェクトは簡単に見つけられるけど、設計するときに中心になるものではない。注意を向けるべきなのは、オブジェクト間で交わされるメッセージ
        • なぜ?
  • シーケンス図
    • シーケンス図を使うことで、オブジェクト間がどんなメッセージを交わすかわかりやすくなる
    • ユースケースでの名詞は、オブジェクトになる
    • ユースケースでのアクションは、メッセージになる
    • オブジェクトは、パブリックインターフェースを介してのみ、お互いに交信すべきなので、シーケンス図を書くことで、パブリックインターフェースを明らかにできる
    • シーケンス図を書くことによって、議論が逆転する
      • クラスに基づく設計からメッセージに基づく設計へ
      • 「このクラスが必要なのは、知ってるけど、これは何をすべきなんだろう」から「このメッセージを送る必要があるけど、誰が応答すべきなんだろう」へ
  • 適切に定義されたパブリックインターフェースをもつ再利用可能なクラスを作るポイント
    • 「送り手の望みを頼むメッセージ」と「受け手にどのように振る舞うかを伝えるメッセージ」の違いを考える
    • メッセージをやりとりする際の「何を」対「どのように」の重要性
      • オブジェクト間でメッセージのやりとりをするとき、「どのように」の部分まで、振る舞いを知っている必要はなくて、「何を」の部分だけにできると、依存関係が小さくなっていよい。オブジェクト同士は、お互いをあまり知りすぎないほうが、コードのメンテナス性や柔軟性が向上する
  • 図4.5から4.7は、設計初心者がたどる「オブジェクト」指向なコードの動き
  • 図4.5では、TripはMechanicに「私は自分が何を望んでいるかを知っているし、あなたがそれをどのようにやるかも知ってるよ」と伝える

  • 図4.6では、TripはMechanicに「私は自分が何を望んでいるかを知っていて、なあなたが何をするかも知っているよ」

  • 図4.7では、TripはMechanicに「私は自分が何を望んでいるかを知っているし、あなたがあなたの担当部分をやってくれると信じているよ」

  • まだ、定義されていないオブジェクトが必要である認識を得るための方法

    • シーケンス図を使う
    • Consumerから発信されるメッセージ(適切な旅行を見つけるためのメッセージ)、を受け取るオブジェクトが定義されていない
      • 適切な旅行を見つける責任を持ったオブジェクト
      • 適切な旅行を見つける振る舞いをするオブジェクト

4.4 一番良い面(インターフェース)を表に出すコードを書く

  • 明示的なインターフェースを作る。パブリックインターフェースに含まれるメソッドは、次のようなものであるべき
    • 明示的にパブリックインターフェースだと特定できる
    • 「どのように」よりも「何を」になっている
    • 名前は、考えられる限り、変わり得ないものである
    • オプション引数として、ハッシュをとる

4.5 デメテルの法則

  • デメテルの法則とは
    • オブジェクトを疎結合にするためのコーディング規則の集まり
    • 列車事故を除去する方法で、委譲を使う方法がある
      • 列車事故は、異なる型のオブジェクトを複数繋げて、メッセージを送ること
      • 委譲とは、他のオブジェクトにメッセージを渡すこと
      • 委譲を実現する方法はいろいろあって、forwardable.rbとか使ったりする
      • これらは、それぞれオブジェクトが自身に送られたメッセージを自動的に捉え、どこか他のところへ送り直すのを簡単にするために存在してる
    • 参考
    • 委譲サンプルコード
  • デメテルの法則は、委譲をたくさん使っていこう、という話ではなくて、長いメソッドチェーンで繋がってて、欲しい振る舞いがかなり遠くにいるのがよくないって話
  • なので、もっと近くでメッセージをやりとりできるよう、新しくオブジェクトを作る
  • デメテルの法則違反(メソッドチェーンで連なった列車事故)は、パブリックインターフェースが足りてなくて、発生するので新しくパブリックインターフェすを作る際のヒントにできる

全体的な感想

  • むずいけど、少しづつわかってきた感もあるので、引き続き頑張る
  • この考え方を元に、実際にコードをかけることが重要なので、取り入れていけるよう頑張る

オブジェクト指向設計実践ガイド3章を読んだ感想

3章は依存関係を管理する話。サンプルコードを使って、オブジェクト指向設計の依存関係について説明されてて、わかりやすかった。最近は Laravelを勉強してることが多くて、その中で依存性の注入(DI)の概念とかがよく出てくるんだけど、いまいちよくわからんなーとか思ってたところを、この章の説明を読んで、理解が深まった気がする。一通り読んだ印象としては、コードの中で依存してる箇所を、どんどん取り除いていって、依存部分を別のところ定義し直して、極力依存してる箇所を減らそうよ、みたいな記述が多くあったのが特に印象に残った。それは、そうすることで再利用可能なコードになるし、複雑さが減る。依存してる部分に変更があった場合に、変更箇所が少なくて済む、などメリットがある。なんども出てきてるけど、アプリケーションを書く上で重要なのは、柔軟性のある、変更に強いコードを書くことなんだなあと思いました。以下、この章で気になったところや重要そうだと思ったところ抜粋

  • 3.1 依存関係を理解する
  • 3.2 疎結合なコードを書く
  • 3.3 依存方向の管理

3.1 依存関係を理解する

  • 依存関係があるコード例(悪い例)
  • 上記の依存関係があるコード例では、Wheelの変更によって、Gearへの変更が強制される部分が少なくても4つある(どこでしょうか?)
  • 正解は次の4つ。一定の依存関係がクラス間に築かれるのはしょうがないけど、この4つは不要な依存関係で、コードをより複雑化してしまう。
    • 他のクラスの名前
      • gear_inches メソッドの Wheel.new(rim,tire) の部分で、Gearクラスが、Wheelクラスの存在を知ってる
    • self以外のどこかに送るメッセージの名前
      • gear_inches メソッドの Wheel.new(rim,tire).diameter の部分で、Gearクラスは、Wheel クラスがdiameterというメッセージに応答することを知ってる
    • メッセージが要求する引数
      • メッセージを送る際に(gear_inches メソッドの Wheel.new(rim,tire).diameter の部分)で、Wheel.newの引数にrimとtireが必要なことを知ってる
    • そららの引数の順番
      • Wheel.new(rim,tire) の引数の順番が知られてる
  • 設計課題は「依存関係を管理して、それぞれのクラスが持つ依存を最低限にすること」です。
  • クラスが知るべきことは、自身の責任を果たすために必要十分でよい。知りすぎるのは良くない
  • 以下の図は、悪いコード例を表した図になっていて、GearがWheelを知れば知るほど、この2つのクラスの結合はより密になる。そして、あたかも1つのエンティティのように振る舞うことになってしまう
    • Wheelに変更を加えると、Gearも変更しなきゃいけなかったり
    • Gearを再利用したいだけなのに、Wheelもついて来ちゃう

  • 依存関係に関連した一般的な問題というものがある。大別して次の2つ
    • 破壊的な類の依存関係が生じる場合
      • 『「何かを知るオブジェクト」を知るオブジェクト』を知るオブジェクトがある場合
      • デメテルの法則違反
      • いくつものメソッドチェーンを繋いで、遠くのオブジェクトの振る舞い実行すること
      • 途中のオブジェクトに変更が入る可能性を考慮すべき
    • コードに対するテストの依存関係
      • テストを書き始めたプログラマーはコードと過度に結合したテストを書きがち
        • コードをリファクタリングするたびにテストが壊れる
          • テストの設計は、9章で検討する

3.2 疎結合なコードを書く

  • 依存オブジェクトの注入
    • 他のクラスに、クラス名そのもので参照してるところは、結合を生み出す主要な場所になる
      • サンプルコードだと、gear_inchesメソッドで、Wheelクラスを参照してる
      • この書き方だと、gear_inchesメソッドは、Wheelインスタンスのギアインチしか計算しないことになって、例えばの他にギアインチを測りたくても測れない(Wheelへの参照がハードコードされてるので)
      • 本来であれば、gear_inchesメソッドは、diameterに応答できるオブジェクト(直径を測ってるくれるメソッドをもつオブジェクト)があれば良い(Wheel.new(rim,tire) じゃなくても良い)
      • Gearクラスが、他のオブジェクトについて、知りすぎてると、再利用しづらくなる
      • そのため、このWheelと結合してる部分をやめて、初期化(initialize)の時に、diameterに応答できるオブジェクトを要求するように変更したら良さそう
    • 修正したサンプルコード
      • この変更をすることによってGearクラスのgear_inchesメソッドは、wheelがWheelクラスのインスタンスであるかを知ってる必要がなくて、単純にdiameterに応答するオブジェクトであるかを気にするだけでよくなった
      • 変更前は、Wheel.new(rim,tire).diameter となっていて、Wheelクラスのインスタンスであることを気にしないといけなかった
      • このような変更後のコードのことを、依存オブジェクトの注入という
  • すでにアプリケーションとして動いているコードは、いろいろと制約すでに存在している可能性があって、依存オブジェクトを注入するように変更できない場合もある。そういう場合は、クラス内で隔離するようにする。
    • gear_inchesメソッド内でWheelインスタンスが作成されてたのを、initializeメソッドで作るようにする。こうすると、gear_inchesメソッドは綺麗になって、依存はinitializeメソッドで公開される
    • wheelインスタンスを作るメソッドを別で用意する
  • メッセージに着目して考える場合
    • メッセージとは
      • とあるオブジェクト(レシーバ)のメソッドを使うことを、メッセージを送るって表現することがある
      • レシーバとメッセージという呼び方は、Smalltalkというオブジェクト指向言語でよく使われる呼び方
    • 外部メッセージを隔離して、selfにメッセージを送るようにする
      • 外部にメッセージを送ってるところを隔離する。隔離するっていうのは、専用のメソッド内にカプセル化するって意味
  • 引数の順番への依存を取り除く
    • initializeメソッドなど、初期化する時に指定されてる引数があって、順番も決まってて、という依存があるが、これらの「固定された順番の引数」への依存を簡単に回避する方法がある
      • initializeメソッドに引数をただ一つargsのみを取るようにする
      • 初期化の際の引数にargs(ハッシュ)が使われてるのって、引数の固定された順番への依存を解消するためだったのか。(railsとかで定義元ジャンプしていくと、引数にargsを指定されてるメソッドとかがたくさんあってよくわからんみたいな印象があったので、なるほどと思いました)
  • 引数の順番への依存を取り除いたサンプルコード
  • 明示的にデフォルト値を設定する
    • || メソッド(or演算子と同様の動作をする)を使う
      • || メソッドを使うと、引数に指定するargs(ハッシュ)の中で、true/falseを値にもつキーのデフォルト値がtrueの場合、明示的に値をfalseにすることができない問題がある
      • そのため、このような場合は、fetch メソッドを使うほうが良くて、fetch メソッドを使うと、フェッチしようとしてるキーの先がハッシュであることが期待されて、ハッシュのキーがない時だけ、デフォルト値を採用される。なので、|| メソッドと違って、意図的に値をfalseにできる
  • 複数のパラメーターを用いた初期化を隔離する
    • 変更が必要なメソッドを「自身で」修正できない場合に、依存関係を取り除くようにする方法についての話
    • 例えば、引数の順番の依存関係などが変更できない状況(外部のフレームワークの一部で、簡単に変更できない状況)の場合に、この依存関係を隔離する方法
    • その方法とは、外部のインターフェースを包み隠すメソッドを作る
    • 以下のサンプルコードでいうとGearWrapperモジュールの特異メソッドgearを定義して、そこでは、argsを引数に取れるようにする。そうすることで、引数の依存関係を隔離できる
    • 結果、GearWrapper.gear(:chainring => 52, :cog => 11, :wheel => Wheel.new(26, 1.5)).gear_inches の部分で、オプションハッシュを使って、Gearの新しいインスタンスを作れるようになった
  • 複数のパラメーターを用いた初期化を隔離するサンプルコード

3.3 依存方向の管理

  • 依存関係には常に方向がある
  • これまでの例は、GearがWheelやdiameterに依存していた
  • 逆に、WheelをGearやratioに依存させることもできる
  • 依存関係を逆転させたサンプルコード
  • 依存方向の選択
    • 依存の方向を考える時「自身より変更されないものに依存する」ようにした方が良い。具体的には以下の3つを考える
      • あるクラスは、他のクラスより要件が変わりやすい
        • 例えば、rubyの基本的なクラス(String,Arrayなど)が大きく変わることは少ない
      • 具象クラスは、抽象クラスよりも変わる可能性が高い
        • 依存オブジェクトの注入のところで説明した話
          • クラス内で、newしたインスタンスに依存するより、初期化時にインスタンスを必要とするように修正すると、より抽象的なものに依存するようになった
        • 静的型付言語は、インターフェースを宣言して、注入するインスタンスがそのインターフェースの一種だと、注入先のインスタンスに教える
        • rubyの場合、ダックタイプに依存していて、これは実は、さりげなくインターフェースを定義してる
        • インターフェースは、依存関係をより抽象的にするためのもの。
          • インターフェースとは、あるカテゴリーのものは、〇〇をもつ、という概念が抽象化されたもの
            • 今回のサンプルコードでいうと、wheelインスタンスは、diameterをもつ
      • 多くのところから依存されたクラスを変更すると、広範囲に影響が及ぶ

全体的な感想

  • なかなか理解が追いつかなくて、スラスラ読めてる感じじゃないけど、読み応えがあってすごく勉強になる章だった。
  • 概念的には理解できた感じあるけど、実践で具体的にコード書くとなると、まだ難しそう。ここで書いてることを意識しながら、実践でもこの考え方を使えるようにしていきたい
  • ここは依存性を注入してコード書こうとか、ここは依存性を逆転させた方が良さそうとかを考えて、コードを書ける日が来るのだろうかと思ってしまったりするけど、少しづつ成長していけるよう頑張る

オブジェクト指向設計実践ガイド1章と2章読んだ感想

オブジェクト指向設計実践ガイドという本を読み始めた。オブジェクト指向設計を意識して責務を分けコードを書くことができるとか、拡張性を考えてコードを書くことができるとか、このあたりの知識が弱いと思っていて、この本を読んでそのあたりの知識をつけていけたらなと思う。

1章 オブジェクト指向設計

一通り読んだ感想として、「変更に強いことが大事」というのが、言い回しを変えて、繰り返し書かれているのが印象的だった。アプリケーションが変更されるのは、重力みたいなものなので、その変更コストをどう抑えるのかみたいなところを考えると、変更に強いことがすごく重要になるという話。以下、本で気になったところ箇所抜粋

  • 設計が解決する問題
    • 要件の変更はプログラミングにおける摩擦力と重力
    • 変更が容易なアプリケーションを書けるようにする
    • 「あとにでも」設計できるようにする。変更コストの削減
  • 原則
    • SOLIDというオブジェクト指向設計でもっともよく知られる5つの原則がある
      • 単一責任
      • オープン・クローズド
      • リスコフの置換
      • インターフェース分離
      • 依存性逆転
  • 設計パターン
    • いわゆるGang of Fourと呼ばれるもの
    • 「設計プロダクトの柔軟性、モジュール性、再利用性、理解のしやすさ」をより高めてくれる

2章 単一責任のクラスを設計する

一通り読んだ感想として、「単一責任にするのは、再利用しやすいため」ということがすごく伝わった。また、変更を歓迎するコードとして、良く知られてる方法の一つに、「データではなく、振る舞いに依存するようなコードを書く」というのもすごく勉強になった。インスタンス変数や構造が複雑な配列を直接参照してて、そのデータに変更が発生した場合、その変更に合わせて参照してたところを全部修正するのは、かなり手間が発生するので、直接データを見るんじゃなくて、それを包むメソッドを作って(メッセージを受け取ると、データにアクセスする振る舞いをするメソッドを作って)、アクセスした方が良い。あと、題材になってた、自転車のギアとギアインチの関係を理解するのが、ちょっと頭痛かった(ただの算数だけど、なんか頭に入ってこなかった。。。)以下、本で気になったところ箇所抜粋

f:id:shimabukuromeg:20190102164250j:plain

  • なぜ単一責任なのか
    • 再利用が容易になる
      • 2つ以上の責任を持つクラスは再利用が簡単ではない
  • クラスが単一の責任かどうか見極める方法
    • クラスのもつメソッドを質問に言い換えた時、意味をなす質問になっているかどうか
  • 変更を歓迎するコードを書く。データではなく、振る舞いに依存する(データに依存するのはよくない)
    • 良くない理由は、データが変わった時に、それを参照してる箇所を全て修正しなきゃいけないから
    • データではなく、振る舞いに依存するよう、特定のデータにアクセスするようなメソッドを作って、そのメソッド(振る舞い)を使ってアクセスする
    • 例えば、インスタンス変数に結びついているようなコードはよくなくて、インスタンス変数を隠蔽するよう、インスタンス変数へのアクセスできるメソッドで包む
    • 配列など、複雑なデータ構造への依存はさらによくない(データに依存するのはよくない)
      • 配列のデータ構造が変わると、それを参照してるコードを全て直さなきゃいけなくなる。なので、データ構造の隠蔽するようにする
        • 複雑な構造への直接の参照は混乱を招く問題があるので、意味と構造を分けれる
          • 意味と構造を分けるとは?
            • 意味(タイヤの直径を知りたい)と構造(配列を直接参照する複雑な構造)が一つのメソッドの中にあったところを、意味と構造を分け、diametersメソッドと構造を作るwheelfyメソッドを用意したってこと?
          • Structクラスを使って、構造を包み隠す。Structクラスは、明示的にクラスを書くことなく、いくつもの属性を1箇所に束ねるための便利な方法
  • Structサンプルコード

全体的な感想

いろいろ勉強になるし面白い。ただ、なかなか読むのが遅い。あと実践でもちゃんと使えるようになることを意識して読み進めたい。

2019年抱負

今年の年末にちゃんと答え合わせできるよう、簡単にだけど今年の目標を書いた。この一年は、もっとWebのスキルをつける年にしたい。2800hぐらい時間積みたいと思ってる。

  • 1700時間ぐらいは、お仕事の時間でいろいろ学べるように頑張る
  • 1100時間は、足りないスキル、気になるスキルキャッチアップする時間にする

今年1年でWebアプリケーションエンジニアとちゃんと名乗れるようになるのが目標

  • まだまだ初心者感が抜けてないと思ってるので、今年1年で脱初心者、中級者になれるようにする
  • rails,laravelのフレームワークをちゃんと使いこなせるようになりたい
  • ちゃんとっていうとあれだけど、アプリケーションを作る際に考慮すべきことを知っていて、それについて議論できて、決まったことをコードで表現できるようになる
  • そのための具体的なTODOは、都度考えながら、仕事で学んだり、本読んで学んだり、軌道修正しながら進めて行きたい

とりあえず今年インプットしておきたいと思ってるリスト

基礎力足りないと思ってて、ちゃんと読み切りたいと思ってる本のインプットと、手を動かして作りたいと思ってることリスト。仕事で使うスキルを中心にいい感じでキャッチアップできたらいいなあと思ってる。本読む、手を動かす、その結果をブログに書く、みたいなサイクルをたくさんしたいと思う。時間割は適当(本については1章3hとかの計算)

  • rubyオブジェクト指向本(30h)
  • laravel本(50h)
  • laravelオープンソースプロジェクトを読む(40h)
  • laravelで自作アプリ(50h)
  • 現場ruby本(30h)
  • javascript本格入門本(50h)
  • churakariリリースする(150h)
  • isucon周りの勉強(200h)
  • 実践コンテナ本読む(30h)
  • hello,world gem

資格

体系的に理解していけるように資格も取れたらいいなあと思ってる

kakakakakku.hatenablog.com

kiryuanzu.hatenablog.com

アウトプット

  • ブログ

    • とりあえず、クオリティ低くても、たくさん書くことと、誰かが読んでることを意識して丁寧に書くようにする(脱メモ)
  • LT

    • チャンスを見つけて挑戦したい(人前で話すの苦手なので、頑張っていきたい)

まとめ

今年も頑張っていきたい。よろしくお願いします!

2018年振り返り

今年の振り返りを簡単に書いた

今年やったこと

  • 時系列に並べてみた
    • 1月 プログラミングスクール
      • html css js php web基礎全般(100h)
    • 2月プログラミングスクール
      • html css js php web基礎全般(100h)
    • 3月 ruby/rails(100h)
      • progate ruby
      • ドットインストール(sinatra,active record)
    • 4月 ruby/rails(100h)
    • 5月 ruby/rails(100h)
      • rails tutorial 1周目
    • 6月 ruby/rails(100h)
      • rails tutorial 2周目
    • 7月 ruby/rails
      • churacari
        • さぼさん、仲嶺さんと一緒にやり始めた趣味プロジェクト
        • 来年はchurakariリリースしたい
    • 8月 ruby/rails
      • churacari
    • 9月 laravel 実務経験
    • 10月 laravel
      • laravelお勉強で個人アプリ作る
    • 11月 laravel 実務経験
    • 12月 laravel 実務経験

今年振り返り

  • railsチュートリアル2週目終わったぐらいから時間計測しなくなったけど多分1000hは学習時間積めた
  • railsチュートリアルと実践的な開発の間にはかなり高い壁があると感じた
  • 今年やったことを時系列並べてみると、頑張ったなあという気持ちと、もっとできたんじゃないかなあという気持ちがあって、もっとできてんじゃないかと思うところとしては、あまりアウトプットできてないところ

    • もっとアウトプットする
      • アウトプットがあるほどインプットできる
      • 一応ブログ書いてるけど、メモ感が強いので、もう少し丁寧に、誰かの参考になるように書く
      • LTとかもっとできるようにする
      • 作ってみた、やってみたの量をもっと増やす
      • コード書いてる時間が単純に少ない問題もある
  • 10月以降はプロジェクトに参加させてもらってめちゃめちゃ勉強になった

    • レビューしてもらえること
      • 個人で学習してるだけだと、それが正しいか判断できないからレビューしてもらえるってことは一番勉強なる
    • チームメンバー間のやりとりをみれる
      • コミュニケーションから学ぶこと多くて、いろいろ教えていただいたり、チームメンバー間のやりとりの内容の意味わからない場合、それが自分に足りてないことで、自分がわかってないことが浮き彫りになるので、良い
    • 実際に動いてるコードを読める
      • 個人学習とか趣味レベルだと仕事で使われてるアプリがどんな感じなのかあまりイメージできなかったけど、実際のコードを見れるってのは勉強になった。最初見たとき、めちゃめちゃクラスがあって、これが実務経験か!!!って気持ちになった
    • 初歩的なところでこけてる
      • めちゃめちゃ初歩的なところでこけるとか度々あって、実務経験積みつつも、基礎力を並行して向上させて行かなきゃいけんなあと感じた
    • 設計むずい
      • コード書き始める前に、まずどういうものを作るのかっていうissueを作ったりするのが、難しいと感じた。どういうポイントを考慮しないといけないのかみたいなところがまだつかめていないと思う
  • もっと言語化
    • ここまで振り返っていて、勉強になったとか、うまくできてない、とかの振り返りがあるけど、それをもう少しちゃんと言語化して、振り返れるようになった方がいいと思ったので、曖昧な言葉で流されないように気をつける
      • 何が勉強になったのか
      • 何ができていないのか
      • そのために何をするのか

まとめ

  • 今年は、アプリケーションを書けるエンジニアになるべくスタートラインに立てるよう、頑張った一年だった。ただ、まだまだ力不足なので来年は今年の三倍頑張りたいと思う。もっとコード書く。
  • あと、プログラミングの勉強を本格的に初めて、同じような目標に向かって頑張ってる仲間もたくさんできたのがよかった。切磋琢磨していけるよう頑張る