読者です 読者をやめる 読者になる 読者になる

「オブジェクト指向設計実践ガイド」 第9章 費用対効果の高いテストを設計する

Programming/一般 読書ノート

第9章 費用対効果の高いテストを設計する

変更可能なコードを書くのに必要な3つのスキル

  1. オブジェクト指向設計の理解
  2. リファクタリングのスキル
  3. テスト設計のスキル

リファクタリングとはコードの機能を変更せずに内部構造を変えること

テストの意図

テストを行う目的は、コストの削減である。テストに伴って、次のような利益が得られる。

  • バグ発見 早期にバグを発見できる
  • テストが仕様書代わりとなる 紙の仕様書をメンテナンスし続ける必要がない
  • 設計の決定を遅らせる テストがあることにより安心してリファクタリングできる
  • 抽象を支える 全体の振る舞いを記述できる
  • 設計の欠陥の発見 テストが書きづらいコードはコンテキストを要求しすぎている

より少ないテスト

テストはなるべく少ない方が良い。テストの数を安全に減らすためには、テストを何のために書くのか考える必要がある。

まず、オブジェクト指向におけるテストとは、オブジェクトが他のオブジェクトからメッセージを受け取った際に「期待される動作」をするかどうかのテストである。ということは、パブリックインターフェースに関するテストのみ書くべきである。(プライベートなメソッドに対するテストは不要!)

テストすべきなのは

  • 受信メッセージ(オブジェクトがメッセージを受取ったら期待するメッセージを返すこと)
  • 送信コマンドメッセージ(副作用を持つメッセージは送られた回数、使われた引数を検証する必要がある)

のみである。

受信メッセージのテスト

  • 使われていないインターフェースは削除する
  • パブリックインターフェースの仕様を保証する
  • 依存するオブジェクトはテストダブル・スタブを活用し、テスト対象ではないオブジェクトの影響をなくす (これを実現するには依存性の注入ができていないと!)

プライベートメソッドのテスト

送信メッセージのテスト

  • 副作用のある送信メッセージが正しく送られるか、モックに対してメッセージを送らせてテストする

ダックタイプのテスト

  • 暗黙的に存在するタイプはテスト内で明示する

継承のテスト

  • リスコフの置換原則が守られているかテストする
  • 抽象クラスのテストにはテストスタブ用のサブクラスを活用する

「オブジェクト指向設計実践ガイド」 第8章 コンポジションでオブジェクトを組み合わせる

Programming/一般 読書ノート

第8章 コンポジションでオブジェクトを組み合わせる

単一の責任を持ったオブジェクトを複雑な全体へと組み合わせることによりアプリケーションは作られる。 より大きい部分が部品を持つという関係でオブジェクトは関連付けられる。そして、それぞれはインターフェースを介してメッセージを送り合う。

コンポジション

継承は IS-A という関係を作るが、コンポジションHAS-A という関係を作る。「マウンテンバイクは自転車である」が IS-A 「自転車はタイヤを持つ」が HAS-A

自転車オブジェクトが持っているタイヤオブジェクトやチェーンオブジェクト、ハンドルオブジェクトを取り替えられる設計になっていれば、自転車クラスの変更無しでマウンテンバイクやロードバイクを表現可能になる。これはロードバイクやマウンテンバイクといった特化された自転車のクラスを自転車クラスを継承することによって作ることと対立する手法である。

ロードバイクとマウンテンバイクを同じ「自転車」としてみて、共有する振る舞いをまとめることができるのは継承もコンポジションも同じ。だが、それぞれ使い所が異なる。

コンポジションと継承、それぞれの特徴

継承

「オブジェクトを階層構造に構成する必要があるが、メッセージの委譲は自動的に行われる」

コンポジション

「明示的にメッセージ委譲を定義する必要があるが、オブジェクトを複雑な構造の下に置かなくてすむ」

というそれぞれ反対の性質を持っている。

