書籍 プリンシプルオブプログラミング 第4章 まとめ

4章

視点 〜プログラマの観る角度〜

4.1 凝縮度

モジュールは純粋に

凝縮度:モジュールに含まれる機能の「純粋さ」を表す尺度

凝縮度は7段階の強度に分かれており、段階が高いほど純粋である。

最も強度が高く理想的なモジュールは「機能的強度」であり、以下のような特徴がある

  • モジュール内の全ての命令が1つの機能のために関連している

    • 「 一部だけ変更」というようなことが減り、あったとしても「関わりのあるモジュールとの共通の事情による変更」である可能性が高くなる
    • 結果的に、変更を行なった際の他モジュールへの影響を、最小限に留められる
雑じり気のあるモジュールは脆い

モジュールの凝縮度を高めることで、得られるメリット

  • コードの設計が明確になり、理解しやすくなる
    • 無駄な内容がないため
  • 保守と拡張が容易になる
  • 再利用性やモジュール間の疎結合性が向上
    • 変更によるほかモジュールへの影響が少ない

モジュールの凝縮度を高めるためには、モジュールの関連性は最大化、他モジュールとのの関連性は最小化する必要がある。

4.2 結合度

モジュール間は疎遠に

モジュール間の関係は、疎遠であればあるほど相互の依存性が低くなり、影響を受けにくくなる。 尚、こちらも凝縮度と同様にレベル分けがされており、データ要素だけの受け渡しを行なう「データ結合」の状態を目指す。

低結合モジュールを目指す

モジュールの結合度を低く保つ手段

  • データの受け渡しでは、引数を使う
  • 出来るだけグローバル変数を利用しない
  • 渡す値によって返り値が変わらないようにする(一貫性を持たせる)

また、結合先モジュールの質にも注目して、なるべく不安定な要素と関係を持たないようにすること。

4.3 直交性

コードに以下の要素を持たせることで、変更に強くコードを生み出すことが出来る。

独立性

  • 他のコードの要素を無駄に含まないこと

分離性

  • バグや脆弱性を含んでいることが分かった際に、その部分を簡単に分離できる能力 また、「生産性の向上」や「リスク低減」といった恩恵もある。

4.4 可逆性

「今後一切変更しない」という決定は避けるようにして、変更に対し、いつでも後戻りや修正を行えるようににする。 また、ソフトウェアとのやり取りを行なう境界部を関節化することで、変更に対し柔軟に対応できるようになる。

4.5 コードの臭い

コードの中に、理解や修正がしにくい部分(臭い)があると、潜在的なバグの要因になりかねない。そのため、適宜改善していかなければならない。

コードの臭いとなりゆる要因

  • 見づらいコード
  • 長過ぎる、大きくて多いコード
  • 命名に一貫性の無いコード

4.6 技術的負債

問題コードは借金である

コードを書く際は、なるべく「綺麗で読みやすいコード」を書くべきだが、納期が迫っている時などは、「汚いけど動くコード」が優先されるときもある。 但し、汚いコードが残り続けると、その後に作成するコードへの影響もあるため、なるべく早くリファクタリングすること。

書籍 プリンシプルオブプログラミング 第3章 下 まとめ

3章 UNIX哲学 〜 UNIX哲学

3.37 UNIX思想

UNIX思想

  • UNIX文化の中で育まれた、優れたプログラミングを行なうための技法の集まり
  • UNIXが誕生してから、今なお50年以上使用されていることが、思想の正しさを証明している

3.38 モジュール化の原則

控えめなモジュールを作る

モジュールの作成は、以下のように行なう - 連性の高いコードのみで構成する - 問題や変更を局所化できる - インタフェースにおいては、とにかく余計なモノを無くして、シンプルにする(入り口を狭く)

3.39 明確性の原則

コードを明確にする
  • とにかく読みやすいコードを書く
    • 多少のパフォーマンスよりも可読性

3.40 組み立て部品の原則

フィルタにして組み立てる

フィルタ:入力値を加工して、別の値として出力するソフトウェア

より多くのソフトウェアとやり取りするためにも、以下のようなことを意識してソフトウェアを設計する

  • 無駄に凝った設計をしない
  • テキスト形式でデータを送受を行なう
    • シンプルにデータを送受できる
    • 異なる実現方法を取っているソフトウェアとも連携しやすくなる

3.41 分離の原則

カニズムからポリシーを離す

ポリシー

  • ビジネスロジックやUIなどの、ソフトウェアが依存する
  • ソフトウェアが作られる目的、前提に近い
  • 比較的不安定(変更される可能性が高い)

カニズム

  • アルゴリズムやエンジンなどの、ソフトウェアに依存しない部分
  • ソフトウェアの内部的な処理、実現手段
  • 比較的安定

相互を繋いでしまうと、以下のようなデメリットが発生してしまう。

  • カニズムの安定に伴い、ポリシーが硬直化してしまい、ユーザの要求に柔軟に対応できなくなる
  • ポリシーの変更によって安定しているメカニズムの変更も強要されてしまう

反して、それぞれを独立して取り扱うことで以下のような恩恵を得られる。

  • カニズムを崩さずに新しいポリシーを試せる
    • カニズムが変わらないため、テストを深く行なうことができ、品質をコスパよく高められる
  • 相互の依存性がなければ、メカニズムは他の開発でも流用できる

3.42 単純性の原則

コードはシンプルにする

自分のエゴや機能を増やしたいという理由から、コードは自然に複雑化していってしまうため、とにかくシンプルに保つことを意識する。(美徳化)

