5章 コンストラクションの設計
moch5oMaki.icon5.3.5まで
akht.icon5.3.6から
5章の概要
設計で難しいこと
設計で大事なこと
言語の選択も終わって、コンストラクションに入るぞ!となったがここでも再度設計の話。
「設計」と一言で言っているが、本文を読むとコンストラクションとほぼ同義(コンストラクションの一部)であるような内容も含んでいる。プロジェクトの規模に応じてどこまでを設計するか、というそもそも論的な課題についても触れている。
導入
設計とはそもそもなんなのか?設計の例はこんな感じ
大規模プロジェクトの場合・・・システムレベルの設計を行うこと
小規模プロジェクトの場合・・・プログラマがキーボードの前で行う(例えばクラスの相関図を書くことかもしれない)
コンストラクションと同義に見えるmoch5oMaki.icon
5.1 設計の難題
設計とは、要求をコーディングやデバッグと結びつける作業である
設計とは何か?に対する答え。いわゆるアーキテクチャをゴニョゴニョするだけじゃなくて、コンストラクションの中で行うこともたくさんあるのはこの定義を読めば分かる
俗に言う仕様のことqst exe.icon とあった
設計=仕様を実装すためのロジック全てと読める
やっかいな問題
それを解決することによって、あるいはその一部を解決することによって、初めて明確に定義できるようになる問題である
問題が起きるまで問題があることが分からず、そのためあらかじめ解決できない問題のこと
思いがけないバグを踏むまで分からないのはあるある過ぎてツライmoch5oMaki.icon
ユーザーは想定外の動きしかしない。。。
設計はルーズなプロセスである ーたとえきちんとした結果を生むとしても
設計はヒューリスティックなプロセスである
ここでいう「ルーズ」とは固まったやり方ではない、かっちり決まってない、くらいの意
何度も間違えてやり直すのが設計
どこまでやれば十分か分からない(設計に上限はない)
時間がなくなったら、という上限が答えとして書かれているが、逆に時間がないから設計がザルというパターンも容易に想像がつくので、どちらの意味でもルーズであるmoch5oMaki.icon
同じく試行錯誤が必要、という文脈で「設計はヒューリスティック(=発見的)」と表現されている
設計の方法に万能なツールや手法はない、とのこと
手を動かす回数で上達するということかぁmoch5oMaki.icon
設計は妥協と優先順位の産物
設計には制限がつきもの
理想の設計は存在しない=全部がパーフェクトに叶えられることはありえない
優先順位やバランスによって設計の目指すゴールが変わる
このあたりはうんうんって感じmoch5oMaki.icon
5.2 重要な設計概念
ソフトウェアの鉄則:複雑さへの対応
ソフトウェアに不可欠な技術的要素が複雑さに対処することであると言えるほど、それは重要なことである。
大事なことなので2回言っている
この章に限らずこの本のハイライトかなと思ったmoch5oMaki.icon
全ては複雑さへ対応するための設計で、具体的な手法やプラクティスで、もっというと上流工程であると言えそうmoch5oMaki.icon
なぜソフトウェアは複雑になるのか??
ソフトウェアは現実の課題を解決するための要求を満たすように作られる
現実は複雑に入り組んで絡み合った概念であり、ソフトウェアはそれに対処する
複雑で無秩序な現実世界と向き合い、
依存関係や例外を正確かつ完全に識別し、
だいたいではなく完全に正確な解決策を設計する、必要がある!!→無理w moch5oMaki.icon
そもそもソフトウェア開発は複雑で難しい(1個の知性で1ビットから数百メガバイトの違いを橋渡しする仕事)
設計に望ましい特性・・・そうですね、という感じ
ファンイン、ファンアウト初めて聞いたmoch5oMaki.icon
最小限の複雑さ、保守性、疎結合、拡張性、高いファンイン、低いファンアウト、移植性、無駄のなさ、階層化
設計のレベル・・・設計はどこまでやるのか、という問題。結局レベルの差はあれど全部やることではある。
「設計」という工程としてプロジェクトの最初にやるのか、個人の裁量に任せるのか、の違いになりそう
レベル1:ソフトウェアシステム
レベル2:サブシステムまたはパッケージに分割
レベル3:パッケージ内でクラスに分割
レベル4:クラス内でルーチンに分割
レベル5:ルーチンの内部設計
数週間以上のプロジェクトならレベル2、数日以上のプロジェクトならレベル3、という目安らしい
大きいプロジェクトほど設計で決めるのは大きいレベルまで
レベル2が詳しく記述されていて、サブシステム同士の繋がり(やりとりの方法)について特に重点的に語られている
ここでも規約を用いて複雑化を防き、単純化するようにいわれている
依存関係、隠蔽などはこの後にまた出てくる話と同じなので割愛
レベル4以降はほとんどコンストラクション(=コーディング)でやる作業って感じ
この辺り、設計というか分割したりするのってみなさんどうやってるんでしょう??聞きたいmoch5oMaki.icon
行ったり来たりしながらやり直しまくるんですが、なんかシュッとできる方法あるのかな
設計は試行錯誤、と言っているのでそれ以外ないか。。。
最初は分けずに書いて、切り出していくイメージ
テスト書けるかどうか
フレームワーク使う場合とかどうなるんだろう
モデルをどう分けてどんな責務を持たせるかが鍵になりそう
サービスクラスにまとめるとか(ファットモデルを避ける)
流行りのパターンとかがあって、設計とかも結局潮流変わってしまうのでやる気でないw
5.3 構成要素の設計:ヒューリスティクス
ここでも試行錯誤、というキーワード出てきた。
設計は非決定的なプロセスである。したがってヒューリスティクスを効果的に適用することが、優れたソフトウェア設計の中心的な作業となる。 ヒューリスティクスについては、「試行錯誤」の試行部分のガイドと考えても良いだろう。
5.3.1 現実世界のオブジェクト
もっともよく使われるアプローチで、現実世界のオブジェクトをプログラムに落とし込んで「オブジェクト指向」で臨む方法
問題領域をさらに詳しく調べれば、現実世界のオブジェクトと1対1に対応させる以外にも、ソフトウェアオブジェクトの効果的な選択肢があることに気づくかもしれない。ただし、現実世界のオブジェクトは出発点としては最適である。
まずは現実を元にモデリングしてみて、そこからより最適な割り当てを試行錯誤しながら探す感じかmoch5oMaki.icon
各オブジェクトが何をすべきかを決定する
他のオブジェクトに対して何ができるかを決定する(包含と継承)
他のオブジェクトに公開する範囲を決定する(パブリックとプライベート)
各オブジェクトのインターフェースを定義する
設計の時のサブシステム同士の関係で出てきたのと同じような話が出てくる
そして反復(イテレーション)する、ということでまた試行錯誤っぽい話になる
最上位レベルのシステム構成作業を反復してクラスをさらに整理する
クラスを定義する作業を反復して、各クラスの設計をさらに細かく定義する
5.3.2~5 抽象化、カプセル化、継承、隠ぺい
いずれも複雑化を回避する、という共通の利点がある
抽象化は関連性のない詳細を無視できる
カプセル化は抽象化がやり残した部分を引き受け、複雑なものを見せない
抽象化は上位レベルで行われるのに対し、カプセル化はその他の詳細レベルで行われる
継承は似たようなオブジェクト同士を一般化することで抽象化と相乗作用を生む
継承によりコーディングがシンプルになる
隠ぺいはカプセル化とモジュール化の概念の由来となっていて、抽象化の概念と結びついている
プライベートメソッドのテストは書くべきか?という議論
パブリック(呼び出し元)のテストが通ってたらOK、とか
プライベートメソッドのテストを書かないといけない状況がまずい、とか
責任の分離できていないよねみたいな感じ
最終的には全部パブリックになるんじゃないの?
初心者と中級者を見分ける方法として、可視性のコントロールという話があった
5.3.5 秘密の隠ぺい
1972年に情報隠ぺいについて初めて言及され、批判的な意見も出たが情報隠ぺいが変更が多いインクリメンタルな環境に特に効果的であることが示された
情報の隠ぺいは「複雑さの隠ぺい」に重点を置いているから必然的にそうなる
クラスを設計する上で重要なタスクの1つは、クラスの外部に公開する機能と秘密にしておく機能を決定することである。
クラスのインターフェイスは、その内部のしくみを出来るだけ明かさないものにすべきである。
良いクラスインターフェイスの例は氷山の一角・・・ほとんどが水面下に隠れている
情報隠ぺいは設計の全てのレベルに効果的、と言及されている
隠ぺいされる秘密の種類は、複雑さの隠ぺいと、変更の隠ぺいの2種類
情報隠ぺいの障壁のほとんどは、他の手法を習慣的に使用している内に植え付けられた、閉鎖的な思考である。
過剰な情報伝達、循環依存、クラスデータとグローバルデータの取り違え、パフォーマンスの明らかな低下、
パフォーマンスとか関係あるの?
昔は関数やサブルーチン呼び出しが少ない方が良い、というセオリーがあったようだ
今は関係ない、とキッパリ書かれていた(リファクタリング)
上記が情報隠ぺいの障壁となりうるが、考え方が間違っているケースもある、というような話
情報隠ぺいの価値
情報隠ぺいを使用しないプログラムに比べて変更が4倍も容易である
!!すごいmoch5oMaki.icon
情報隠ぺいについて考えることが、オブジェクトについて考えた時に得られなかった設計のひらめきを与え、その決断を促すのである。
これは難しそう。。。moch5oMaki.icon
「何を隠ぺいする必要があるか」という問いかけは、全てのレベルで良い設計を後押しする。
「何を隠ぺいすべきか」と自問する習慣をつけよう
これを自問した結果、正解というか「これは隠ぺいしよう」とか決断する時の決め手はどうやって身に付けるのかmoch5oMaki.icon
レビューしてもらう時に意識的に尋ねるくらいしか思いつかなかった。。。
hr.icon
akht.icon5.3.6
akht.icon↑やってる!!
5.3.6 変更の可能性が高い領域の特定
変化に対応することは、優れたプログラムの設計において最大の難関の1つである
不安定な部分を隔離する
そうすれば変更の影響範囲を制限できる
不安要素への準備
変更されそうなところを
特定する
どこが変わりそうかというのはだいたいわかる
分離する
クラスとして独立させたり
囲い込む
クラス間のインターフェースには変更が表れないようにする
変更されやすいものを列挙
業務ルール
これが一番変わりそうakht.icon
法律・制度・税率・規定などいろんな影響ですぐ変わる
ハードウェアへの依存部分
自分がやってきたなかでは、OSとかOfficeのバージョン依存とかが近いかなあakht.icon
むずかしそう
入出力
入出力フォーマットは変わりやすいakht.icon
プログラミング言語の標準ではない機能
異なるハードウェア、別ベンダーによる言語実装、同じベンダーからの言語の新しいバージョン
破壊的変更を行う言語もいくつかありますねakht.icon
プログラミング言語の場合はだいたいまずdeprecatedとかになるから対応はしやすい印象akht.icon
設計とコンストラクションの難度が高い領域
難易度が高い = うまくできない = やり直しにやりなすい、ということで変更されやすい
状態変数
状態変数とは、プログラムの状態を示すもの
よくあるのは、エラーを表す状態変数が 真偽値 -> 列挙型 に変わったり
状態変数を使う場合はこうすればいい
ブール型を使わず列挙型を使う(列挙型は新しい値の追加も簡単だし)
直接参照するのではなくアクセスルーチンを使う
データサイズの制約
配列のサイズを100で宣言すると、知る必要のない情報を世界中に公開することになる
マジックナンバーじゃなくて定数を使おう
これは「情報隠ぺい」の文脈なのか?
変更の可能性を予測して、その予測コストも計算(設計)に入れよう
変更可能性のある領域を特定する良い方法
まずシステムの核心となるミニマムなサブセットを抜き出す
そこからシステムに追加していく最も小さな増分を定義する
この小さな増分について変更可能性を検討する
質的な変化も検討する
スレッドセーフにする
国際化する
など
変更されそうな領域なら、情報隠ぺいの原則を適用
5.3.7 疎結合の維持
疎結合 = 結合が「疎」であること
結合 = クラスやルーチンの他のクラスやルーチンとの結びつき
設計で目指すのは、他のクラスやルーチンとの関係を小さく、直接的で、可視的で、柔軟なものにすること、つまり「疎結合」である
「密」はダメ
ここからはクラスとルーチンをまとめてモジュールと呼ぶ
良いモジュール間結合
= あるモジュールが他のモジュールから簡単に使用できるくらい弱い(疎である)
列車の連結のメタファ(接合部分とその仕組みが単純)
良いモジュール間結合とは、まず単純であることが条件
他のモジュールになるべく依存しないようにする
引数の数のメタファ
5個の引数をとるルーチンより、1個しかとらないものの方が疎結合なので良い
それはそうだけど・・・akht.icon
5個の引数をとるものは、呼び出し側がそのなかで何が起こるかを実質的に知っているから
同じグルーバルデータを異なるクラスから使わない
そうなると密結合になっちゃう
ここでいうグローバルデータってなんだろうakht.icon
スーパーグローバル変数とか、スコープが巨大なものかな
モジュール間結合の評価基準
サイズ
ここでいうサイズはモジュール間結合の数のこと
ルーチンだったら引数の数
クラスだったらパブリックメソッドの数
それらの数は小さいほどよい
可視性
モジュール間の結合が目に見えること
データを引数に明確に渡すのは結びつきが可視化されるので良い
グローバルデータは結びつきが不透明なので良くない
柔軟性
モジュール間結合をどれくらい簡単に変更できるか
例
code: module1
func LookupVacationBenefit(employee Employee) {
employee.雇用日, employee.職種を使って何かする
}
code: module2
Employee {
雇用日
職種
}
func something() {
employee = new ...
LookupVacationBenefit(employee)
}
↑ここまででは、モジュール間結合は弱い
2つのモジュール間のemployeeオブジェクトの結びつきは見えているし、結びつきは1つしかない
↓こういうことが起こったらどうか?
code: module3
func something2() {
雇用日 = ...
職種 = ...
LookupVacationBenefit(********)
}
module3からもLookupVacationBenefit()を呼びたい
module3はemployeeオブジェクトは持たないが雇用日と職種を持つ
module3がLookupVacationBenefit()を使うためには、employeeオブジェクトについて知る必要がある
ダミーのemployeeオブジェクトを作って雇用日と職種を代入することも可能だが...
LookupVacationBenefit()が使うのは雇用日と職種だけと知っていないといけない
こういう方法はその場しのぎで見苦しい
この場合は、employeeオブジェクトを引数にとるのではなく雇用日と職種を引数にとるように変更するとよい
オブジェクトいらないじゃんって話にならない...?akht.icon
さっきの引数が5つのルーチンとかオブジェクトにまとめて渡したくなるが...akht.icon
つまり、他のモジュールからの呼び出しが簡単であればあるほどそのモジュールの結合は弱い
一般的なモジュール間結合の種類
単純データパラメータ結合
やりとりされるデータが基本データ型で、すべてのデータが引数として渡されるもの
単純オブジェクト結合
モジュールがオブジェクトをインスタンス化すること
モジュールとオブジェクトとの結合をそう呼ぶ
この種の結合は望ましい
オブジェクトパラメータ結合
Object1がObject2からObject3を受け取ることを要求するような場合
Object1は、Object2がObject3について知っていることを要求している
Object2に対して基本データ型だけを要求する場合よりも結合が密
セマンティック結合
この結合が一番やっかい
内部のしくみに関するセマンティクスを使う場合に生じる
セマンティクスは意味とか意図を表すもので、シンタックスの対となるものakht.icon
例をいくつか
フラグを渡して何をすべきか伝える
そのフラグが渡った先でどんな意味を持つのか、そのフラグで何をするのかを推測しないといけない
グローバルデータの更新順番を想定している
自分で更新する前に、あそこで適切に更新されているはず...みたいな
Aを呼び出す前にBを呼び出してね、とあるが、実際はBの中でもAが呼ばれることを知ってるので、直接Bを呼び出す
あるオブジェクトを初期化するとき、自分に必要な部分のデータだけ入れる
自分に必要なルーチンで使うデータだけ、とか
こんなのはよくやっちゃうakht.icon
渡ってきたオブジェクトが実はある子クラスだと知っているのでキャストして子クラスのメソッドを呼び出す
セマンティック結合はとても危険
なぜなら、使用される側のコードを変更するとだいたい問題が発生するから
しかも、コンパイルエラーでは検知できないような問題(セマンティクス依存なので)
疎結合の目的は、効果的なモジュールによって抽象化をレベルアップすることだ
そのようなモジュールを一度書いてみれば、それが当然のことであるとわかるだろう
↑こういうの多いけど、実際そうなんだろうなakht.icon
一度に一つのことに集中できるようにするため
5.3.8 一般的なデザインパターンの検索
ほとんどの問題は過去の問題に似てるから、同様のソリューション(デザインパターン)を使おう、という話
ざっくり利点をあげると
デザインパターン名だけで伝わる情報が多い(どんなことをするのか、どういう方法でやるのか)
デザインパターンは一般的な問題に対する長年の取り組みから生み出されたノウハウである
自分で実装せず、ライブラリを使うようなもの
設計の選択肢を提案するという価値がある
共通言語としての役割で議論が活発になる
「AとBどっちにしようか」といえば、相手に多くのことが伝わる
※デザインパターンの例は書籍を参照
デザインパターンの落とし穴
無理にデザインパターンに押し込もうとすること
デザインパターンを試してみたいという理由で使うこと
5.3.9 その他のヒューリスティクス
必ずしも効果が期待できないものの、役にたつかもしれないヒューリスティクス
強い凝集度
クラスのルーチンが、主目的をどれだけ厳密にサポートしているか
クラスがどれだけ焦点に凝集されているか
どれだけ関連性が強いか
凝集度は強い方がいい
階層の構築
ある研究によれば、階層は複雑な情報を整理するための自然な方法であることがわかっている
常にすべての詳細について考えるのではなく、必要な時に考えられるようになる
OSI参照モデルとかTCP/IPもakht.icon
クラスの規約の形式化
各クラスのインターフェイスをプログラムの他の部分との規約として考える
あんまピンとこないな...akht.icon
責任の割り当て
オブジェクトが何に責任を持つのか考える
テストのための設計
テストしやすいように設計する
失敗の回避
過去の成功した設計の特徴をそっくりまねるだけだと失敗する
失敗する可能性があるケースを十分に検討する
バインディングタイムの選択
バインディングタイム = 特定の値が変数にバインドされるタイミングのこと
早い段階にバインドするコードは単純だが柔軟性に欠ける傾向がある
これらの値を先にバインドするとどうなるか
これらの値を後でバインドするとどうなるか
このテーブルをコードのこの場所で初期化するとどうなるか
この変数の値を実行時にユーザーから読み取るとどうなるか
スコープとかイミュータブルみたいな話題も絡んできそうakht.icon
制御の一元化
1つの正しい場所の原則──重要なコードの検索も、保守作業による変更も、1つの正しい場所で行うこと
何かを探す場所が少なければ少ないほど、変更は容易で安全になる
凝集度とも関連しそうakht.icon
ブルートフォースの検討
へたなソリューション(アルゴリズム)を使うくらいなら、総当たりした方がいい
洗練されたものを使わなくても、総当たりで十分なことも多い
これは競プロとかでもよく言われるakht.icon
データ量的に問題なければ総当たりの方がシンプルで理解しやすく変更も容易
図の作成
階層化して考えるのは図の作成とセットになる気がするakht.icon
(余談)作図するときどんなツールとか使ってますかー?
紙とペン
iPad
ipadでapple pencil アプリはGoodNotesmoch5oMaki.icon
〃 アプリはOneNote
keynote
whimsical
設計のモジュール化
モジュール化設計の目的は、ルーチンやクラスを「ブラックボックス」に仕立てることである。
モジュール化の概念は、情報隠ぺいやカプセル化といったヒューリスティクスに関連している
ブラックボックスからシステムをどのように組み立てるか考えると、それらではわからないことが理解できることもある
hr.icon
5.3.10 設計に関するヒューリスティクスのまとめ
■現実の世界のオブジェクトを見つけ出す
■首尾一貫した抽象化を目指す
■実装の詳細をカプセル化する
■可能な場合は継承する
■秘密を隠す(情報隠ぺい)
■変更される可能性がある領域を特定する
■モジュール間結合は疎結合にする
■一般的なデザインパターンを探す次のヒューリスティクスが役立つこともある。
■モジュール凝集度を強くする
■階層を構築する
■クラスの規約を形式化する
■責任を割り当てる
■テストのために設計する
■失敗を避ける
■バインディングタイムを意識的に選択する
■制御を一元化する
■図を描く
■設計をモジュール化する
もっと気がかりなのは、同じプログラマが同じタスクを何通りもの方法で処理しかねないことだ。無意識にそうするのかもしれないが、ほとんどの場合は単に目新しさを求めて、あるいは凝ったことがしてみたいという理由でしかない。
─A.R.Brown、W.A.Sampson
やりたいからやってみる、は場合によっては許して...akht.icon
5.3.11 ヒューリスティクスを利用するためのガイドライン
ソフトウェアの設計では、他の分野の設計方法が参考になる。
例えば『How to Solve It』(G. Polya)には、数学の問題を解く方法について汎用化した手法が載っている。
問題を理解する
計画を立てる
計画を実行する
振り返る
※詳細については書籍を参照
『いかにして問題を解くか』買ったけど最初らへんしか読んでないakht.icon
最も効果的なガイドライン = 1つの方法にこだわらないこと
UML図に表せないときは言葉で書いてみる
簡単なテストを書いてみる
別の方法に切り替えてみる
ブルートフォースを検討する
概要をまとめ、それを図に描きながら、頭で追ってみる
問題から離れてみる
散歩に出かけるとか、別のことを考えて、それから問題に戻る
脳には集中モードと分散モードがある
分散モードの時に案外物事を解決できたりする
物を持ってうたた寝する
設計に行き詰まったら、後回しにしてもいい
情報が揃ってないせいかもしれない
経験を積めばいい決断が下せるようになるかもしれない
5.4 設計のプラクティス
どのように設計すればいいか、つまり設計プラクティスのヒューリスティクスについて
5.4.1 反復
設計は反復的なプロセス
一度で完璧にはできないし、しようと思わないでいい
上位レベルと下位レベルから設計を眺めてみる
上位レベルで得られる全体像が、下位レベルの詳細を正しく把握するのに役立つ
下位レベルで得られる詳細は、上位レベルでの決定事項を支えるゆるぎない土台を築く
コンテキストスイッチが発生して負荷はかかるが、効果的な設計には欠かせない
最初に思いついた設計が十分良さそうでも、そこで終わらず2回目の設計をやっみる
必ず2回目のほうがよくなる
これは文章でもなんでも同じですねakht.icon
なのでまず早く完了させるってのが重要に思うakht.icon
5.4.2 分割攻略
複雑な問題は分割して考える、行き詰まったら反復する
分割統治法というアルゴリズムもあるakht.icon
5.4.3 トップダウン方式とボトムアップ方式
オブジェクト指向だと、
トップダウン方式
上位レベルの抽象化 -> スーパークラスや一般的な設計要素を定義 -> 詳細な設計(サブクラス・ルーチンなど)
ボトムアップ方式
具体的な要素が出発点 -> 具象オブジェクトを洗い出す -> スーパークラス・抽象クラスにまとめて汎用化
5.4.1 にある上位レベル・下位レベルとの繋がりがある話
どっちがいいというのは色々意見がある
トップダウン賛成論
トップダウン方式の背景:人間の頭脳が一度に処理できる情報には限りがあるという考え
汎用的なクラス -> 専門的なクラスというように分解していけば、一度に考える情報量を抑えることができる
分割攻略のプロセス
反復することを含む
分解するとき、いろいろな方法が考えられる
ひとつを試して状況を見守る
別の分解方法を試して、そっちの方がうまくいくかどうかを確認する
これを繰り返すうちに、どの方法がうまくいくか、なぜうまくいくかがわかってくる
プログラムはどこまで分解すればいいのか?
次のレベルを分解するよりもコーディングする方が簡単になるまで分解する
その解決策が自分でも少しややこしく感じられるとしたら、後からそれを担当する人にとっては手に負えないほど難しく感じられることに注意しよう。
ボトムアップ賛成論
トップダウン方式は抽象的なのでどこから手をつけたらいいかわからないことがある
「このシステムが何をする必要があるか知っているか」と自問する
具象クラスに必要な下位レベルの機能が思い浮かぶはず
それらを上位レベルで見直す
どちらでもない
トップダウン方式とボトムアップ方式の特徴・長所・短所
トップダウン方式
分解戦術である(汎用的な問題からはじめ、扱いやすい大きさに分解していく)
長所
簡単であること
コンストラクションの詳細を先送りにできること
短所
ボトムアップ方式
組み立て戦術である(扱いやすい大きさから初めて、全体的なソリューションを組み立てていく)
長所
必要なユーティリティ機能が早い段階にわかること
短所
これだけを使うのが難しいこと
最初に選んだ要素からプログラムを構築できないと判明することがあること
レンガから飛行機を作ることはできない
結局、トップダウンとボトムアップは競合しないので両方取り入れていい
5.4.4 実験的プロトタイプ
設計がうまくいくかどうか、最初から全てわかる訳ではない
それがうまくいったと言えるのは、パフォーマンス目標を達成するとわかったときかもしれない
それがうまくいったと言えるのは、GUIライブラリを選択した時かもしれない
少なくとも設計を部分的に解決してからではないと、設計の問題を完全に定義できない
これに対処するには実験的プロトタイプを作ればいい
必要最小限の使い捨てコードで作る
ただし、設計の問題を適切に把握していないと、過不足ない使い捨てコードは書けない
「このコードはあとで使えるかも」という気持ちがあると使い捨てのコード(プロトタイプ)にはならない
それはシステムを書くことになってしまう
あえてプロダクション版とは別の技術要素を選択することも有効らしい
5.4.5 コラボレーティブな設計
設計は1人でやるより2人でやるほうがいい
書籍にはコラボレーションのやり方が書いてあるけど、ここはいろんな方法がありそう
5.4.6 どれだけ設計すれば十分か
私たちは、プロジェクトの最後に十分な時間が残るように、設計プロセスをさっさと片付けて、問題を解決しようとする。しかし、その残った時間は、設計プロセスを急いだために生じたエラーを発見するために使われる。
─GlenfordMyers
厳密にこれと決まっている訳ではない
チームの経験
システムの寿命
信頼性の理想的なレベル
プロジェクトやチームの規模
などに左右される
https://gyazo.com/00d4d6f88ee7c762121d147527e9d81a
Steve McConnell. 【電子合本版】Code Complete 第2版 完全なプログラミングを目指して (Japanese Edition) (Kindle の位置No.3294). Kindle 版.
設計をどれくらい吟味すればコーディングを開始できるかわからない場合は、少ししつこく調べることもあり
設計の最大の問題は、十分に調べたと思っていたのに、後になっていくつか問題が残っていたことに気付くことである。
つまり、設計の最大の問題は、難しいことがわかっていて設計がうまくできなかった領域ではなく、簡単であることがわかっていたのでまったく設計していなかった領域で見つかることが多い。
設計作業をしすぎて問題に悩まされたというプロジェクトなど、ほとんど聞いたことがない。
ただし、設計書が多すぎて悩まされるプロジェクトならたまにある
設計作業に80%、残りの20%で見栄えの良くない設計書をどうにか書き上げると著者は書いてる
5.4.7 設計作業の文書化
通常は公式な設計書を書き上げることで、設計作業を形にする
他の方法がいくつか提案されてる
設計書をコードに直接挿入する
設計に関する議論や決定事項をWikiに記録する
電子メールで議事録を送る
デジタルカメラを使用する
設計をフリップチャートに記録する
CRCカードを使用する
UML図を適度に詳しく作成する
tommy.icon 加えるならPlantUMLはDSLで書けてバージョン管理とかもできていい感じ 5.5 一般的な手法へのコメント
「全てを設計する」から「何も設計しない」に大きく変わった歴史があったり、「少しだけ設計する」とか「十分に設計する」が取って代わった歴史もあった
設計をどれだけやれば十分かは断言できないが、常に誤りであると保証できることが2つある
全ての詳細を残らず設計すること
全く何も設計しないこと
これらは常に誤り
設計を厄介でルーズでヒューリスティックなプロセスとして扱うこと
最初に思いついた設計で満足しない
第三者と協力すること
単純さにこだわること
必要であればプロトタイプを作ること
反復、反復、反復を繰り返す
5.6 参考資料
参考文献がたくさん並んでる
5.7 まとめ
ソフトウェアの鉄則は、複雑さに対処すること
単純さにこだわった設計にする
単純さをどう実現するか
一度に頭に入れなければならない本質的な複雑さを最小限に抑えること
偶発的な複雑さを必要以上に増やさないこと
設計はヒューリスティックな作業なので、一つの手法に固執しない
反復。いろんな設計を繰り返し試してみる
「何を隠蔽すべきか」を自問する
余談