一般的には、はっきりとした継承を使うべき理由がないときはコンポジションを使うべきである。なぜなら、コンポジションのほうが依存関係を小さくすることができるからである。

継承のメリットとデメリット

うまく構築された継承の階層構造は「合理性」「再利用性」「模範性」に優れている。

  • 合理的: 階層の上の方を変更することで、小さな変更を継承ツリーの下の方まで波及されられる。必要な大変更をわずかなコードの変更で実現できる。
  • 再利用性: 階層構造は拡張には開いており(サブクラスの追加が容易で既存コードの変更がいらない)修正には閉じている(変更の影響範囲が明確)
  • 模範性: 階層構造があると、これから追加するコードが有るときどこに追加するかわかりやすい

しかしこれらのメリットはあくまで上手に階層構造が設計できた場合の話である。下手な設計は、合理性・再利用性・模範性という性質を逆転させてしまう。

  • 変更コスト(⇔合理性): わずかな変更がすべてを破壊するリスク
  • 振る舞いの変更不可能(⇔再利用性): 既存の階層にうまくあてはまらないクラスの置き場所がない
  • 混沌(⇔模範性): どこに何があるのかわからないコード

継承は深い依存関係の集まりを伴っていることに注意を払う必要がある。RubyではどのクラスもObjectクラスから継承継承とつづくツリーの下にある。

コンポジションのメリットとデメリット

コンポジションを使うことのメリットには次のようなものがある。

  • 単一責任で、継承しているものが少ない「見通しの良い」オブジェクトがつくりやすい
  • インターフェースが定義されており、既存の部品の亜種を新しく作るのが容易

コンポジションによって成るアプリケーションの部品(オブジェクト)は小さく、独立し、インターフェースを持つ。なので交換可能なコンポーネント化しやすい。

一方で、部品が小さくなるということは全体として大量の部品が現れるということになる。ひとつひとつが見通しよくても、それがたくさんあった場合には把握が難しくなる。

どちらを選ぶべきか

  • 継承とは特殊化です
  • 継承が適しているのは、過去のコードの大部分を使いつつ、新たなコードの追加が比較的少量の時に、既存のクラスに機能を追加する場合です。
  • 振る舞いが、それを構成するパーツの総和を上回るのなら、コンポジションを使いましょう

「オブジェクト指向設計実践ガイド」 第7章 モジュールでロールの振る舞いを共有する

Programming/一般 読書ノート

第7章 モジュールでロールの振る舞いを共有する

継承はクラスの継承のみにとどまらない。Rubyのモジュールを使い振る舞いを共有させることが可能である。

ロール

クラスによる振る舞いの共有が縦の関係であるとすればロールによる振る舞いの共有は横の関係。

オブジェクト指向なアプリケーションにおける一般的な規則「オブジェクトは自身を管理すべき」。オブジェクトAについての情報を得たいとき、オブジェクトBに関する知識が要求されるのであればそこには不穏な匂いを感じ取るべきである。オブジェクトAの情報を得るには、オブジェクトAに関する情報のみあれば十分なはずである。

Rubyのメソッド探索については

Effective Ruby

Effective Ruby

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

などが詳しいので省略

モジュールの使い方

抽象スーパークラスの中に、あるサブクラスは使うが他のサブクラスは使わないコードがあってはいけない。スーパークラスはすべてのサブクラスの汎化でなければならないが、そのようなコードはサブクラスで汎用的に使われるものではなく特化されたものである可能性が高い。

3/20~3/26のアンテナ

今週読んでいた本

「レイヤー化する世界」世界みんなコスプレイヤーになっちゃうっていう説(嘘)

続きを読む

The Inevitable(<インターネット>の次に来るもの)

読書ノート

書誌情報

タイトル: 『<インターネット>の次に来るもの 未来を決める12の法則』 著者: ケヴィン・ケリー