3.43 倹約の原則

分量や複雑さをなるべく小さく留めることで、保守する際の負債を減らすことが出来る。 また、コードを小分けにして、大きめのコードに継ぎ足しはしないようにする

3.44 透明性の原則

ソフトウェア動作の見える化

ソフトウェアのデバッグをやりやすくするために、設計時から以下のことを意識して設計する。

透明性

  • 一目見て、ソフトウェアがどんな動作をしているのか理解できるようにする

開示性

  • ソフトウェアの内部を監視すること
    • ログを取るなど

デバッグを簡単にする仕組みを、積極的に本番コードにも組み込んでいく。

3.45 安定性の原則

ソフトウェアを安定させる

コードが複雑になっていくと、

「理解が追いつかない」

 ↓        ↑

「把握できていないところで障害 (理解できていないため、発生確率高)」

というスパイラルに陥るため、理解しやすいよう「透明性」と「単純性」を満たすコードを記述する。

3.46 表現性の原則

コードにおける情報の表現は、ロジックではなくデータに寄せて記述するようにする。 また、処理の複雑化を避けられない場合、コードよりもデータ構造を複雑にした方が分かりやすくなる。

3.47 驚き最小の原則

インタフェースは、利用者が想像するであろう形に設計し、利用者が見た際に、悪い意味で驚かせないようにする。 また、利用者の想像に近づけることで、元々持っている知識を使うことができて、学習コストを抑えられる。

方法

よく似たソフトウェアのインタフェースをモデルにする

  • 同じような機能を持ったソフトを参考にしてUIや操作系を設計する

想定ユーザーの特徴を考慮する

  • ユーザーが誰なのかを考えて、想像のレベルがどこにあるのかを考慮する

伝統に注意を払う

  • その機能を持ったソフトウェアにおいて、伝統的に使用されているファイルの形式やショートカットなどを参考にする
  • 伝統的に築かれた手段は、学習コストを低減する

「一見似ているが異なる」を避ける

  • 「一見同じだが微妙に違う」機能を実装する際は、見た目も大きく異ならせた方が、間違いにくく親切

3.48 沈黙の原則

ソフトウェアは余計な出力を行わずに、重要な情報のみを表示する。

  • 余計な出力があると、重要な出力が埋もれてしまう

3.49 修復の原則

ソフトウェアでエラーの回復に失敗した場合、思わぬ障害を発生させてしまう可能性もあるため、処理を中断すること。

また、エラーはなるべく早い段階で派手に通知して、ユーザーに気づいてもらえるようにする。

3.50 経済性の原則

時間を大切に

時間は限られているので、以下のような理由でなるべく浪費しないこと

マシンスペックの不足

  • ハードの基本性能が不足していると、あらゆる処理に時間がかかってしまう。

使用するツールの制限

  • 必要なツールを利用できないと、「別のソフトを探す、インストール、操作方法を勉強」「別の方法を考える」のように時間の浪費に繋がってしまう

環境に関するルールや制限

  • ネット環境の使用禁止などによって、必要なサイトや情報へのアクセスが失われてしまう

3.51 生成の原則

コードを書くためのコードを書く

人間がコードを記述するよりも、コードにコードを自動生成させた方が「安価(人が書かないため)」で「高品質(機械が書くためミスが少ない)」というメリットを得られる。

3.52 最適化の原則

早いコードより正しいコード

コードの処理を早めるために最適化するより、冗長でも必ず動くコードを書くこと

早い段階から「速いコード」を設計しない

透明性や単純性が犠牲になる

  • コードの可視性が低下したり、より複雑なものになってしまう

部分の最適化が、全体の妨げに

  • 全体に足並みを揃えずに部分の最適化を行なうと、全体の最適化で得られる効果が薄くなってしまう

プロトタイプの作成などによって、徐々に早いコードにしていくことが効果的である。

3.53 多様性の原則

多様性の許容

「正解は一つ」という考えは持たずに、他の手段を否定しないようにして、よりよい手段を求め続けること。

3.54 拡張性の原則

ソフトウェアは追加の機能実装などで変更されるモノであるため、未来に備えた設計をしておく。

3.55 UNIX哲学

UNIXにおける哲学

3.56 小は美なり

小さなソフトウェアは、理解しやすかったり、マシンリソースへの負担の低減も期待できる。

3.57 1つ1仕事

1つのソフトウェアに1つのみの仕事を実装することで、以下のようなメリットが期待できる。

ソフトウェアの本質を見失わなくなる 問題解決を行いやすくできる 理解しやすくなる

3.58 即行プロトタイプ

出来るだけ早くプロトタイプ着手

実際にデブロイする完成品では無いため、アイデアを試行錯誤するためにも、早い段階でプロトタイプを実装する

3.59 効率性より移植性

「開発効率性」と「移植性」なら、移植性を優先すべき。

環境に依存しないソフトウェアにすることで、移植の際のコード修正を少なく済ませることができ、ソフトウェアの価値を維持できる。

3.60 データはテキスト

データをテキストファイルにまとめることは、以下のようなメリットがある。

移植性

  • テキストファイルは多くの端末に対応している

可視性

  • 人間にとって確認しやすく、エディタなどで簡単に編集できる

ツール、コマンド

  • ツールやコマンドにとって扱いやすく、バイナリ形式の変換などを気にする必要が無くなる

また、テキストファイルの形式には、標準規格のもの(CSV,XML,JSONなど)を使用することで接続性の向上も見込める。

3.61 レバレッジ・ソフトウェア

