書籍 プリンシプルオブプログラミング 第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 安全原理

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