〈インターネット〉の次に来るもの 未来を決める12の法則

〈インターネット〉の次に来るもの 未来を決める12の法則

概要

今後のテクノロジーの進化が備えていると筆者が考える12の性質について

12の性質とは

  1. Becoming
  2. Cognifying
  3. Flowing
  4. Screening
  5. Accessing
  6. Sharing
  7. Filtering
  8. Remixing
  9. Interacting
  10. Tracking
  11. Questioning
  12. Beginning

各章

Becoming

終わることのないアップデート(技術進化)は、人間の心の貧しさとあわせて語られることもある。しかし、満足せずに「もっと」を求める不満足こそが人間の創造性や成長をもたらした。 自分なりにいうと、人々のあてのない欲望を特定の技術の形態が整流すること、といえるだろうか。「早く移動したい」という漠然とした欲望が自動車という技術と出会うことで、「よりパワーの有るエンジンがほしい」「より空力性能のよい形にしたい」といった具体的な欲望になる。

Cognifying

AIはあらゆるところに行き渡ると同時に、隠れた存在になる。つねにスイッチを入れれば同じ動きをするのではなく、ほんの少し「外の世界」が見えるようになるだけで、あらゆるものが変化する。これらの「知能」は特定のタスクをうまくやるためのもので、爺意識を持つようなものではない。 かつては、「水や空気の温度を一定に保つ」ことができるのは知性を持った人間だけだと考えられていた。しかし、サーモスタットによってそれは可能になった。その後、「水や空気の温度を一定に保つ」ことは知的なことだとは考えられなくなった。

「人間の知性」にしかできないと考えられていたものが「人工知能」によって奪われるにつれ、奪われたものは「人間の知性」とは無関係のものだと考えられるようになった。多くのものを奪われるにつれ、人間の知性の本質に近づいていく。 人間には、ロボットにさせる仕事を与えるという仕事だけは残る。

Flowing

インターネットはコピーする。情報はオリジナルが移動するのではなく、コピーが新しい場所に作られる。 我々はオリジナルを所有している必要はなく、いつでも必要なときにコピーをもってくればよい。

無限にコピーできるものには希少性はなく、お金を払う価値はない。では我々は何に価値を感じてお金を払うのだろうか?

- 即時性
- パーソナライズ
- 解釈(というよりサポート?)
- 信頼性
- アクセス可能性
- 実体化
- 支援者(クラウドファンディング)
- 発見可能性(キュレーション)

銀行(というか金融業)の重要性と通じるものが多い。銀行の場合は信頼性(お金を盗まれる心配なく代わりに持っておいてもらえる)、アクセス可能性(自分の預金をどうするか指図できる)、実体化(必要なときは現金化できる)といったものがわかりやすい。

特に日本では物自体を生産することばかりが偉いことだと思われがちだと大学の指導教官も言っていたが、その風潮はあると思う。反対に金融業なんか半分詐欺師みたいな扱いを受けている。でも、ものの配分を調整したり利用可能製を高めたりっていう仕事のほうが重要だと思う。

Screening

この章はダメだと思う。前の章がけっこういいなと思う反面。いたるところに画面があってアクセスできる、、80sなSFの世界である。 ただ、本題からそれたところでいいことが書いてある。

それにコードは、法律以上にとはいかないにせよ、法律と同じように行動を規定することもできる。オンラインやスクリーン上での人の行動を変えたかったら、単にその場を支配するアルゴリズムを変えるだけで、集団行動を監視したり、人々を好ましい方向に誘導したりすることができるのだ。

「これをしちゃダメ」と相手を抑圧するのではなく、自分では他人に操られていると知らずに望む行動を取らされている。そーいうテクノロジーを僕も作りたい。

Accessing