ソフトウェア同士を連携させて、ソフトウェアの価値をコスパ良く高める。

少ない労力で巨大な成果を得る

他人のコードを借りることで、少ない労力でソフトウェアの機能を実装できる。(勿論十分に理解した上で)

手作業を自動化する

人間が手作業していたものを自動化することで、「手間」「ミス」「時間」「コスト」の全てを減らせるため、正確性と生産性の劇的な向上が見込める。

3.62 シェルスクリプト

シェルスクリプトによって、梯子の効果と移植性を高める。

梯子の効果

  • シェルスクリプトを利用することで、他のソフトウェアやコマンドを繋げる
    • 多くのプラットフォームで使用できれば、伴ってより多くの人に利用される可能性が上がる

移植性

  • シェルはインタプリタであるため、プラットフォームごとに専用コンパイルする必要が無い。そのため、大した変更をしなくとも多くの環境で実行できる

3.63 対話インタフェース回避

ユーザーとソフトウェアが対話する形式を利用すると、以下のような弊害が出てくる。

  • ソフトウェアごとに専用の対話方法を覚えなければならない
  • ソフトウェアとの対話ができなくなる
  • 人間の入力時間がボトルネックになる
  • 入力部分がコードを大型化させてしまう

3.64 フィルタ化

ソフトウェアをフィルタとして設計する(データを入れたら処理されて出てくる)

ソフトウェアとは入出力である

データは人間が生成するもので、ソフトウェアは何かを生成するのではなく、あくまでデータの処理に徹するのが本来の姿である。

UNIX哲学・小定理

ざっくり解釈

環境カスタマイズ

UNIXの設定を細かく、ユーザ好みに行えること。 最初は戸惑うし、細かい設定を突き詰めるのに苦労するが、一度完璧な設定が出来上がってしまえば、非常に操作性のよい環境が出来上がる。

小文字使用

基本は小文字を利用すること。 目への負担の低減や、その中で大文字を使えばより目立たせることが出来る。

沈黙は金

重要な情報のみを表示し、小さなエラーは表示しない。(重要な情報が埋もれてしまうから)

90パーセント解

90から100の壁は非常に分厚いため、90%の完成度に抑えておく方がコスパがいい。

劣るが優る

「高品質高価」より「コストはそこそこだが、効率的」なシステムの方が、受け入れられやすいという考え方

書籍 プリンシプルオブプログラミング 第3章 中 まとめ

3章 アーキテクチャ非機能要件 〜 7つの設計原理

3.23 アーキテクチャ非機能要件

非機能要件:機能以外の全般についての要件

非機能はリリース後に影響大

開発や保守、運用、コンピュータリソースへの影響がある。 例えば、運用時における「システムダウン」「パフォーマンスの低下」などのトラブルは、非機能要件の不備によって引き起こされる。

非機能観点で設計

アーキテクチャ設計において、以下のように非機能要件を考慮する。後から再設計はできない。

  • 要件定義時に、それぞれの観点についてどの程度必要か確認する
  • アーキテクチャ設計で、↑の要件を考慮に入れた構造を考える
  • テストで要件を満たしているか

機能のテストは「何をするのか」、非機能のテストは「どのように動作するか」着目する部分を変える。

セキュリティ非機能要件
  • 機密性

    • 認可されていないプロセスに対して、情報を使用不可、非公開にする
    • 不正に情報を取得されてはならない
  • 完全性

    • 資産の正確さと完成さを保護する
    • 情報が不正確であってはならない
  • 可用性

    • 認可されたプロセスから、情報へのアクセス及び使用が可能な状態
    • アクセスできるはずなのに出来ない状況は避ける

ペネトレーションテストなどの検証を行いつつ、ユーザーにも分かりやすく解説することが必要。

3.23 変更容易性

コードの変更を容易にする

コードの変更を行う際は、素早くかつ品質を落とさないことが求められる。(簡単に追加や変更できて、他への影響が少ない)

保守性 - 問題や障害の解決しやすさ、他への影響を最小限に留める

拡張性 - 機能の追加や置き換えのやりやすさ、モジュール間での結合度(依存度)を弱くする

再構築 - モジュール間の関係を再組織化すること、モジュールを柔軟に配置できるような設計が必要

移植性

  • ソフトウェアをあらゆる環境(ハード、OS、言語など)に適合させやすくする
    • プラットフォーム特有の部分を専用モジュールとして独立させる(取捨できるようにする)
    • dockerとかも良さそう

3.24 相互運用性

他のソフトウェアとの連携

ソフトウェアは相互で接続出来ることがが求められる。

設計時にソフトウェアで使用する「プロトコル」「データ形式」などの規格を、業界の標準規格に合わせること - 将来に渡って利用し続けやすくなる - 標準的な規格のため、より多くのソフトウェアと連携できる可能性が高い

万が一既存のソフトウェアに合わせて設計しなければならない場合は、既存の方式と標準的な方式を仲介するようなソフトウェアの導入を検討すべき。

3.25 効率性

リソースをうまく使う

効率性:ソフトウェアが実行に伴うリソース使用において、適切な性能を引き出す能力(コスパ的な)

時間効率性

  • 時間という観点から使用効率を定義
  • スループット:一定の時間内に何件の処理を負えられるか
  • レスポンスタイム:ユーザーの入力操作から応答までにかかる時間
  • ターンアラウンドタイム:ユーザーの作業開始時から求められた情報の出力を終了するまでの時間

資源効率性

  • コンピュータ資源という観点から使用効率を定義
  • CPU使用時間や、メモリ、ストレージ消費量、ネットワーク伝送量などで計測
