Software Trends
-
ここ数十年で人気となったトレンドやパターンについて
- 本書で挙げた設計原則との関連について述べる
-
本書で挙げた設計原則に照らして評価する
- ソフトウェアの複雑性に立ち向かうための力を提供してくれるかどうか
Object-oriented programming and inheritance
-
OOPはここ30-40年で最も重要なアイデアの一つ
-
導入された概念
- クラス
- 継承
- プライベートメソッド
- インスタンス変数
-
-
注意深く利用すれば良い設計の助けになる
-
例: privateメソッド/変数
-
知識の隠蔽に有用
- 外からアクセスできないので外部との依存がない
-
-
-
OOPの鍵は継承
-
2つの形がある
- インタフェースの継承
- 実装の継承
-
-
インタフェースの継承
-
親はメソッドのシグネチャのみ定義する
- 実装は定義しない
- サブクラスはそのシグネチャを実装しなければならない
-
サブクラスごとに異なる実装が可能
-
例
- ディスクI/O
- ネットワークソケット
-
-
異なる用途のものに同一のインタフェースを与え、複雑性を下げる
-
知識を流用できる
- ディスクI/Oモジュールの使い方がわかれば、ネットワークソケット越しの通信もわかる
-
モジュールが深くなる
-
実装が異なるほどモジュールは深くなる
- 実装別の詳細を除外した本質をとらえる
- 抽象化の肝
-
-
-
-
実装の継承
- 親がデフォルトの実装も定義する
- サブクラスは親の実装を継承するか、上書きするか選べる
-
同じ実装が複数のサブクラスに重複するのを避ける
- 複雑性の3本の柱のひとつ、「変更の増幅」を避ける
-
実装の継承の利用は注意深く
-
親クラスとサブクラスとの間に依存が生じる
-
知識の漏出
- 親クラスの変数は子クラスからアクセスされうる
- 親クラスの実装者は、子クラスを壊さないように全子クラスを調べる必要があるかもしれない
- 子クラスの実装者は、メソッドをオーバライドする場合、親クラスの実装を知る必要があるかもしれない
- 最悪のケースでは、あるクラスに変更を加えるために、クラスツリー中の全クラス調べないといけないかもしれない
- 実装の継承は広範囲にわたり複雑になりがち
-
- 継承を選ぶ前に、集約と委譲を検討せよ
-
継承以外に可能な選択肢がない場合は、親が管理する状態と子が管理する状態とを分離せよ
-
例: あるインスタンス変数は親のメソッドでのみ管理
-
子から利用するときは…
- 読み取りのみ行う
- 必ず親クラスのメソッドを通す
-
-
-
- オブジェクト指向は、きれいな設計の助けにはなれど、それ自体が良い設計を保証するものではない
Agile development
-
1990代後半に生じたソフトウェア開発アプローチ
- 開発を軽量に、柔軟に、インクリメンタルにするためのアイデア群
- 2001年に、実践者らにより形式化
-
アジャイル開発のアイデアの多くは、設計ではなく開発プロセスに関するもの
- チーム構成
- スケジュール管理
- 単体テストの役割
- 顧客と関わる
- etc.
-
一部は設計にも関連する
-
「開発はインクリメンタルで反復的であるべき」
- プロジェクトの最初から複雑なシステム像を描いて最適な設計を決定することは困難
- インクリメンタルアプローチで、各インクリメントごとに数個の抽象を追加し、リファクタリングするのが最良
-
-
アジャイル開発のリスク: 戦術的プログラミングに陥る
- 「機能」の追加にフォーカスしがち
-
次のように主張する実践者もいる:
- 「最小限の目的特化のメカニズムから始めて、あとで必要に応じて汎用にリファクタリングせよ」
-
戦術的なスタイルが奨励されている
- 著者が支持する「戦略的」な「投資」の姿勢に反する
- 複雑性はすぐに蓄積してしまう
-
機能ではなく、抽象を追加すべき
-
ch6にて提唱の「ある程度汎用」にしたがうとよい
- 【補】実装は目的特化、インタフェースは汎用
-
Unit test
-
開発者がテストを書かなかったのも今は昔
- 書くにしても、独立したQAチームが書いていた
- アジャイルの教義のひとつ: テストと開発とは統合され、プログラマは自分の書いたコードのテストも書くべき
- 今や広く普及している
-
テストは典型的に2種に分けられる
- 単体テスト
- システムテスト
単体テスト | システムテスト | |
---|---|---|
誰が書く | 開発者自身 | QAチーム テストチーム |
スコープ | 狭い。 1メソッドの1セクション等 |
アプリケーション全体 |
環境 | 隔離 | 商用 |
-
単体テストはカバレッジツールと組み合わせて実行されること多し
-
開発者の責任で、適正なカバレッジを保つようにテストを更新する
- 新しくコードを書いたとき
- 既存コードに変更を加えたとき
-
-
特に単体テストは、リファクタリングを容易にすることから、ソフトウェア設計において重要な役割をになう
-
テストがないと、システム大きな構造変更を加えるのは危険
- 不具合の発見が困難
- デプロイして始めて不具合が見つかること多し
- 不具合を探して直すのは高コスト
-
開発者はテストがないとリファクタリングを避け、変更を最小にするようにつとめる
- 複雑性は蓄積の一途
- 設計上の誤りは正されることがない
-
よいテストがあると、自信をもってリファクタリングできる
- 不具合の混入のほとんどを見つけられるため
-
システムの構造的な(大規模な)変更を促す
- 良い設計をもたらす
- システムテストよりも高カバレッジなので、不具合混入を見つけやすい
-
- 著者自身、Tclスクリプトのエンジンを根本的に作り変えた際、素晴らしい単体テストスイートのおかげで、エンバグは1つで済んだ
Test-driven development
- 著者は単体テストの支持者だが、テスト駆動開発のファンではない
-
テスト駆動開発の問題は、「良い設計」ではなく、「特定の機能が動くようにすること」にフォーカスしていること
- 戦術的プログラミングの欠点そのもの
-
テスト駆動開発はインクリメンタルすぎる
- 次の機能がテストをパスするようにコードを改造することの繰り返しに陥りがち
- 設計のための時間を明確に割くことがない
-
先述のとおり、開発の単位は機能ではなく抽象であるべき
- 抽象化の必要性が判明したら、長時間かけてバラバラに作るのではなく、一度に作れ
-
そのほうがクリーンな設計になりやすい
- 各抽象のピースがうまくフィット
-
不具合修正の場合はテストファーストが良い
- その不具合が原因で落ちるテストをまず書く
- 不具合を修正し、テストが通ることを確認する
-
テストを書く前に不具合を直してしまうと、追加されたテストが本当にその不具合を再現するのかわからない
- 不具合が本当に直ったのか分からない
Design patterns
-
ある種の問題を解決するために共通して利用されるパターン
-
例
- iterator
- observer
- デザインパターンという概念はGoFにより一般に普及した
-
-
デザインパターンは設計の選択肢
- 1から設計するより、有名なデザインパターンを適用せよ
-
それで大抵うまくいく
- 共通の問題の綺麗な解決法として、広く受けられてきたものだから
- うまくいく場合は、それより良い別の方法を思いつくのは難しいだろう
-
デザインパターン最大のリスクは、適用しすぎること
- 既存のデザインパターンで解決できる問題ばかりではない
- あつらえのアプローチのほうが綺麗なら、無理して適用しない
-
デザインパターンは、適用すれば自ずとシステムが改善する、というものではない
- フィットして初めてそうなる
-
多く適用するほどよい、というものではない
- 設計における他の多くのアイデアと同じ
Getters and setters
- Java界隈で普及しているデザインパターン
-
厳密には不要なもの
- publicメンバと同じ
-
支持者の言い分
-
値の読み出し、変更時に別の処理を挟める
-
例
- 変更通知
- 値の制約条件を課す
-
- 最初は無くても、将来、呼び出し側に変更を加えることなく追加できる
-
-
内部表現の暴露なのでやめたほうがいい
-
getter/setterは浅いメソッド
- 典型的には1行程度
- 大した機能を提供しないわりにクラスのインタフェースを散らかしてしまう
-
-
デザインパターンを確立することのリスクは、それを「良いものだ」と思った開発者が、なるべく多く適用しようと努めてしまうこと
- Javaのgetter/setterはこの類
Conclusion
-
新しいソフトウェア開発のパラダイムに出くわしたら、複雑性の観点から疑ってみよ
- 大きなソフトウェアシステムの複雑性を最小化する助けになるだろうか?
- うわべは良さそうに見えるもの
- が、深く見てみると、複雑性を悪化させるものだとわかることも
英語
-
steer clear of
- 避ける、かかわらない
-
viable
- 実行可能な