「所有権の購入から、アクセス権の購入へ」。音楽を聞くためには、かつてはレコードを買わなくてはいけなかった。それがCDレンタル(&MDやカセットへのダビング)、MP3ダウンロード、そして定額制のストリーミングへと変化。もはやハードとしてのメディアはおろか、デジタルファイルとしてすら「所有」はしない時代に。

ものを買わずに、共有するというスタイルはこれからも加速していくだろう。うちもカーシェアはよく利用しているし。この本の大きなテーマは「名詞から動詞へ」という流れだ。我々は「移動したい」のであって、そのための答えとして「車」は今のところよい答えである。でもそれは必然ではなく、他の手段でもよいのである。

でも人の欲望には「動詞」に還元できない「名詞」へ向かうものがあるのも間違いない。「寒さをやわらげたい」だけならわざわざヴェルサーチのマフラーを買う必要はないのである。テクノロジー信仰の強い人々はあまりこの感覚をもっていないのではないか、というニオイがする。

Sharing

人々の無償のはたらきが価値を生み出している。その共同体に所属することによりほかの人のはたらきによる恩恵を得ることができるし、自分もそこに参加しようという意欲が生まれる。ボトムアップ社会主義。 そういうの悪くないなとは思うんだけど、生産行為それ自体が分散して行えるようになった今こそ舵取りをするリーダーの重要性は高まっていると思う。

というより、そうでなければ「良いもの」は生まれないだろう。コミュニティの生み出すものは「足し算」は特異だと思うけど、「引き算」は難しい。みんな自分のほしい要素を盛り込みたいし、人のほしい要素を否定したくない(自分もそうされる可能性があるから)でも良い物って足し算よりも引き算のほうが重要なわけで。

Filtering

選択肢が増えすぎた今、何を選べばいいのか教えてくれるものが必要。

フィルターは人間から見るとコンテンツに注目している。逆にコンテンツの方から見ると、人間のアテンションに注目している。コンテンツが潤沢になれば、人のアテンションが希少になる。

Remixing

動画や静止画、音声といったコンテンツは世の中に溢れている。これらをマッシュアップし、新しいものを作り上げることができる。これは文章を書くことと似ている。文章を書くことは、辞書の中にある言葉を組み合わせることで文章を作り上げることである。

マルチメディアと言葉が違うのは、「インデックス」の作りやすさ。ことばが規則正しく並んだ辞書のように、動画や静止画が整理することは今の段階では難しい。それらが可能になれば、よりマッシュアップは加速するだろう

Interacting

これまでのコンピューターは、人間に見られたり聞かれたりするためにあった。人間がコンピューターを操作するには、キーボードやマウスと言った「コンピューターを操作するための装置」を使って働きかける必要があった。しかし、これからはコンピューターのほうが我々を見て耳を傾ける時代になってきている。

という主張であるが、これに僕は否定的。せっかく指一本をちょちょいと動かすだけでいろいろなことができるようになったのに、どうして今更全身を激しく動かさないといけないのか。SF映画やゲームではやはり画面に「映える」けれども、実際エクセルで書類を作るのに腕を激しく動かして踊らないといけないといわれたら、嫌じゃないですか?

Tracking

センサーと、センサーが受ける情報を処理するコンピューターと、処理したデータを保存するストレージ、全てが安くなった。今までは捨てられるどころか収集すらされていなかったデータの中から、今まで誰も気づかなかった知見が得られる。(ビッグデータ)

それらを直接身体にフィードバックしていれば、いずれ人間は新しい感覚を手に入れるだろう。

Questioning

新しいことがわかるとき、それ以上に新たにわからないものが現れる。良い質問とは、単に答えを得るだけのものではなく、創造性の源でもある。

Beginning

今について、我々は変化がおわってしまっている瞬間だと考えがちである。しかし、つねに新しいプロセスは始まっている。

「オブジェクト指向設計実践ガイド」 第6章 継承によって振る舞いを獲得する

Programming/一般 読書ノート

第6章 継承によって振る舞いを獲得する