リソースは限られている

限られたリソースを、ソフトウェアは効率よく有効活用しなければならない。 リソースが適切に扱われていないと、ソフトウェアの動作が遅くなるなど使い勝手が悪くなる。

そのためには、アーキテクチャ設計の段階でモジュールへの責務分散と、モジュール間結合を適切に行う必要がある。

リソースは積極的に活用する

リソースの節約も時には大切だが、使えるものは積極的に使い、最大限に効果を発揮させるべき。

関節化と効率性のバランス

関節化:モジュール間の直接の結合を避けるために、仲介する「媒介モジュール」を導入すること

関節化によって、疎結合性の維持や保守性を高めることが出来る。 その反面、処理が冗長になるため、効率性を維持しつつ、どのレベルで関節化を行うか検討していかなければならない。

3.26 信頼性

機能を維持する能力

信頼性:ソフトウェアが例外的な場面や予期しない方法、不正な方法で使用された場合でも、機能を維持する能力のこと

フォールトトレランス

  • ソフトウェアに障害が発生しても、正常な動作を保ち続ける能力
  • 例外の発生に対し正しい振る舞いを保証しつつ、内部修復を行う

ロバストネス

  • 不正や入力ミスなどから、ソフトウェアを保護する能力
  • 例外事象を起こした処理については、繰り返しや内部修復を必ずしも要求せず、あらかじめ定義された状態への移行を保証する

ソフトウェアごとに求められる信頼性は変わってくるため、過不足が無いよう、それに合わせた設計を行うのが大切である。

他の信頼性

フェールソフト (優先度)

  • 障害時に提供する機能を絞り込み、重要な機能のみを優先して提供する設計

フェールセーフ (切り捨て)

  • 障害時に障害部分を切り離す設計

フールプルーフ (予防)

  • ユーザが誤った操作を行なっても安全に稼働させる設計

3.27 テスト容易性

テストを効果的に行う能力

テスト容易性:ソフトウェアに対し、「効果的」かつ「効率的」にテストをする能力

効果的なテスト

  • ソフトウェアの隅々まで漏らさず行われ、その品質を検証できること

効率的なテスト

  • テストのコストや労力が少なく、コスパよく品質を検証できること
テストの品質は本体の品質に直結する

規模の大きいソフトウェアは、テストも困難で高コストになってくるため、テストを容易にするアーキテクチャが求められる。

また、本体そのものをテストを前提に設計することで、テストをしやすくなる→品質を高められるというメリットが生まれる。 そのためには、極力依存関係を排除し、小さい単位でテストをより容易にする必要がある。

3.28 再利用性

再利用性:ソフトウェアの全体や一部で、別のソフトウェアの開発に再利用できる能力

再利用するソフトウェア開発

  • 既存のソフトウェア開発で得た成果物を、現在開発中のソフトウェアに適切な形で結合する
  • アーキテクチャの構成を、既存の構造やモジュールにプラグインできるようにする

再利用されるソフトウェア開発

  • 将来のプロジェクトで再利用できるモジュールを、現在開発中のソフトウェアで創出すること
  • 開発中のソフトウェアから自己充足的な部分を取り出せるアーキテクチャにする
できるだけ「作らない」で開発効率化

ソフトウェア開発の効率と品質を維持するには、なるべく作らずに、どこかから借りてくるのが有効。 それにあたって、実績のあるモジュールを利用することで、新規開発を行うソフトウェアの品質が向上する。

3.29 7つの設計原理

コード妥当性レビュー観点

障害を作り込まないために考慮すべき、7つの設計原理がある。

3.30 単純原理

シンプル

一貫して単純なコードを書くようにする。 なるべく高級テクニックを使わずに、単純なやり方で進めること。

3.31 同型原理

形にこだわる

コードに一貫性を持たせる。 自分のエゴで独創的で複雑なコードを記述しても意味は無い。

3.32 対称原理

形の対称性

一つの条件に対し、反条件の提示を行う(例えば、登録機能に対する削除機能) また、コードから例外的状況をできるだけ排除すること。

3.33 階層原理

階層ごとに処理を決め、同じ種類の処理が別の階層に渡らないようにすることで、階層単位で抽象的に機能を理解することが出来る。

3.34 線形原理

処理の流れは直線に
  • 処理の流れを、なるべく上から下へ一直線にすることで可読性を上げる(分岐を減らす)
  • 障害は、特に複雑な条件分岐や繰り返し文で発生しやすい

3.35 明証原理

ロジックの明証性
  • ロジックを明確に証明するようにする
  • 少しでも不明瞭なところがあったら、コメントなりドキュメントなりで確実に明証しておく
  • トリッキーなコードを書かない(自然なコードにする)
  • コードを再利用や借りてくる場合は、そのコードを完全に理解した上で行う

3.36 安全原理

安全性
  • 必然性のないところや曖昧なところは、安全サイドで設計・プログラミングをしておくこと
  • 絶対起こり得ないということも考慮する

書籍 プリンシプルオブプログラミング 第3章 上 まとめ

3章 プログラミングセオリー 〜 アーキテクチャ根底技法

思想 ~プログラミングのイデオロギー~

プログラミングセオリー

最高のコード:拡張しやすい、余分な要素が存在しない、読みやすい、理解しやすい

価値観を技術の選択基準に

プログラミングにおける問題解決は、その状況に応じて適切な技術を選択する必要がある。 下記の価値観は、個別技術を適用する際の理由になる。

  • コミュニケーション
  • シンプル
  • 柔軟性
フォース

フォース:技術を適用する際に考慮する観点

  • 要件:解決策が満たす必要のある
  • 制約:課題に含まれている
  • 特性:解決策に望まれる

これらを考慮した上で、解決策を選択していく。

意識すること

言語・ツール・技術・問題領域などといった、利用するモノを理解した上で作業に取り掛かるべき

-「何のために?」という本質を理解しておくことで、初歩的なミスや品質的な問題を減らすことが出来る

価値をプログラミングに繋げる原則

3.2 コミュニケーション

コードは記述している時間よりも読んでいる時間の方が圧倒的に多い。 読みやすいコードを記述することで、コードを読む時間を減らせるため、プログラマ同士のコミュニケーションをより円滑にできる。

意識すること

コードを書く際に「このコードを誰かが見たらどう感じるだろう?」と考えながら記述する

3.4 シンプル

コードの複雑性を排除する

コード内の「余分な複雑性」には一切の価値はなく、ただの禍根でしかないため、極力排除すべきである

コードの本質性は独立させる

コードの本質的なところと余分なところを混ぜ込まないようにする

コミュニケーションとシンプルはどっちが優先?

シンプルとコミュニケーションの両立が難しくなった際は、多少冗長でも「コミュニケーション」を優先させる

3.4 柔軟性

コードの変更は必ず行われるので、変更を考慮した「適切に柔軟な設計」を心掛ける

  • 余計な柔軟は複雑化を招くので注意

3.5 結果の局所化

変更の影響を抑える

コードを変更した際の影響を最小限に抑える - 関係ないところに飛び火しない設計

発見、修正が容易になる

結果を局所化することで一部分の検証のみで済ませることができ、余計なコストを減らせる。

3.6 繰り返しの最小化

コードを分割して管理

重複を防ぐためには、コードを小さく分割することが重要。

  • 共通の部分を探し出して関数化

3.7 ロジックとデータの一体化

データと操作は近くに

「ロジック(操作)」と「ロジックで扱うデータ」はなるべく近くに置くこと。

  • コードを読む量を減らせる

3.8 対称性

コードに一貫性を持たせる

同質なコードは、どこで実行されても同じように表現されるよう設計する。

  • 追加メソッドがあれば削除メソッドもある
  • モジュール内のデータの生存期間を統一
  • 抽象化レベルの統一

3.9 宣言型の表現

宣言型を取り入れる

宣言型で無い言語でも、アノテーションDSLなどの宣言型の表現手法を取り入れること。

  • 命令形特有のフローが無くなり、読みやすくなる

3.10 変更頻度

変更理由でグループ化

同じタイミングや同じ理由で変更されるコード同士をグループ化する。

  • 変更によって起こりゆる影響範囲を狭められる

アーキテクチャ根底技法

3.12 抽象

抽象化を行うことで、対象物を以下のように取り扱うことが出来る。

  • 無駄な要素を排除し、本質を捉える
  • 必要な要素のみを残すので、理解しやすくなる
  • 汎用化することで他の場所でも取扱やすくできる

3.13 カプセル化

関連の深いデータや機能を集約してモジュール化することで、関係性を強め無駄な要素が混じるのを防ぐ。 また、以下のようなメリットを得られる。

  • 必要な要素だけになるため、コードが見やすくなる
  • コードの変更が容易になる
  • 変更時の影響をモジュール内で抑える
  • 関数化と同様、機能が独立するため再利用しやすくなる
  • 小さい単位に分割されるため、複雑な問題への対処ができる(小回りが効く)

3.14 情報隠蔽

必要ないものは見せない

モジュール内のデータや機能を外部からアクセスできないようにする。

  • 無駄な関係を作らないことで、モジュールをシンプルにでき、変更などによる影響を最小限に留めることができる。

3.15 パッケージ化

モジュール同士をグループ化

モジュール自体をまとめる(パッケージ化)ことで、機能をモジュール化したときのようなメリットを得られる。

ボトムアップで設計

トップダウンだと正確に決めることが困難なため、ボトムで必要なパッケージ化を行う (上だと詳細なことを把握できない)

3.16 関心の分離

関心ごとにコードを分離

大きな機能や目的(関心)ごとに機能を他の機能から分離することで、関心ごとに独立して操作を行える。

3.17 充足性、完全性、プリミティブ性

モジュールが担っている抽象の表現において、下記の要素全てが成立していなければならない。

充足性

  • 機能が十分存在しているか
  • 一貫性を持っているか

完全性

  • 全ての特徴を完全に備えているか
  • 必要なものを表現できない状況であってはならない

プリミティブ性

  • 必要なものだけで純粋であるか
  • 無駄なものが無い状況

3.18 ポリシーと実装の分離

「ポリシー」と「実装」は混ぜない

ポリシーモジュール

  • ソフトウェアの前提に依存する、ビジネスロジックやその他のモジュールに対する引数の選択を行う部分
  • 特定のソフトウェアに特化している
    • ソフトウェア側で変更が生じると、モジュール側でも変更を強いられる

実装モジュール

  • ソフトウェアの前提に依存しない、独立したロジック
  • 特定のソフトウェアに依存しない「純粋」なモジュール
    • 他のソフトウェアでも再利用可能

それぞれを把握、意識して設計し、別モジュールに分けてコーディングをする。 モジュールの分離が不可能な場合は、モジュール内に明記しておく。

3.19 インタフェースと実装の分離

構成はインタフェースと実装から