よいアプリケーションの設計とは、再利用可能なコードによって構成されていること。このためにこれまでは

  • コンテキストを最小限にする
  • 明確なインターフェースを定義する -依存オブジェクトを外部から注入する

といったテクニックが紹介されてきた。これに加えて、Rubyに限らず多くのオブジェクト指向言語が持つ継承を利用することを考える。

継承とは

継承とは、「メッセージの自動委譲」である。サブクラスが理解できないメッセージをスーパークラスに転送すること。

継承を使うべきポイント

あるオブジェクトの属性を確認して、その属性に応じて送るメッセージを変えるようなコードがあったら、それは継承を使うべき。

サブクラスがスーパークラスのより特殊な形態になる場合、それは継承関係を使うにふさわしい。一方、単に共有したいコードがたくさんあるだけのときは継承を使うべきではない。(IS-Aの関係)

継承が効果を発揮するには、次の2つが成り立っている必要がある。

  1. モデル化したオブジェクトが汎化-特化の関係にあること
  2. 正しいコーディングテクニックを使っていること

すでにあるコードに階層構造を導入するときには、いまあるコードは「具象」としてとりあつかいそこから「抽象」を切り出していくほうがよいとされている。抽象的なものがサブクラスに残っていることより、具象的なものがスーパークラスに残っていることのほうが危険だからである。

コーディングテクニック

テンプレートメソッドパターンを使うことで、サブクラスを専門的な仕事だけに特化させることができる。

サブクラスが明示的にsuperを呼び出すということは、サブクラスがスーパークラスの実装を知っているということである。この依存をなくすために、スーパークラスのメソッド内でフックメッセージを使うことができる。

「オブジェクト指向設計実践ガイド」 第5章 ダックタイピングでコストを削減する

第5章 ダックタイピングでコストを削減する

ダックタイピングとは、暗黙的なパブリックインターフェース。オブジェクトの振る舞いは、コードとして存在する明示的なインターフェースではなく、「どのメッセージに応答するか」という暗黙的なインターフェースに依存することになる。

ダックタイピングの理解

あるオブジェクトにメッセージを送ろうとするとき、そのオブジェクトの型を知っている必要はない。重要なのは、そのメッセージに応答可能かどうかだけである。オブジェクトが何で「ある」かではなく何を「する」かに注目する。

ダックタイピングによって、簡潔な記述ときれいな構造を両立することもできれば、理解不能な混沌を作り出すこともできる。コードとしてインターフェースを定義しなくて良いということは、好きにして良いということではない。クラスやインターフェース(Java)と同様に設計し、ドキュメントを整備する必要がある。

すべてがコード上に現れる具象的なコードは理解しやすいが拡張しにくい。(ダックタイピングのように)暗黙的な取り決めがある抽象的なコードは拡張性は高いがひとめで理解しにくい。このトレードオフが常に存在する。

Rubyのようにダックタイピングが使えない場合にポリモーフィズムを実現するには継承、Mix-inなどがある。

ダックタイピングの利用

ダックタイピングを使うべきはどんなときか、いくつかパターンが存在する。

ダックタイピングを使えないか検討すべき項目

  • クラスで分岐するcase文
  • kind_a?, is_a?
  • responds_to?

これらが現れたとき、なにか「共通のもの」がそこには存在するはず。

def start(args)
  args.each do |a|
    case a
    when Bar
      a.run
    when Baz
      a.init
    when Qux
      a.ignite
    end
end

start([bar, baz, qux])

Bar, Baz, Quxはどれも何かを始めるためのメソッドを持っているが、それらすべてばらばらの名前になっているので、呼び出す側がそれぞれのクラスの実装を知っている必要があった。しかしBar, Baz, Quxがすべて start というパブリックメソッドを実装している(暗黙的に"Startable"インターフェースを実装している)とすれば、こうできる。

def start(args)
  args.each(&:start)
end

start([bar, baz, qux])