インタフェースパート

  • モジュールが持つ機能を定義し、使用方法を定める部分

実装パート

  • モジュールが持つ機能を実現しているコードの部分
  • 内部で使用するロジックとデータが含まれる

インタフェースと実装を分離させること

  • 使用者が実装部を理解していなくても、インタフェースを利用できる (シンプル)
インタフェースを用いて設計
  • 実装ではなく、インターフェースを意識して設計する
  • モジュール間での呼び出しも、実装を直接叩くのではなくインタフェースを経由して呼び出す
    • 実装側も、インタフェースからの呼び出しのみに対応する

3.20 参照の一点性

定義は一回だけ

モジュールの要素の定義は、最初の一回のみにする(後から変更しない)

  • モジュールから得られる結果が、いつどこで取っても変わらないようにする
  • コロコロ変わる変数を広域で利用しない
単一代入

基本的に変数への再代入を行わない。 なるべく定数を利用したり、Javaでいう「final」を定義して再代入を制限すること。

3.21 分割統治

大きな問題を小さく割る

解決が難しい大きな問題は、小さな問題に分割して各個撃破していく

  • 問題を小さくすると、一つ一つの問題が解決が容易になるため

問題が発生してから分割するのではなく、あらかじめ制御が容易になるくらいの規模に分割しておく。

書籍 プリンシプルオブプログラミング 第2章 まとめ

原則 ~プログラミングのガイドライン~

2.1 KISS

KISS:シンプルに保つ

コードを書く際は、「単純性」「簡潔性」を最優先にすべき

コードの可読性を高めることによって、修正時の読み返し時間を短縮させられる。 また、プログラマ同士「コードを見れば分かる」という状況を作り出せることで開発速度を保てる。

シンプルにする上でやってはいけないこと

新しく覚えた技術を強引に使用

  • コードは頭の良さやテクニックをアピールする場所ではない。あくまでシンプルに動作させることに重点を置く

将来必要になりそうなコード

  • 現在必要な分だけを記述する。将来必要だと思っても不必要になることもある(結果的に無駄)

勝手に要件を加える

  • 要件を決めるのは「ユーザ」、プログラマは余計なことをしてリスクを増やさない。
KISSの適用範囲

内部のコードだけでなく、GUIや機能などの外部インターフェースもシンプルに保つことは、使いやすいソフトウェアの誕生に貢献する。

意識すべきこと
  • 今自分の書いているコードは本当に必要なのか
  • もっとも単純な物が正しい

2.2 DRY

DRY:繰り返さない

コードのコピペ厳禁

同じコードが複数の場所にばらまかれてしまうため、以下のような作業をするのが困難になる

読み返し

  • 単純にコードの量が多くなるため、無駄に時間がかかる

修正

  • 同じ修正を複数の箇所を行うため、手間や見落としが発生する可能性がある

テスト

  • 重複しているコードは、大抵の場合レガシーコードであるためテストが無く、不具合が発生する可能性がある
対処法
コードを抽象化する

共通の処理をメソッドやモジュール化することで修正を一括で行なったり、データに名前のある定数を定義して扱いやすくするといったこと

  • コードの量を減らせる
  • ロジックやデータに名前(定数)が付いているため、コードを直感的に読める
  • コードの修正を一箇所で行えるため、品質の向上や開発時間を短縮できる
  • 継承などを行うことでクラスを再利用できる(作り直すための時間を減らせる)

現在のコードをこういった形に変更するのは時間もかかるし面倒な作業だが、長い目で見ると後々有利になってくるのでなるべく行うべき。

DRYの適用範囲

コーディングのときだけでなく、ソフトウェア開発のときにも繰り返しは割けるべきである。 例えば、プログラムの動作テストを自動化することで、人が行なっていた時の「手間」「ミス」「時間」といった問題点を全て解消できる。

DRYとプログラミング言語

プログラミング技術やその設計手法の多くは、重複を排除することを目的に含んでいる。 また、技術や手法を学ぶ際は、やり方だけなく「何故こうしているのか」といった目的にフォーカスすることで理解を早められる。

やむを得ないDRY違反

異なる抽象化スタイルの境界で同じ情報の重複が起きてしまう場合もある。 そういった場合は、一箇所に情報を全て集約した上で、そこから他の情報を動的に生成する仕組みにするといった工夫が必要になる。

DRYに関連する言葉

WET

  • DRYの対義語

OFOP

  • DB設計における「一つの情報は一つの場所へ」という原則を示す。同じ情報が複数のテーブルやフィールドに存在していると冗長なため正規化が必須。

OAOO

  • DRYと同じ意味だがプログラミングのみに適用される。「コードの重複」「不必要なコード」を排除することを強調。

レガシーコード

  • スパゲッティコード的なものだけでなく、テストの無いコードに対しても言われる。(仮にどんなに綺麗なコードでも)
  • コードに対しては必ずテストを行うべきで、テストの無いコードを見つけたらまずテストをすべき

2.3 YAGNI

YAGNI:それはきっと必要にならない

コードは必要最低限にする

緊急時に備えて機能を用意しても大抵利用されず実装時間の無駄なため、コードの機能は必要最低限に抑えること。

(おまけに可読性が下がり保守に時間がかかるため、もはや邪魔)

コードは今必要なものだけに抑える

まず最低限の機能だけを実装する(汎用性よりも単純性)

これはコードだけでなく、開発全体で適用される。

DTSTTCPW

「上手くいく手段のうち、一番シンプルな手段を選べ」

「まず単純ことをして、変更が必要なら追加コストを払う」が理想。複雑なことは必要なときに実装すればいい。

先のことをあまり考えすぎない、必要最低限の機能を常に念頭に置くべきである。

2.4 PIE

PIE:意図を表現してプログラミングせよ

コードの意図を伝える

コードは人が読むものということをを第一に考え、分かりやすく書く。 (ソフトウェアの仕様を完全に把握するには、仕様書以上にコードを読む必要があるから)

読みやすさが最優先

コードは書かれる機会よりも読まれる機会の方が圧倒的に多いため、書くのに多少手間がかかっても読みやすい方が全体の効率は上がる。

モグラたたき開発は避ける

モグラたたきのように、あちこちにバグが潜むようなコードを書かないようにする。 多少時間がかかっても、可読性や品質が高く障害の無いコードを書いた方が長期的な利益に繋がる。

コメントを書くこと

コードだけでは「何故その処理をしているのか」などの伝えられないことがあるため、コメントは積極的に活用してコミュニケーションを取る。

文芸的プログラミング

コードとドキュメントを編み込んで記述する方法 (コンパイラに通すコードとしても、人が読むドキュメントとしても扱えるようにする)

特徴

  • コードの説明と正当性を記述しながらプログラミングを進めるため、コードについて客観的に考えられる。

  • コードと説明が近接した位置に記述されるため、修正がしやすい

  • コードベース全体でドキュメントの一意性を保証できる

  • アルゴリズムの説明や設計上の決定の根拠などといった、通常のコメントでは解説しきれないこともコードに記述される

2.5 SLAP

SLAP:抽象レベルの統一

コードのレベルを合わせる

機能(関数)を抽象化する際に、同じ階層にある機能同士の抽象化レベルを統一することで、書籍の目次のように各機能を参照しやすくなる。 また、関数だけでなくモジュールにも適用できる。

2.6 OCP

OCP:開放・閉鎖の原則

コードの変更を波及させない

「拡張に対し開放的」と「修正に対し閉鎖的」の2つを満たせるようコードを設計することで、既存のコードに影響を与えずに機能の拡張を行える。 ある程度変更が行われそうな部分に適用すべき。

2.7 名前重要

命名は最重要課題

クラスや関数に適切な命名が行われていないということは、プログラマがその要素を理解できていないことの裏付け。

コードは読む人のUI

関数名やクラス名から機能を推測出来ない場合、内部の構造を確認しなければならない。 さらにそれを保守の度に行わなければならないため大きな負債となる。

意識すべきこと
  • 名前は「短いコメント」だと考えて、伝えるべき情報をしっかり含むこと

  • 他の名前と誤解されないように一貫性を持たせる

  • 「効果」「目的」を説明する

  • 現実の会話で発音できるものにする

書籍 プリンシプルオブプログラミング 第1章 まとめ

前提 ~プログラミングの変わらぬ真実~

1.1 プログラミングに銀の弾丸はない

ソフトウェアは本質的に困難

プログラミングの成果物である「ソフトウェア」は、本質的に以下の4つの性質を持っている。

  • 複雑性

ソフトウェアの構成は、規模が大きくなるにつれて非線形に増大していく

  • 同調性

ハードウェアやネットワーク、他のソフトウェアなどと同調し続けなければならない

  • 可変性

ユーザの要求に対し、変化をし続ける必要がある

  • 不可視性

ソフトウェアは概念の集積であり、実物やプロセスを見ることはできない。見れるのはせいぜい抽象化された図面くらい

これらは本質的に取り除けないものであり、すべてを確実に解決するような特効薬は存在しない

ソフトウェアの偶有部分の改善

偶有とは、付随的でそれがなくても物事が成り立つという性質のことであり、ソフトウェア開発における、ビルド環境、言語、ライブラリ、フレームワークなどといったことである。 例えば、フレームワークは無くても開発は出来るが、利用できれば開発効率の向上が期待できる。

偶有的な部分を最適化したり作業を自動化することで、より多くのリソースを本質的なことに割ける。

1.2 コードは設計図である

ソフトウェア開発においては、上流から下流の全ての作業が『設計』であり、そのアウトプットが『コード(設計図)』になる。 (UMLやCASEツールはあくまで補助)

そのためプログラミングによってコードが完成しなければ、それより上位の設計も確定されない。

コードを早めに書き始めることで、設計の明確化して早めに設計を終わらせることが出来る。

保守においては、ソフトウェアの開発環境やアーキテクチャを理解するための『ロゼッタストーン』というドキュメントが重要になる。

主にビルドやテストのプロセス、コードからは読み取れない全体像などを記述することで、後任の保守担当者が躓きにくくなる。

これに「何故この仕様にしたか」といった記述をすることで、保守担当者の修正材料として役に立つ。

1.3 コードは必ず変更される

コードは修正や機能の拡張などによって必ず修正や変更が行われるため、変更されることを前提にコードを作成すべきである。

そのため、読みやすいコードを記述することがが最も重要 (変更時に読む時間を短縮できる)

Webを支える技術 第5部 まとめ

Webを支える技術 第5部 まとめ

Webサービスの設計

WebサービスやWeb APIの設計では、HTTPやRESTだけなく様々な知識が必要になってくる。

第15章 読み取り専用のWebサービスの設計

WebサービスAPIの設計には、システム全体の設計や内部クラス、DBといったような広範囲に渡る設計が必要になってくる。

リソース設計とは

その中でもリソース設計とは、以下のようなクライアント-サーバ間のインターフェースの設計を指す。(URI命名、リソースの分割など)

尚リソースを設計する際には、「人間向けののWebサービス」と「プログラム向けのWeb API」を分けずに設計することが重要になってくる。(分けて設計すると機能が違ったものになりがち)

リソース指向アーキテクチャのアプローチ

以下は「リソース指向アーキテクチャ」に基づいた開発アプローチである。

  1. Webサービスで提供するデータを特定する
  2. データをリソースに分ける
  3. リソースにURIで名前を付ける
  4. クライアントに提供するリソースの表現を設計する
  5. リンクのフォームを利用してリソース同士を結び付ける
  6. イベントの標準的なコースを検討する
  7. エラーについて検討する

1. Webサービスで提供するデータを特定する

リソース設計の最初の工程であり、サービスで提供するデータを特定する作業。

2. データをリソースに分ける

1で特定したデータをリソースとして分割する作業。 何をもってリソースとするか、リソースをどのような単位で分割するかなど非常に重要な取り決めを行う工程である。 また、最適なリソース分割を行うため、この工程を何回も繰り返すのが一般的である。

3. リソースにURIで名前を付ける

リソースにURI命名する作業。 WebサービスAPIを利用する相手にとって、なるべく使いやすいURIを検討することが重要である。

4. クライアントに提供するリソースの表現を設計する

リソースの表現形式を設計する作業。 表現形式には以下のようなものが挙げられる。

各表現形式には得意不得意があり、1つのリソースが複数の表現形式に対応していると便利。

5. リンクのフォームを利用してリソース同士を結び付ける

1~4で設計してきたリソース同士をリンクで接続する。 通常のWebページでは、トップページへのリンクやパンくずリスト、グローバルナビゲーションを用意しておくことで、クライアントが他のリソースに移動しやすく設計されていることが多い。

6. イベントの標準的なコースを検討する

利用者がどのようなフローで利用するか検討する。

7. エラーについて検討する

利用時に起こりうるエラーの検討。

第16章 書き込み可能なWebサービスの設計

最近のWebサービスは、何らかの形で書き込み機能があるものがほとんどである。 書き込み機能を実装する場合、読み取り専用のサービスと比較して、より多くの処理を考えなければならない。

リソースの作成

リソースの作成にはHTTPの「POST」もしくは「PUT」を使う方法があり、一般的にはPOSTを使う方が無難。

リソースの更新

リソースの更新に置いては「PUT」で行う。但し、バッチ処理を行う際には「POST」を利用する。

リソースの削除

削除したいリソースのURIに「DELETE」を送ることで削除できる。 削除設計においての注意点は、親子関係にあるリソースの削除についてである。一般的には親リソースを削除した場合、付随して子リソースも削除すべき。

バッチ処理

処理を大量に行う場合、一つ一つリクエストを送信していてはサーバに負荷がかかってしまう。これはリクエストを一括して送信(バッチ処理)する機能を実装することで解決できる。(POSTで送信)

トランザクション

トランザクションは複数のリソースにまたがって行う処理を一つの処理として扱うことで、「一つでも処理に失敗したら全て失敗」というような一貫性を保証する。

排他制御

複数のクライアントが一つのリソースを変更しようとした際に、競合を起こさないよう一つのクライアントのみが変更できるように制御する処理。大きく分けて「悲観的ロック」と「楽観的ロック」の二つがある。

  • 悲観的ロック

    • ユーザをあまり信用せず、絶対に競合が発生しないように制御する方法
  • 楽観的ロック

    • 悲観的ロックに対して、競合が起きたときにのみ排他制御をする方法

設計のバランス

  • なるべくシンプルに保つ

    • 設計が複雑になったり無駄な機能が増えてきた場合、一段階メタな視点で全体を考え直すこと。
    • 全体をシンプルに保つことは、設計のバランスにおいて最も重要
  • 困ったらリソースに戻って考える

    • HTTPメソッドで実装出来ない機能があると感じたら、独立した別リソースで代替できないか検討する
  • 本当に必要ならPOSTで何でも出来る

    • PUTの方が処理が簡単になるとしても、バッチ処理などで複数のリソースが対象になった時点で、POSTにシフトした方が賢明である。

第17章 リソースの設計

リソース指向アーキテクチャの落とし穴

第15章の「リソース指向アーキテクチャ」にある

1.Webサービスで提供するデータを特定 2.データをリソースに分ける

について何を基準にこれらを行っていくのかが曖昧である。

この基準を明確にするため、既存の設計手法で得られる以下の成果物を元にリソースを設計する。

関係モデルからの導出

関係モデルはRDBMSの基礎となっているデータモデルで、データの正規化の手法が確立されているため、効率的なDB設計を行える。

オブジェクト指向モデルからの導出

オブジェクト指向設計の成果物であるクラス図やシーケンス図を使い、リソースの導出を検討する。

情報アーキテクチャからの導出

情報アーキテクチャは、知識やデータの組織化を意味し「情報を分かりやすく伝える」「受け手が情報を探しやすくする」といったことを実現するための表現技術

難易度は高いがWebデザインにおける

  • ユーザの操作
  • ページ遷移
  • ネットワーク通信
  • アプリケーションの実行

などといった複雑なデザインをリソースの設計と共に整理して、ユーザビリティを高めることが可能である。

リソース設計で最も重要なこと

リソース設計する際に、WebサービスとWeb APIを分けて考えないことが最も重要である

※参考

・Webを支える技術 https://www.amazon.co.jp/dp/4774142042/ref=cm_sw_em_r_mt_dp_U_LiIUEbQ3NJSJH