ソフトウェア開発プロセス

ソフトウェア開発プロセス
ソフトウェアを作るための方法論であり、組織化された手順のこと。
目的:信頼性や品質が高く、メンテナンスが容易なソフトウェアを作成すること
このプロセスは複数の段階から成り立っており、それぞれの段階は、プロジェクトに関わる人々の要望や要求を満たすことを確保するために存在している。
※https://recursionist.io/dashboard/course/32/lesson/1114 より画像引用
1. 要求の収集と分析
ソフトウェアが何をする必要があるかを特定し、定義することが主な目的。
これには、ソフトウェアを使用する人や利害関係者が何を必要としているかを把握し、ソフトウェアの目的を理解し、ソフトウェアが満たさなければならない特定の要件を定義することが含まれる。
これらの要件には、
・ソフトウェアができること
・動作方法
・外観
などが含まれます。
このフェーズでは、
・機能要件(ソフトウェアが何をすべきか)
・非機能要件(ソフトウェアがどの程度それを行なうべきか)
の両方を特定することが重要。
2. 設計
ソフトウェアがどのように構成され、設計されるか計画を作成
この計画は、前フェーズで特定された要件に基づいている。これにはダイアグラム、フローチャート、スケッチ、UMLなどのツールや、技術的な詳細、テストプラン等が含まれる。
このフェーズの目的は、ソフトウェアの構築を開始する前に、慎重に計画を立て、モデル化すること。これにより、潜在的な問題やトレードオフを事前に特定し、対処することが可能になる。こうすることで、ソフトウェアが適切に設計され、必要な要件をすべて満たしていることを確認することができる。
3. 実装
設計仕様に基づき、要件を満たし、テストに合格しなければならないソフトウェアのコードを記述する。
4. テスト
ソフトウェアが機能的および非機能的な要件を満たしていることを確認するためにテストを実行する。
単体テスト、結合テスト、システムテストの実施が含まれる。
5. デプロイメント
開発されたソフトウェアを、使用される環境で利用可能にするプロセス。
ユーザーのコンピュータにインストールしたり、サーバに置いたり、ウェブアプリケーションからアクセスできるようにしたりすること。
簡単に言えば、完成した製品をエンドユーザーが使えるようにすること。
6. メンテナンス
ソフトウェアが関係者のニーズを満たしていることを確認するために、定期的にチェックとアップデートが行われる。これには、バグの修正、ユーザーからのフィードバックへの対応、ソフトウェアの改良が含まれる。
ソフトウェアエンジニアリングのプロセスは継続的かつ反復的なものであり、ソフトウェアが要件を満たし、高品質であることを確認するために、各フェーズを何度も見直される。このプロセスは、適用される領域、規模、複雑さ、厳密さに関係なく、あらゆるソフトウェアに適用することができる。
ソフトウェア開発における一般的なアプローチ
ウォーターフォール設計
最初に厳しい要件を設定し、詳細設計を作成し、それを正確に実装する必要がある。
アジャイル開発
初期の設計やプロトタイプとともに初期の要件を設定し、要件の変更や新たな知見の獲得に応じて繰り返しソフトウェアを構築していくことになる。
ソフトウェア開発では、タスクを開発しやすい小さなパーツに分割し、それらを組み合わせてソフトウェア全体を作り上げていくのが一般的。このピースには、機能、要件、モジュール、ライブラリ、個々の機能などがあり、それぞれに対して同じ開発プロセスを適用することができる。
開発プロセスの各フェーズにおける詳細度は、タスクや時間的な制約によって変わる可能性がある。
しかし、このプロセスに従うことで、エンジニアは要件を満たし、十分な品質を持つソフトウェアをタイムリーに提供することができる。

アジャイル開発
※https://www.kagoya.jp/howto/it-glossary/develop/what-is-agile/ より画像引用
ソフトウェアを小さく管理可能な段階に分けて構築し、最終製品が完成するまで繰り返す手法。
プロセスは段階に分けられ、各段階では、計画、設計、コーディング、テスト、機能的なソフトウェアの提供など、ソフトウェア開発の特定の側面に焦点を当てる。
このアプローチでは、開発チームは最後まで変更を待つのではなく、プロセス全体を通してソフトウェアを調整し、改良することができるため、柔軟性と適応性がある。
チームワークと顧客からのフィードバックを重視したソフトウェア開発の方法
多くのソフトウェアプロジェクトが直面する共通の問題を解決するのに役立つ作業方法
目標は、プロジェクト全体が完了するまで待つのではなく、ソフトウェアの作業バージョンを頻繁にリリースすることによって、顧客に価値を提供すること。
アジャイル開発の主な原則
顧客満足
顧客のニーズと要求を満たす製品を提供することに主眼を置く。
変化の受け入れ
要件や優先順位の変更に柔軟に対応できるように設計されている。
実用的なソフトウェアの提供
各イテレーションの終了時に、テストと評価が可能な実用的な製品を提供することを目標とする。
コラボレーションとコミュニケーション
開発チーム、利害関係者、顧客が密接に連携し、プロジェクトが計画通りに進行しているか、全員が同じ考えを持っているかを確認する。
フィードバックへの対応
開発チームは、顧客や利害関係者からのフィードバックをもとに、ソフトウェアを改善し、必要に応じて変更を加える。
アジャイル開発における一般的なプラクティス
スプリント計画
チームは、通常 1 週間から 4 週間の各反復(スプリント)において行なう作業を計画する。
スタンドアップミーティング
チームは定期的にミーティングを開き、進捗状況を話し合い、問題を特定して解決し、次の日の計画を立てる。
継続的な統合とテスト
コードの変更を頻繁に統合し、ソフトウェアが期待通りに動作することを確認するために継続的にテストを行なう。
レトロスペクティブ
各反復の終わりに、チームは進捗状況をレビューし、何がうまくいき、何がうまくいかなかったかを特定し、次のスプリントに向けて調整を行なう。
アジャイル開発手法は、多くのソフトウェアプロジェクトが直面する共通の問題を解決するのに役立つ作業方法
具体的な問題
問題 1
プロジェクトの始めに、顧客がソフトウェアに何を求めているかを正確に知ることは極めて困難。
後で気が変わったり、違うものを欲しがったり、優先順位や条件が常に変化する可能性がある。
問題 2
ソフトウェアをどのように設計し、構築するか、事前に正確に計画するのは困難。
いろいろと試してみて、何がうまくいくかを確認しながら進める方が良い場合もある。
問題 3
ソフトウェアを設計し、構築し、テストする過程で、予期せぬ問題や変更が生じることはよくあること。
アジャイル開発手法は、こうした変更に柔軟に対応できるように設計されている。
アジャイル開発手法では、ソフトウェアを小分けにしてリリースするため、チームは顧客からフィードバックを得て、必要に応じて変更を加えることができる。
これにより、ソフトウェアが顧客のニーズを満たし、高品質であることを保証することができる。

一般的なアジャイルフレームワーク
仕事をより小さく、より管理しやすいパーツに分解して整理する方法。
これは、作業をマイルストーン、スプリント、タスクに分割することで行われる。
マイルストーン
マイルストーンは、プロジェクトのタイムラインにおける大きなイベントであり、主要な目標や成果物の完成を意味する。
通常、1 ヶ月から 3 ヶ月で達成される。
スプリント
スプリントは、通常 2 週間から 4 週間の小さな仕事の塊。
スプリントでは、マイルストーンの達成に必要な一連のタスクや目標を達成するために、チームは協力し合って作業を行なう。
タスク
タスクは、より大きな目標を達成するために必要な個々の作業単位。
通常、1 日から 7 日で完了する。
チームが要件を明確に理解することで、達成すべき重要なマイルストーンを計画することができる。
当初は具体的なマイルストーンを設定するが、プロジェクトが進むにつれて、予期せぬ変更や新しい発見があった場合に対応できるよう、より柔軟なマイルストーンを設定することもできる。
マイルストーンが設定されると、チームはそれをスプリントと呼ばれる達成可能な小さなタスクに分解することができる。
スプリントは通常 2 - 4 週間で完了し、チームはその期間内に達成する必要のある項目のリストを作成する必要がある。各項目はソフトウェア全体の要件であり、スプリント中に構造化されたプロセスを使ってアプローチされる。
チームは、リスト上のすべての項目を完了し、達成しようとしたマイルストーンを達成するまで、一連のスプリントを通じて反復的に作業する。このプロセスにより、ソフトウェア開発が計画的かつ効率的に行われるようになる。
スプリントプランニングでは、そのスプリントの ToDo リストにある項目を完了させるために必要なタスクのリストを作成する。
そして、毎日または毎週、定期的にミーティングを開き、これらのタスクの進捗を確認し、問題があれば解決し、潜在的な問題を早期に発見する。
スプリントの終わりには、チームと利害関係者が一堂に会し、すべてのタスクが成功裏に完了したかどうかを評価する最終ミーティングを開催する。このミーティングは、完成したソフトウェアのデモンストレーションも兼ねている。
最後に、各スプリント終了後に、チームは、何がうまくいき、何が改善され、次のスプリントに何をコミットすべきかを振り返るために、振り返りのミーティングを行なう。
このように作業を細分化し、協力して目標を達成するプロセスが、一般的なアジャイルフレームワークの基礎となっている。
スクラムの開発サイクル
※https://www.kogasoftware.com/solution/system_development/development_style/agile_scrum/ より画像引用

ソフトウェア要件
ソフトウェアの全体または一部について考える場合、要件を明確にし、それを満たすために実装される設計を作成することが重要。ソフトウェア開発プロセスでは、問題を理解し、解決策を計画し、計画を実行し、結果を正確性の観点で検証する必要がある。
要件(requirement)
ソフトウェアシステムや製品が何をすべきかを記述したもので、ユーザーや利害関係者のニーズを満たすためにシステムや製品が提供しなければならない特徴や機能、能力などが含まれる。
要件は、機能要件、非機能要件、設計要件など、さまざまな種類に分類することができる。
機能要件(functional requirement)「何をするか」
システムが行なうべきことや実行できる具体的なアクションを説明する。
例えば、ユーザーが記録を作成、編集、または削除したり、レポートを生成したりできることなど。
非機能要件(non-functional requirement)「どのようにするか」
システムがどれだけ優れたパフォーマンスを発揮すべきかや品質に関する属性を説明する。
セキュリティ、信頼性、使いやすさ、パフォーマンス、拡張性などがこれに該当する。
要件の明確化は大切。美しくても、間違った課題に答えるソフトウェアは望ましくありません。要件をしっかり決めることで、ソフトウェアの期待値やユーザーとの関わり方、そしてその影響を理解できる。これにより、目的に合ったソフトウェアを作成できる。
要件を作成する際に従うべきテンプレートの例
背景
・プロジェクトがどのように始まったか
・どのようにビジネスニーズやサービスが特定されるに至ったか
を説明する。
目的
・システムの目的は何か
・何が達成されるのか
・製品がどのように使用されるのか
を説明する。
機能要件仕様書
システムが持つ機能要件を列挙したもの。
非機能要件仕様書
システムが持つ非機能要件のリスト。
プロジェクトを始めるとき、全ての要件を完璧に詳しくまとめるのは難しい。プロジェクトが進むにつれて、関係者の要望も明らかになり、要件も変わったり新しくなることがある。要件はただの固定的なルールと思わず、プロジェクトのゴールを示す指針として使いましょう。

要件モデル
ソフトウェアの目的や機能、操作方法が定まったら、次はソフトウェアの設計をする段階になる。
この時、ソフトウェアの見た目や動きを詳しく考え、その設計のための模型を作りる。このために、2 種類の図を描く必要がある。
① ワイヤーフレーム(WF)
ワイヤーフレームは、ソフトウェアやアプリの画面レイアウトや機能の初期デザインを表すもの。これにより、デザイナーや開発者は、ソフトウェアの構造や流れをイメージしやすくなる。
※https://tsunaweb.book.mynavi.jp/tsunaweb/tsuna/detail/id=3797 より画像引用
ワイヤーフレームは手書きのスケッチやホワイトボード、デジタルツールなどで作成できる。
主に、ボタンやメニュー、テキストフィールドといったUIの要素を基本的な図形や線で示す。
さらに、機能の詳細や動きについてのメモや注釈が付けられることもある。
このワイヤーフレームをチームや関係者と共有することで、開発前にデザインの確認や変更がスムーズに行なうことができる。
② 要件モデル
要件をソフトウェア設計の形に落とし込むための図。
この図はソフトウェア計画の初めての技術的な描写となり、規模や予算に合わせて、より具体的な部分、例えば機能やクラスのレベルまで詳しく描かれることもある。
この図はソフトウェアの目的を示すものだが、具体的にどう動くのかの全ては表していない。図を見ることで、システム内での相互作用、システムが扱うオブジェクトや、システムの持つ機能や動きがどういったものかが分かるようになっている。
※https://wiki.eclipse.org/Requirements_Model より画像引用
作成する図の例
【ユースケース図】
※https://www.lucidchart.com/pages/ja/uml-use-case-diagram より画像引用
ユースケース図は、システムの主な機能や行動を示す図。
システムの使用者(アクター)と、そのアクターがシステムでできること(ユースケース)がわかりやすく描かれている。
例えば、オンラインショッピングサイトの場合、アクターは「顧客」で、ユースケースは「商品検索」や「商品購入」など。この図を見れば、サイトの機能がすぐに理解できる。
【クラス図】
※https://www.visual-paradigm.com/guide/uml-unified-modeling-language/what-is-class-diagram/ より画像引用
クラス図は、ソフトウェアの各部品を見やすく示す図。
この図には、システム内のクラスやその属性、機能、そしてどのように繋がっているかが描かれている。
これを使うと、開発者はシステムの構造を理解しやすくなり、設計や整理に役立つ。
クラス図はソフトウェアの設計図のようなもの。
【アクティビティ図】
※https://lucid.co/templates/activity-diagram-with-swimlanes-example より画像引用
アクティビティ図とは、システム内でのアクションやタスクの流れを示す視覚的な表現。
システム内で発生するすべてのアクティビティ、選択が行われる決定点、異なるアクティビティをつなぐ遷移が表示される。
【シーケンス図】
※https://softwareengineering.stackexchange.com/questions/434113/drawing-a-sequence-diagram-for-a-view-posts-in-a-section-use-case より画像引用
シーケンス図は、システムのオブジェクト間の相互作用を時間経過とともに説明するもの。
オブジェクト間でメッセージが交換される順序や、システム内で発生するイベントの順序を示すもの。
【状態図】
※https://recursionist.io/dashboard/course/32/lesson/1118 より画像引用
状態図は、オブジェクトの可能な状態や、それらの状態の変わり目にあたるイベント、そしてその変化の際の行動を示す図。これを使うと、オブジェクトがどんな条件でどう変わるかや、システムがどのように動くかを簡単に理解できる。

ソフトウェア設計
ソフトウェアの要件と、見た目や機能のモデルが整ったら、設計の段階に進むことができる。
ソフトウェア設計とは、ソフトウェアシステムやアプリケーションのためのアーキテクチャ、コンポーネント、モジュール、インターフェース、データを定義し、明確にするプロセス。
ソフトウェア設計の目標:開発プロセス中のガイドとして使用できるソフトウェアシステムの構築計画や設計図を作成すること。これにより、望ましい要件と目標を満たすソフトウェアシステムの構築が進められる。
ソフトウェア設計のステップ
※https://recursionist.io/dashboard/course/32/lesson/1119 より画像引用
① 要件分析
システムおよびユーザーの要件と制約を理解する
② アーキテクチャ設計
ソフトウェアシステムとそのサブシステムの全体的な構造を定義する
③ 詳細設計
ソフトウェアシステムの各コンポーネントやモジュールの詳細な機能・動作を定義する
④ データ設計
ソフトウェアシステムが必要とするデータ構造やアルゴリズムを設計する
⑤ ユーザーインターフェース設計
ソフトウェアシステムのユーザーインターフェースとユーザーエクスペリエンスを設計する
⑥ テストと検証
設計が要件と目的を満たしていることを確認するためのテストと検証を行なう

アーキテクチャ
初期のプロトタイプや開発において、最も重要なのは、ソフトウェアのアーキテクチャを考えること。
この段階では、システム全体の全体像について考える。
アーキテクチャが決まれば、コードベース全体はそれを中心に開発されることになる。
アーキテクチャを考える際に重要な要素
【システム要件】
システム要件は、ソフトウェアが何を行ない、どのような機能を持つ必要があるかを説明している。
アーキテクチャは、これらの機能要件、非機能要件を満たすように設計される必要がある。
【スケーラビリティ(拡張性)】
「システムが大きく成長するとき、問題なく対応できるか」というのがスケーラビリティのポイント。
具体的には、将来的にユーザーが増えたり、データの量が増えたりした場合、システムはその増加をスムーズに処理できるかということ。そのための技術や方法として、水平スケーリングなどがある。
1,000 人から 10 万人のユーザーを想定したシステムは、数千万人のユーザー向けのシステムとは大きく異なる。同様に、1 秒あたり 100MB 〜 1GB のデータを処理するシステムは、1 秒あたり数百テラバイトのデータを処理するシステムとも大きく異なる。
【保守性】
アーキテクチャは保守性のために設計される必要がある。つまり、システムに必要な変更やアップデートを容易に行えるようにする必要がある。全体的なシステムに支障をきたさずに変更ができることが求められる。これは全てのシステムに共通する要件であり、そのために一般的なソフトウェアのベストプラクティスが採用される。具体的には、低い結合度、高い凝集度、そして自己文書化されたコードなどが重視される。
【セキュリティ】
ソフトウェアアーキテクチャは、セキュリティを考慮して設計される必要がある。これには、
・攻撃からシステムを保護すること
・データのプライバシーと整合性を確保すること(正確で一貫性のあるデータ、データ損失の防止)
・関連する規制に準拠すること
などが含まれます。システムによってはセキュリティの制約が異なる場合もあるが、すべてのシステムで最低限のセキュリティを考慮する必要がある。
【パフォーマンス】
アーキテクチャは、システムがタイムリーかつ効率的に機能を実行できるように、高いパフォーマンスを発揮できるように設計する必要がある。規模や段階に応じて、開発前に性能要件を設定し、ソフトウェアがスケールするにつれて、システムのスケールに合わせて最小要件のベンチマークとして機能させることができる。
【統合性】
ソフトウェアアーキテクチャは他のシステムやアプリケーションと容易に統合できるように設計する必要があり、システムがエコシステムの他の部分と相互作用できることを保証する必要がある。
【技術スタック】
アーキテクチャに使用する技術スタックは、プラットフォーム、信頼性、保守性、使いやすさなどの要素を考慮し、慎重に選択する必要がある。アーキテクチャは、開発チームを念頭に置いて設計する必要があり、チームのスキルや専門性に合わせて、開発チームが実装できるようにする必要がある。
【開発チーム】
アーキテクチャは、開発チームのスキルと専門知識に合わせて設計される必要がある。
そして、チーム自身が実装できるようにすることが重要。

アーキテクチャスタイル(architecture style)
アーキテクチャースタイルとは、ソフトウェアシステムの構造全体がどのように構成されるべきかを指示する、一般的なガイドラインと原則のセット。このスタイルは、システムのコードベース全体またはその一部に対して一貫して適用される。
ソフトウェアを設計するための様々な知識が蓄積されるにつれて、ソフトウェア業界では、あるパターンが常に現れるようになった。これらのパターンは、建築のスタイルの違いのようなもので、家が倉庫や教会と違って見えるように、ソフトウェアシステムもその目的によって異なるアーキテクチャを持つ。
データ中心型アーキテクチャ(data centered architecture)
データが中心となり、アプリケーション、サービス、インターフェースなどの他のシステムコンポーネントは、データとの連携や操作を目的として設計される。このアプローチにより、データが常にシステムの最も重要な要素であり、他のコンポーネントがデータの管理と使用をサポートするために設計されていることが保証される。
(例)ChatGPT、検索エンジン
※https://www.tutorialride.com/software-engineering/architectural-styles-for-software-design.htm より画像引用

データフローアーキテクチャ(data flow architecture)
システムは独立したコンポーネントの集合体として設計され、各コンポーネントが特定の機能を担当し、データはこれらのコンポーネント間を流る。
データフローアーキテクチャは、リアルタイムシステム、信号処理システム、分散システムなど、大量のデータを処理する必要があるシステムでよく使われます。
このアーキテクチャの主な利点は、必要に応じてコンポーネントを追加または削除することでシステムの拡張が容易になること。
データフローアーキテクチャでは、データの流れを直線的なパイプラインとして表現することが一般的。
データは一つのコンポーネントから次のコンポーネントへと順次渡され、各コンポーネントはデータを変換、処理、分析、または他の操作を行なう。このようなパイプラインの構造により、データの処理が効率化され、柔軟性や拡張性が向上します。
(例)TypeScript トランスパイラ、Web クローラ
※https://docs.hevodata.com/pipelines/data-flow-arch/ より画像引用
※https://recursionist.io/dashboard/course/32/lesson/1121 より画像引用

コール・アンド・リターンアーキテクチャ(call-and-return architecture, CRA)
プログラムは主に手続きや関数と呼ばれる小さな処理単位に分割される。プログラムの実行は、メインのプログラムから手続きを呼び出し(コール)、呼び出された手続きが処理を行った後にメインのプログラムに戻る(リターン)という流れで進む。
このアーキテクチャは、同一システム内の手続き型プログラムや関数呼び出しで使用される場合と、異なるシステム間でのリモートプロシージャコール(RPC)アーキテクチャで使用される場合がある。後者では、ネットワークを介して別のシステムのプロシージャを呼び出し、処理結果を受け取る。
例えば、CLI は、ユーザーがテキストベースのコマンドを入力し、それに対応するプログラムや機能を呼び出すインターフェース。ユーザーがコマンドを入力すると、メインプログラム(シェル)がそれを受け取り、関連するサブプログラム(コマンド)を呼び出す。サブプログラムが処理を完了すると、制御はメインプログラムに戻り、新たなコマンドの入力待ち状態になる。
※https://www.geeksforgeeks.org/software-engineering-architectural-design/ より画像引用

レイヤードアーキテクチャ(layered architecture)
アプリケーションを論理的なレイヤーに分割し、各レイヤーが特定の機能を提供する。通常、レイヤーは階層的に配置され、各レイヤーは下のレイヤーに依存する。レイヤーが上に行くほど、ユーザーに近くなり、レイヤーが下に行くほど、オペレーティングシステムやデータベースに近くなる。
(例)TCP/IP プロトコル

イベント駆動アーキテクチャ(event-driven architecture)
システム内で何かイベント(特定の出来事)が発生した時、そのイベントに関連するソフトウェアの部品同士が、互いに非同期(同時ではなく順不同で)に通信し合って動作する仕組みのこと。イベント駆動アーキテクチャ(event-driven architecture)では、明示的な要求(リクエスト)に頼るのではなく、イベントが起こった時に自動的に対応する。
例えば、ゲームはイベント駆動アーキテクチャを使用することが一般的。
ゲームでは、プレイヤーの入力やキャラクターの動き、衝突やトリガーの発生など、さまざまなイベントが発生する。これらのイベントに応じて、ゲームエンジンやゲーム内のオブジェクトが適切なアクションを実行する。イベント駆動アーキテクチャは、ゲーム開発において柔軟性と拡張性を提供する。ゲーム内の様々な要素(キャラクター、オブジェクト、エフェクトなど)は独立したコンポーネントとして扱われ、イベントを介して相互作用する。これにより、新しい要素を容易に追加したり、既存の要素を修正したりすることができる。
(例)サードパーティとの連携サポート
※https://www.scylladb.com/glossary/event-driven-architecture/ より画像引用

クライアントサーバアーキテクチャ(client-server architecture)
クライアントと呼ばれるアプリケーションがサーバと呼ばれる別のアプリケーションに対してリソースやサービスを要求する。
例えば、ウェブブラウザがクライアントであり、ウェブサーバがサーバとなる。
(例)オンラインゲーム、ソーシャルネットワークサービス
※https://www.jaroeducation.com/blog/client-server-architechture-guide/ より画像引用
※https://www.enjoyalgorithms.com/blog/client-server-architecture より画像引用

サービス指向アーキテクチャ(service-oriented architecture, SOA)
ソフトウェアシステムを独立したサービスに分割し、それらのサービス間の連携と通信を可能にするアーキテクチャのアプローチ。各サービスは、特定の機能を提供し、他のサービスと組み合わせて大規模なシステムを構築するために使用される。
SOA を利用することで、開発者は、開発、保守、更新が容易なソフトウェアシステムを構築することができる。なぜなら、各サービスは、システムの他の部分に影響を与えることなく独立して開発することができるため。これは、すべてを自分でやろうとするのではなく、プロジェクトの特定の部分にそれぞれ集中できる、専門的な労働者のチームを持つようなもの。
(例)クラウドコンピューティングサービス
SOAとマイクロサービス
※https://www.xenonstack.com/insights/service-oriented-architecture-vs-microservices より画像引用

P2P アーキテクチャ(peer-to-peer architecture)
ピア・ツー・ピアアーキテクチャは、ネットワーク上のコンピュータシステムやアプリケーションの間で直接的な相互接続を可能にする分散型のアーキテクチャ。このアーキテクチャでは、コンピュータ(ピア)同士が対等な地位で通信し、リソースや情報を共有できる。
P2P アーキテクチャでは、中央集権的なサーバが必要なく、各ピアが自己組織化されたネットワークを形成する。ピアは同時にクライアントとサーバの役割を果たし、他のピアに対してリソースを提供することができる。データやファイルはピア間で直接転送され、ピアが相互にリクエストや応答を行なう。
(例)ブロックチェーン
※https://systemdesignschool.io/blog/peer-to-peer-architecture より画像引用

システムの全体像
※https://appkitbox.com/knowledge/test/20121112-367511 より画像引用
サブシステム(Subsystem)
サブシステムとは、大きなシステムを構成する中規模の部分システムのこと。
それ自体も独立して機能を持っていて、ある目的に特化している。
(例)
電子商取引システムのサブシステム例
・ユーザー管理サブシステム
・商品管理サブシステム
・注文処理サブシステム
・決済サブシステム
コンポーネント(Component)
コンポーネントは、ソフトウェアの一部分で再利用可能な小さな機能単位。
具体的には、クラス、モジュール、ライブラリ、UIパーツなどが該当する。
再利用が主な目的。他と組み合わさって機能を果たす。
1つの小さな機能や責務。
※コンポーネントの中にモジュールが含まれることもあるし、その逆もあり得る
→ 使う文脈・設計レイヤーによって関係が変わる
(例)
【UIコンポーネント】
・ボタン
・モーダルウィンドウ
・入力フォーム
【ロジックコンポーネント】
・認証機能
・バリデーション機能
・API通信モジュール
[全体システム]
├── サブシステムA(例:ユーザー管理)
│ ├── コンポーネント1(ログインフォーム)
│ ├── コンポーネント2(登録バリデーション)
│ └── コンポーネント3(プロフィール編集画面)
├── サブシステムB(例:商品管理)
│ └── ...
「分割統治」のための考え方
システムをわかりやすく分割することで、開発効率・保守性・拡張性が高まる
・サブシステムは「大まかな機能の単位」
・コンポーネントは「再利用可能な小さな部品」
モジュール(Module)
モジュールとは、機能を1つのまとまりとして外部と分離したプログラムの部品。
ファイル単位で分けて、他のファイルに読み込んで使うのが基本。
・関数、クラス、変数などをまとめて管理
・再利用しやすい
・他のモジュールと明示的にimport/exportでやりとり
・依存関係がはっきりしていて、保守性が高い
※コンポーネントの中にモジュールが含まれることもあるし、その逆もあり得る。ただし、基本的にはコンポーネントがモジュールによって構成されることが多い。
→ 使う文脈・設計レイヤーによって関係が変わる
項目 | モジュール(Module) | コンポーネント(Component) |
---|---|---|
主な意味 | 機能・ロジックのまとまり | UIや機能のまとまり(もっと上位の構造) |
主な役割 | 処理・関数・API通信などをまとめて提供 | UI部品・画面の一部・再利用可能な部品 |
スケール感 | 小〜中規模 | 中〜大規模 |
例(Web) | ログイン処理の関数、API呼び出し、計算ロジック | ログインフォーム、商品カード、モーダルなどのUI |
関係性 | コンポーネント内で利用されることが多い | モジュールを複数使って構成されることが多い |
内部モジュール(Internal Module)
内部モジュールとは、1つのファイルの中で名前空間(namespace)を使って論理的にコードを分ける方法。
主にTypeScriptの旧バージョンで使われていた概念で、現在はモジュール(import/export)を推奨されている。
・ファイルを分けずに、namespaceでスコープを限定
・小規模なアプリや古いコードベースで見かける
・モジュールシステム(import/export)とは併用できない

アーキテクチャスタイル・アーキテクチャパターン
システムの設計において、アーキテクチャスタイルとアーキテクチャパターンという 2 つの概念がある。
アーキテクチャスタイル
システム全体またはその一部の一般的な構造を示すもの。
つまり、システムの設計の基本的な枠組みやレイアウトのこと。
これは、高いレベルで抽象化されており、具体的な実装の詳細には触れない。
アーキテクチャパターン
具体的な問題を解決するためのルールやガイドラインのセット。
つまり、特定の要件や制約を満たすために確立された方法。
アーキテクチャパターンは、実践的な経験に基づいて検証され、効果的な解決策として認められている。
一般的には、まず問題に適したアーキテクチャスタイルを考え、それに応じてアーキテクチャパターンを選ぶことがある。あるいは、既に解決済みの問題に対して有効なアーキテクチャパターンが存在する場合は、それを直接利用することもできる。
アーキテクチャパターンには、特定の名前が付けられており、デザイナー間のコミュニケーションに役立つ。これらのパターン名は、多くの意味を含んでいるため、設計者が効果的に情報を伝えるのに役立つ。
パイプとフィルタのアーキテクチャ(pipes and filters architecture)
パイプとフィルタのアーキテクチャは、データを小さな部分に分けて段階的に処理するソフトウェアの設計手法。
この設計では、データは「フィルタ」という部品を経由して流れる。それぞれのフィルタはデータに特定の処理を加え、その後「パイプ」を通じて次のフィルタへデータを送る。各フィルタは独立して設計やテストができるため、変更や取り替えがシステム全体に影響せずにスムーズに行なえる。
(例)
アーキテクチャスタイル:データフローアーキテクチャ
使用例:データ処理ソフトウェア、バッチ処理システム、ETL(extract, transform, load)など
※https://recursionist.io/dashboard/course/32/lesson/1123 より画像引用

多層アーキテクチャ
ソフトウェアアプリケーションを、それぞれが特定の機能を持つ異なる層に分割して整理する方法。
これは、アプリケーションがサーバ上で動作し、ユーザーがクライアントを介してアプリケーションとやりとりするクライアントサーバモデルでよく使用される。層(ティア)の数は、アプリケーションの複雑さに応じて変えることができる。
3 層アーキテクチャ
アプリケーションをプレゼンテーション、ビジネス、データストレージの 3 つの層に分離する、多層アーキテクチャの特定のタイプ。
プレゼンテーション層は、Web ページやモバイルアプリのように、ユーザーに情報を表示する役割を担いう。
ビジネスロジック層は、データを処理し、アプリケーションのルールやロジックを実装する役割を担う。
データストレージ層は、データベースなどからのデータの保存と取得を担当する。
3 層アーキテクチャでは、ユーザーが操作するコードであるクライアント、ビジネスロジックのコードを含むバックエンドサーバ、アプリケーションで使用するデータを格納するデータベースの 3 つの主要コンポーネントが存在する。
N 層アーキテクチャ
3 層アーキテクチャを拡張したもので、より大きなスケーラビリティを可能にするために、層をさらに細分化することができる。
例えば、メッセージング、個別サービス、ミドルウェア、サードパーティーの統合などのために、さらに層を追加することができる。これにより、アプリケーションはより柔軟になり、ニーズの変化に対応できるようになる。
(例)
アーキテクチャスタイル:レイヤードアーキテクチャ、クライアントサーバアーキテクチャ
使用例:ウェブサイト、モバイルアプリ、オンラインゲーム、IoT システム
※https://vfunction.com/blog/3-tier-application/ より画像引用
N 層アーキテクチャ イメージ
※https://learn.microsoft.com/ja-jp/azure/architecture/guide/architecture-styles/n-tier より画像引用

MVC アーキテクチャ(model-view-controller architecture)
ソフトウェアをモデル、ビュー、コントローラの 3 つの部分に分けて組織化する方法。
モデル
アプリケーションのデータとロジックが格納される場所。
つまり、モデルはアプリケーションのデータを管理・操作する役割を担っている。
ビュー
モデルからのデータをユーザに表示する責任を持つ。
ここで、ユーザはアプリケーションのユーザインタフェースとやりとりすることになる。
コントローラ
モデルとビューの間の橋渡しのようなもの。
コントローラはユーザの入力を処理し、それに応じてモデルやビューを更新する。コントローラはユーザからの入力を受け取り、それをモデルに送ってデータを更新し、そしてビューを更新してユーザに新しい情報を表示する。
MVC を使用することで、アプリケーションをモジュール化し、保守を容易にし、理解しやすくすることができる。データ、ユーザーインターフェース、制御ロジックを異なるコンポーネントに分離することで、他のコンポーネントに影響を与えることなく、各コンポーネントの変更や更新を容易に行なうことができる。
(例)
アーキテクチャスタイル:レイヤードアーキテクチャ
使用例:ユーザーインターフェースを持つアプリケーション(Web、モバイル、デスクトップアプリ)
※https://www.sayonetech.com/blog/mvc-vs-microservices/ より画像引用

マイクロサービスアーキテクチャ(microservices architecture)
アプリケーションを小さな独立したサービスの集合体として開発するソフトウェア設計パターン。
これらのサービスは軽量なプロトコルを使用してネットワーク上で互いに通信し、各々が自己完結型で特定のビジネスタスクや機能を実行する。また、他のサービスとは独立して開発、展開、スケーリングが可能。
これらのサービスは自律的で分散化されているため、障害箇所に対して耐性を持ち、必要に応じてスケールアップやスケールダウンが可能。ソケット(または HTTP)を介した通信やリモートプロシージャコールを行なう際に、すべてのサービスが同じプロトコルを知っているため、それぞれのサービスは異なるプログラミング言語やテクノロジースタックを使用して開発することができ、技術に依存しません。
(例)
アーキテクチャスタイル:サービス指向アーキテクチャ
使用例:エンタープライズアプリケーション、中・大規模クライアントサーバ、データセンター、サードパーティサービス統合、クラウドコンピューティング
※https://webandcrafts.com/blog/what-is-microservices-architecture より画像引用

Blackboard アーキテクチャ(blackboard architecture)
異なるモジュールが共同で問題に取り組むためのソフトウェア設計方法。
モジュールはすべて、Blackboard と呼ばれる共有データ構造にアクセスし、更新することができる。Blackboard は、異なるナレッジソースやコントローラが情報を交換し、問題解決のために協力し合う中心的なハブとして機能する。
アーキテクチャスタイル:データ中心型アーキテクチャ
使用例:A.I システム、意思決定支援システム、自然言語処理、エキスパートシステム
※https://recursionist.io/dashboard/course/32/lesson/1124 より画像引用

メディエータートポロジーアーキテクチャ(mediator topology architecture)
システムのさまざまな部分で発生したイベントがまずイベントキューに送られるようにシステムを組織化する方法。次に、イベントメディエーターと呼ばれる中央のコンポーネントが、どのイベントをどのような順序で処理すべきかを決定する。メディエーターは、各イベントプロセッサで処理されたイベントを新たに生成することもできる。
(例)
アーキテクチャスタイル:イベント駆動型アーキテクチャ
使用例:非同期トランザクション、金融取引システム、電子商取引アプリケーション、ソーシャルネットワークサービス
※https://pradeepl.com/blog/architecture/event-driven-architecture/ より画像引用

ブローカートポロジーアーキテクチャ(broker topology architecture)
イベント駆動型アーキテクチャにおける一種の構造。この構造では、メッセージブローカーまたはイベントバスがシステム内の他のコンポーネントから送信されるすべてのイベントを受け取り、イベントチャネルと呼ばれるものに格納する。その後、メッセージブローカーはイベントの内容に基づいて、それぞれのイベントが適切なイベントチャネルに送られるようにルーティングする。
各イベントプロセッサは、受け取ったイベントを処理し、新しいイベントを生成して再びメッセージブローカーに送信する役割を担っている。このプロセスでは、イベントの処理順序を調整するための仲介者や、処理後に新しいイベントを生成するためのメカニズムは存在しません。その結果、一連のイベントが順番に発生することがよくある。
(例)
アーキテクチャスタイル:イベント駆動型アーキテクチャ
使用例:非同期トランザクション、金融取引システム、電子商取引アプリケーション、ソーシャルネットワークサービス
※https://recursionist.io/dashboard/course/32/lesson/1124 より画像引用

マスタースレーブパターン(master-slave pattern)
分散システムのアーキテクチャの一種で、マスターと呼ばれるデバイスまたはプロセスが、スレーブと呼ばれる他のデバイスまたはプロセスを制御する。
マスターはシステム全体の動作を制御し、スレーブはマスターから割り当てられた特定のタスクを実行する。スレーブノードは互いに直接通信することはできず、代わりにマスターノードを介して通信する。
(例)
アーキテクチャスタイル:メイン-サブルーチンアーキテクチャ
使用例: データベースレプリケーション、Web クラスタ、並列計算、ロボティクス
※https://recursionist.io/dashboard/course/32/lesson/1124 より画像引用

アーキテクチャの設計におけるメリット・デメリット
すべての解決策には独自の利点と欠点が伴う。アーキテクチャの設計においては特にその傾向が強い。
一部のアーキテクチャは、開発の複雑さを受け入れることでスケーラビリティの向上を期待できる一方、シンプルなアプローチは迅速な実装を可能にするものの、スケーラビリティの面で制約が生じる場合がある。
また、ストレージの使用量を増加させることでパフォーマンスの最適化を図る設計や、大規模システムでの即時メッセージ配信を重視するアーキテクチャは、一貫性の確保に課題を持つことがある。
各ソリューションの長所と短所を確認し、プロジェクトの要件と比較することが重要。
要件によっては、特定の長所が必須である場合もあれば、要件がなくても特定の短所には大きな影響がない場合もある。
決定する前に、ソリューションの短所が最低要件を満たしているかどうかを必ず確認することは重要。
アーキテクチャはシステム全体に関わるものであるため、各ソリューションについて慎重に検討することが重要になる。特定のパターンが最適なソリューションかどうかわからない場合は、少なくとも 1 つの代替アーキテクチャーのソリューションと比較するのが常によいでしょう。
レビューによって、各ソリューションの長所と短所をプロジェクトの要件に照らして評価し、見落としている可能性のある問題を特定することができる。

詳細設計
システムの全体構造、コンポーネント、およびその相互作用の高水準の設計図としての役割を果たすのがアーキテクチャ設計。これはシステムまたはサブシステムの広範なビジョンをキャッチする基盤的なステップ。しかし、ソフトウェア設計プロセスの一側面に過ぎず、詳細設計はさらに深く掘り下げ、各モジュールやコンポーネントが入念に詳述される段階。
詳細設計は、ソフトウェアの品質と開発効率の向上の 2 つに結びついている。ソフトウェアが高品質と認められるためには、機能的・非機能的要件を満たすだけでなく、利害関係者の期待にも応えることが必要。また、開発者はロジックの概念化や実装のアイディアをブレインストーミングすることが求められる。
ソフトウェアの品質
ソフトウェアの品質は、要求モデルに記載された全ての機能的及び非機能的要件を正確に実装し、さらに利害関係者の期待にも応えることを指す。
この理念は、既に全要件が定義されていると仮定される伝統的なウォーターフォールモデルのフェーズである詳細設計から、アジャイル開発のようにフィードバックループを持つ反復的なプロセスにも適用される。
アジャイル開発の文脈においては、設計はエンドユーザー、ターゲット市場、投資家といった利害関係者のニーズを中心に据えるべき。
アジャイルの開発サイクルは、反復を重ねる中で要件が逐次的に加えられたり、更新されたり、時には削除されたりする。そのため、ソフトウェアの高い品質を確実に維持するためには、確立されたベストプラクティスに従うことが不可欠。
開発効率
開発効率とは、ソフトウェアを開発・維持する際の効果的な取り組み方を指す。
詳細設計を行なうことで、ロジックが具体的にどのように機能するかを考え、実装方法をアイデア出す。
これにより、実装の観点から問題を解決する方法を考える自由が生まれ、作業を始める前に品質を保証できる。
詳細設計に十分な考慮をせず、またはそれをスキップすることは非効率。なぜなら、ソフトウェア開発のこの段階では開発コストが最も低いため。詳細設計中の実装変更にかかるコストは、コーディング中の実装変更に比べてほぼゼロ。実装変更にかかるコストは時間の経過とともに指数関数的に増加する。コーディングが完了した後、品質保証(QA)中や本番リリース後の実装変更にかかるコストは、10 倍ずつ増加する。
そのため、ソフトウェアを効率よく、低コストで開発するためには、詳細設計フェーズに時間をかけることが重要。
※https://techplay.jp/column/1610 より画像引用
詳細設計
詳細設計には、コンポーネントレベルの設計と UI/UX 設計の 2 つのタイプが含まれている。
コンポーネントレベルの設計
コンポーネントの構造、実装方法、および他のコンポーネントとの相互作用に関する詳細を提供する。
コンポーネントレベルの設計は、ユーザーには見えないソフトウェアの部分を主に取り扱っている。
UI/UX 設計
ソフトウェアとエンドユーザーとの間のインタラクションに関連する部分に焦点を当てている。
UI/UX はエンドユーザーに見える、また経験するソフトウェアの部分に焦点を当てている。
これらの詳細設計は並行して開発されることがあり、UI/UX の設計もコンポーネントレベルの設計に支えられている。
アーキテクチャはプロジェクトの初めやシステム全体のイテレーションで重点的に設計されるのに対し、詳細設計はソフトウェアに適用される各イテレーション、機能の追加、コンポーネントの更新やリファクタリング、コーディングが必要な作業に適用される。

詳細設計
コンポーネントレベルの設計
モジュールやサブシステムのような個々のソフトウェアコンポーネントを個別に設計・開発するソフトウェア開発プロセス。各コンポーネントは特定の機能を念頭に置いて設計され、他のコンポーネントとの相互作用が定義される。
コンポーネントレベルの設計の目的は、モジュール化された再利用可能なソフトウェアコンポーネントを作成し、大規模なシステムに容易に統合してテストできるようにすること。
このアプローチにより、開発者は複雑なシステムをより小さく管理しやすいコンポーネントに分解することができ、ソフトウェアシステムの設計、実装、テスト、および保守が容易になる。これらのコンポーネントが組み合わされると、ソフトウェアシステム全体が形成される。
コンポーネントはアーキテクチャが設定された後に設計される。各コンポーネントはアーキテクチャの個々の部分。また、コンポーネントはさらに小さなコンポーネントに分割することもできる。
例えば、ゲームエンジンは、ゲーム固有のサブシステム、フロントエンド、ゲームプレイの基盤、視覚効果、衝突と物理などから構成されるレイヤーアーキテクチャに従う場合がある。
コンポーネントレベルの設計では、ゲームプロファイリングなどのレイヤーのうちの 1 つに特化する。このコンポーネントはさらに細分化されて、録画と再生、メモリとパフォーマンスログ、ゲーム内開発者コンソールなどのさらなるコンポーネントに分割される場合もある。
※https://recursionist.io/dashboard/course/32/lesson/1126 より画像引用
各コンポーネントは、単一責任の原則に従い、1 つの主要なタスクに焦点を当てる。
コンポーネントを開発するための一般的な手順
(1) システムを管理しやすい小さなコンポーネントに分解する
(2) コンポーネント間のインターフェースと相互作用を定義する
(3) 各コンポーネントの開発プロセスに従う
(要件)コンポーネントの機能要件または目的を特定する
(設計)各コンポーネントの内部構造を設計する
(コード&テスト)各コンポーネントを個別に実装し、テストする
(デプロイメント)コンポーネントをより大きなシステムへ統合する
(4) システム全体をテストして、コンポーネントがうまく統合されていることを確認する
コンポーネントレベルの設計の初めのステップは、システムをコンポーネントに分解し、それらのコンポーネントがどのように相互作用するかを明確にすること。
このステップにおいて、コラボレーション図を作成し、各コンポーネントのインターフェースを開発する。これらのプロセスは同時に進行する。
コンポーネントの契約、別名インターフェースとしても知られているが、どの関数が利用可能であるか、入力/出力の値は何かを明示する。場合によっては、契約は前提条件、後置条件、および例外も定義することができる。これにより、モジュールとして扱うことができ、維持管理が容易で、拡張性があることを保証するのに役立つ。
UML 図の正確な仕様や詳細に関しては公式ドキュメントを見ることがベストですが、目的や状況に応じて、もっと単純で簡易な図を作成しても問題ない。
コラボレーション図
システム内のコンポーネント間で交換される相互作用とメッセージを視覚化するために使用されるダイアグラムの一種。特定のタスクや機能を達成するために、オブジェクトが互いにどのように協力し合うかを示すもの。矢印はメッセージの流れの方向を示し、メッセージの名前とフロー番号でラベル付けされている。
※https://atmarkit.itmedia.co.jp/fjava/devs_bk060201/column/nagase03/nagase03.html より画像引用
コンポーネント図(UML)
システムのコンポーネントとその依存関係を示す図の一種で、ソフトウェアシステムの組織を高い抽象レベルで視覚化するために使用される。
システムの各部分は、特定の機能を実行する個別のコンポーネントとして示される。これらのコンポーネントはモジュール化されており、システムの他の部分や他のプロジェクトで簡単に再利用することができる。システムが必要とする情報、作成する情報、アクセスする情報はすべてアーティファクト(artifact)と呼ばれる。
※https://recursionist.io/dashboard/course/32/lesson/1127 より画像引用
インターフェースクラス
インターフェースクラスは、インターフェースを実装するクラスが必ず持つべき抽象的なメソッドやプロパティのグループを定義した設計図。コンポーネントが期待するオブジェクトの種類を指定するために使用される。
インターフェースの主な目的は、システムの異なる部分の間で共通の合意や標準を確立すること。
インターフェースは抽象的であり、実装クラスが行なうべきことを概説するだけで、それをどのように行なうべきかの詳細を提供するものではない。
※https://recursionist.io/dashboard/course/32/lesson/1127 より画像引用
各コンポーネントがどのように相互作用し、公開インターフェースを持つかを理解し、レイアウトすることができれば、個々のコンポーネントを設計することができる。個々のコンポーネントに取り掛かる前に、システムとコンポーネントを再帰的に分解し、個別に機能するコンポーネントレベルまで分解する。
最も低いレベルでは、コンポーネントの意味を明確に定義することができる。
例えば、オブジェクト指向プログラミング(OOP)言語では、クラスがコンポーネントの一つとなる。C のような低レベル言語では、構造体(struct)がコンポーネントとして使用されることがある。また、Haskell のような関数型プログラミング言語では、データ型とライブラリがコンポーネントの役割を果たすことがある。

コンポーネントレベルの設計
1 つのコンポーネントの詳細を決定するために必要な 3 つのステップ
① 公開インターフェースの定義
これはコンポーネントやクラスを設計する際に非常に重要。これは基本的に、他のコンポーネントやクラスがこの特定のコンポーネントとどのように相互作用するかを定義するもの。
公開するメソッドは、外部に提供する振る舞いや機能を決定する。入力と出力のデータ型、そして関数の目的を記述することは重要。
② データ設計
コンポーネントの内部構造を理解することが重要。これは、OOP におけるカプセル化の考え方に通じるもので、オブジェクトの内部状態はプライベートに保たれ、他のオブジェクトやコンポーネントはそのパブリックメソッドを通じてオブジェクトと相互作用する。カプセル化と再利用性を確保するために極めて重要。
③ 各プロセスの手順の説明
コンポーネント内の各プロセスが単一のタスクに焦点を当てるようにすることで、コンポーネントがより保守しやすくなり、テストも容易になる。
コンポーネント内の各プロセスや機能が具体的にどのように計算や処理を行なうのかの手順を策定する。
アクティビティ図やフローチャートを使用して、これらのステップを視覚化すると、設計、レビュー、共同作業が容易になる。
この段階では、UML クラス図を使って詳細を示すことが一般的。その結果として、クラスの概要、クラス変数、インスタンス変数、メソッドなどの基本構造を示す分析クラスを作成できる。
分析クラス(analysis class)
ソフトウェア開発の初期段階での設計プロセスにおいて使用される概念。
この段階では、実際のプログラムを書く前に、システムが対処すべき問題や要件を理解し、それをオブジェクト指向のクラスの形式に落とし込む作業を行なう。
エンティティが持つべきデータや属性、そしてそれらのデータに対して行なうべき操作やメソッドを特定したり、どのクラスが他のクラスとどのように関連するのか、または相互作用するのかを理解し、それをモデルに反映することも含まれる。
※https://recursionist.io/dashboard/course/32/lesson/1128 より画像引用
分析クラスを作成した後は、全ての変数やメソッドの詳細なリストと、関係(集約や組成)の完全な詳細を持つ、詳細設計クラスを作成することができる。
詳細設計クラス(elaborate design class)
分析クラスから派生したもので、詳細設計クラスには分析クラスの属性や操作だけでなく、可視性、アクセス制御、継承、インターフェース、必要なデータ構造やアルゴリズムなどの追加の詳細も含まれる。設計クラスは、オブジェクト間の関係(継承、コンポジション、関連)の実装も担当している。
※https://recursionist.io/dashboard/course/32/lesson/1128 より画像引用
最終ステップは、各プロセスの手順的な説明を作成すること。
この段階では、特定の方法に注目し、そのプロセスがどのように進行するかを手順毎に詳細に書き出す。
全ての前提条件や後条件、例外、性能に関する制約、そして具体的な入出力値を明記します。このプロセスの説明には、アクティビティ図が役立つ。
データ構造やアルゴリズム、プログラミングの経験は、この手順の詳細や制約を理解するのに有効。
さらに、前提条件やプロセスが呼び出される前後の状態などの詳細が必要な場合は、状態図を用いることが推奨される。特に、エントリーポイントの関数や while ループのように、複数のイベントや状態を持つ複雑な関数に対しては非常に役立つ。
このようにして、各プロセスの動きや条件を明確にしていくことが、高品質なシステムの設計や実装に繋がる。
アクティビティ図
アクティビティ図は、機能に関わるアクティビティの流れを入力から出力まで示す。
機能の要件と目的を検討した後、機能は個々のステップ、アクティビティに分解される。
状態図
システムの動作を異なる状態やそれらの状態間の遷移に分けて視覚的に表現したもの。
状態は遷移でつながれており、これはシステムがトリガーに応じて状態間を移動する方法を示す。
アクションは状態と遷移の両方に関連付けることができ、これらのアクションはシステムが特定の状態に入る、または退出する時、あるいは状態間で遷移する時の動作を表す。
すべてのサブシステムに属するコンポーネントに対して、これらのコンポーネント設計手順を再帰的に並行して適用することができる。
設計は繰り返し行なうプロセス
主要なコンポーネントの詳細を同時に考えながら、コンポーネントが互いにどのように協力するかを探求する必要がある。各メソッドが手順に従ってタスクを実行するために必要なデータと構造を考える必要がある。
この設計段階を利用して、要件をソフトウェアコードにどのように変換するかを探求してください。
詳細を詰めたり、潜在的な代替案を検討することで、設計は n 回の反復で固まっていく。
設計とコーディングは、ソフトウェアの開発において共同で進行する。探求、プロトタイピング、そしてソリューションを見つけるためのリスクとコストは、これらの 2 つの段階で最も低くなる。アジャイル開発においては、要件がフィードバックループを通じて追加または変更されるにつれて、設計と実装が前後することがある。

デザインパターン
ソフトウェアを設計する際、品質と実装効率を向上させるための核となるコンセプト
抽象化(abstraction)
抽象化とは、ソフトウェア工学において、開発プロセスを簡略化するために用いられる技法。
システムの最も重要な機能を特定し、無関係な細部を無視する。これにより、開発者はより理解しやすく、使いやすいモデルやインターフェースを作成することができる。
関心の分離(separation of concerns)
関心の分離とは、複雑なシステムをより小さく、より管理しやすい部分に分割し、それぞれが特定の責任を持つようにすること。
これにより、複雑さを軽減し、モジュール性を高め、システムの保守と修正を簡素化することができる。
高凝集・低結合(cohesion・coupling)
凝集性とは、モジュールやコンポーネントの要素がどれだけ互いに関連しているかを意味する。結合力の高いモジュールには、明確で集中した目的があり、すべての要素がその目的を達成するために協力し合う。
結合度とは、モジュールやコンポーネントが互いにどの程度依存しているかを意味する。結合度の低いモジュールは独立性が高く、システムの他の部分に影響を与えることなく修正や置き換えが可能。
ソフトウェアシステムは、高凝集と低結合を持つことを目標とする必要がある。
モジュール性(modularity)
モジュール性とは、システムを独立したモジュールに分割し、それぞれを開発、テスト、保守することでシステムを構成する方法。このアプローチにより、柔軟性、拡張性、保守性が向上する。
拡張性(extensibility)
拡張性とは、ソフトウェアシステムを拡張したり変更したりして、新しい機能や特徴を追加することが容易であることを指す。
情報隠蔽(information hiding)
情報隠蔽とは、モジュールやコンポーネントの実装の詳細をシステムの他の部分から隠すことを指す。これにより、開発者はシステムの残りの部分に影響を与えることなく、モジュールの実装を変更することができる。カプセル化と同様に、コントラクトやインターフェースがよく使われる。
独立性(independence)
独立性とは、システムの異なるコンポーネント間で、機能面でも実装面でも、どの程度隔離されているかを示すもの。これにより、複雑さを軽減し、柔軟性を高めることができる。
リファクタリング(refactoring)
リファクタリングとは、コードの動作を変えずに、品質と保守性を向上させるためにコードを再構築するプロセスのこと。リファクタリングでは通常、コードを単純化し、重複を減らし、モジュール性と可読性を向上させるための変更を行なう。

ソフトウェアエンジニアが共通して守るべき原則
DRY(Don't Repeat Yourself)
開発者は可能な限りコードの重複を避けるべきという慣習。
DRY の背景にある考え方は、コードを DRY に保つことで、開発者が書くべきコードの量を減らし、コードの保守性を向上させ、コードを拡張する必要がある場合に不要な作業を避けることができるというもの。
KISS(Keep It Simple Stupid)
ソフトウェア設計におけるシンプルさの重要性を強調するもの。
開発者が物事をシンプルに保つことで、コードが読みやすくなり、複雑さが軽減され、バグが発生するリスクを最小限に抑えることができる。
YAGNI(You Ain't Gonna Need It)
開発者は絶対に必要な機能だけを追加するように気をつけるという慣習。
必要な機能と要件に集中することで、開発者は開発期間とコストを削減し、複雑さを軽減してコード品質を向上させることができる。
要件を分析することと、必要に応じてコードベースを拡張できるようにソフトウェアを設計することの間で、適切なバランスを見つけることが重要。これは、アジャイル開発環境では特に重要になる。

SOLID
プログラミングパラダイムにある、そのパラダイムの利点を活かすためにエンジニアが従うべき原則。
オブジェクト指向プログラミングでは、SOLID の原則に従うのが一般的。
S: 単一責任原則(single responsibility principle)
クラスは 1 つの責任または役割のみを果たすべき。
この原則はモジュール性と関心の分離を促進し、クラスの機能を変更または拡張する際に、システムの他の部分に影響を与えずに容易に行えるようにする。
O: 開放/閉鎖原則(open/closed principle)
OCP とは、ソフトウェアの実体(クラス、モジュール、関数など)は、拡張に対してはオープンであるが、変更に対してはクローズであるべきであるというもの。
新しい要求が生じたとき、あるいはシステムを拡張する必要が生じたとき、既存のコードを変更するのではなく、新しいコードを追加することで拡張できるようにする必要がある。
このアプローチは、バグの導入や既存の機能の破壊のリスクを最小限に抑えるのに役立つ。これは、継承や組み合わせを通じて拡張できる明確で安定したインターフェースを持つソフトウェアモジュールを設計することで部分的に達成される。
L: リスコフ置換原則(liskov substitution principle)
スーパークラスのオブジェクトは、プログラムの正しさに影響を与えることなく、サブクラスのオブジェクトと置き換えることができるべきであるというもの。
これは、サブクラスがスーパークラスのすべての動作を継承しているため、シームレスに置き換えることができる必要があるため。
一般的に、多くの人は「正方形は特殊な長方形である」と捉えるため、正方形を長方形のサブクラスとしてモデリングしようと考えるかもしれないが、LSP の観点からこのモデリングは問題を持つ可能性がある。
長方形クラスには「幅」と「高さ」の 2 つのプロパティがあり、それぞれを設定できるとする。
正方形は四辺がすべて等しいので、幅または高さのどちらかを変更すると、もう一方も同じ値になる必要がある。もし正方形を長方形のサブクラスとして実装してしまうと、以下のような問題が発生する。
長方形のインスタンスとして正方形を置き換えた場合、幅を変更しても高さが変わらないという長方形の振る舞いを期待するかもしれません。しかし、正方形の実装では、幅を変更すると高さも変わるため、この期待は裏切られる。逆に、正方形のインスタンスの場所で長方形を使用すると、幅と高さが常に一致することを期待することができない。
このような振る舞いの違いのため、正方形を長方形のサブクラスとして扱うことは LSP に違反する可能性が高くなる。この例から、親クラスの振る舞いの期待を子クラスが裏切ると、継承が正しくない場合があることがわかる。
※https://recursionist.io/dashboard/course/32/lesson/1130 より画像引用
I: インターフェース分離の原則(interface segregation principle)
インターフェースを一貫性があり、特定のタスクに焦点を当てて保つことの重要性を強調する原則。
これにより、クライアントは使用しないメソッドに依存するよう強制されるべきではなく、インターフェースはできるだけ特定的で最小限に設計されるべきだとされている。
目的は、使いやすく、維持しやすいインターフェースを作成すること。
インターフェースのメソッドの数を最小限にすることで、クライアントがインターフェースを理解し、使用するのが容易になえう。
(例)プリンタを使って文書を印刷するソフトウェアシステムを設計している
プリンタインタフェースには、print()、scan()、copy()、fax() などのメソッドが用意されていることでしょう。しかし、プリンタインターフェースのすべてのクライアントが、これらのメソッドをすべて必要とするわけではない。例えば、文書を印刷するために print() メソッドだけを必要とするクライアントもあれば、文書のコピーを作成するために copy() メソッドを必要とするクライアントもいるでしょう。
プリンタインタフェースが ISP を意識して設計されていない場合、インタフェースのすべてのクライアントが、たとえそれらを必要としなくても、すべてのメソッドを実装しなければならないことになる。
これは不必要な複雑化を招き、エラーやバグのリスクを高めることになる。
一方、ISP を意識したプリンタインタフェースであれば、各クライアントに必要なメソッドのみを実装することになり、使い勝手やメンテナンスが容易になります。結果として、print() メソッドのみ必要な場合、その関数だけ定義する Printable インターフェース作成する。
※https://recursionist.io/dashboard/course/32/lesson/1130 より画像引用
D: 依存性逆転の原則の原則(dependency inversion principle)
高レベルのモジュールが具体的な実装よりも抽象に依存すべきだとする原則。
これにより、高レベルと低レベルのモジュールの結合度を低くすることができ、低レベルのモジュールを変更しても高レベルのモジュールに影響を与えにくくなる。
この原則を実現するための方法の一つは、抽象を表すインターフェースを作成すること。
高レベルのモジュールは、そのインターフェースの特定の実装よりもインターフェース自体に依存することができ、具体的なクラスやサブクラスではなく、抽象クラスや親クラスに依存することもできる。
(例)Calculator
最も基本的な Calculator クラスでは、足し算や引き算の操作があるが、新しい操作、例えば掛け算を追加したい場合、現在の Calculator クラスを変更する必要がある。これを実現するために、Calculator クラスをリファクタリングして、ICalculatorOperation というインターフェースを使用するようにする。
そして、具体的な操作のためのクラス(AddCalculatorOperation や SubtractCalculatorOperation など)を作成して、ICalculatorOperation インターフェースを実装する。この方式で、Calculator クラスを変更せずに新しい操作を追加することができる。
※https://recursionist.io/dashboard/course/32/lesson/1130 より画像引用

優れたソフトウェア設計
優れたソフトウェア設計は、これらの概念を利用し、一般的な実践と原則に従う傾向がある。
多くの場合、目の前の問題は、ソフトウェアで繰り返し発生する問題。つまり、ソフトウェアを設計する場合、車輪の再発明をするのではなく、すでに利用可能なソリューションがあるかどうかを検討するのが賢明な選択となる。なぜなら、新しく設計したソリューションが問題に合わない、あるいは既存のソリューションほど効果的でないというリスクがあるため。
幸い、ソフトウェア開発で頻繁に起こる問題であれば、デザインパターンと呼ばれる広く受け入れられている解決策がある場合がほとんど。デザインパターンは、ソフトウェア設計の一般的な問題に対する再利用可能な解決策で、その分野の専門家によって特定され、文書化されている。デザインパターンは、ソフトウェア開発における一般的な問題を解決するための、信頼性が高く、効率的で、一貫性のある方法を開発者に提供する。
ソフトウェアエンジニアは、経験と直感を使って、ソフトウェア設計によくあるパターンを認識し、それに従って解決策をソフトウェアとして開発する。ソフトウェアエンジニアは、問題を解決するために使用できるパターンを特定した場合、特定のドメインに特化した公式文書や書籍にそれを文書化することがよくある。この文書によって、他のエンジニアがそのパターンを学び、自身のソフトウェア開発プロジェクトで適用することができる。
デザインパターンに含まれているエンジニアがパターンを識別し参照するのに役立つコンポーネント
パターン名
パターンに付けられた名前で、識別や参照を容易にするのに役立つ。
カテゴリ
パターンのカテゴリは、類似のパターンを分類してグループ化するのに役立つ。
問題
そのパターンが解決しようとしている問題を詳細に説明する。
適用性
問題領域に対する制約を設定し、パターンを適用できる具体的なシナリオと環境をリストアップする。
解決方法
問題を解決するための構成要素や構造、部品間の連携などを図や説明で示し、パターンを使ってどのように問題を解決するかを説明する。構造、構成されるコンポーネント、依存関係モジュール、パーツ同士がどのように連携して問題を解決するのかを列挙している。
結果
パターンを使用することの利点を列挙し、問題を解決する際に優れた設計コンセプトや原則がどのように守られているかを詳しく説明する。また、潜在的なマイナス面や注意点を列挙することもある。
実装
特定の言語や環境において、パターンをどのようにコードで実装できるかを詳細に説明する。
サンプルコード
このパターンを実装した実際のコードを掲載し、読者が参照できるようにする。
ケーススタディ
読者が知っているソフトウェアを中心に、パターンを実装しているソフトウェアとその部分のリストを提供する。
これらの構成要素を含めることで、ソフトウェア工学における共通の問題を解決しようとする技術者にとって、デザインパターンがより理解しやすく、アクセスしやすくなる。
ある問題に対する優れた解決策がすでにあるかどうかを調べるには、その問題領域に関連するパターンを調べることから始めることができる。
そのためには、解決する問題や背景にある動機など、パターンの種類によって分類し、そのパターンの適用可能な部分が自分の状況にマッチしているかどうかを確認する必要がある。
デザインパターンは多様で、常に進化しており、何千種類ものパターンが存在する。
この複雑さを理解するために、デザインパターンはそのドメインによって分類されている。パターンには多くの分類がある。

デザインパターン
オブジェクト指向プログラミングは、ソフトウェア業界で最も一般的に使用されているプログラミングパラダイム。
小さなベンチャー企業であろうと大企業であろうと関係なく、多くのソフトウェアエンジニアがクラスとオブジェクトを使用してソフトウェアを設計している。
オブジェクト指向プログラミングの中で、デザインパターンは生成パターン・構造パターン・行動パターンの大きく 3 つに分類される。
いくつかのパターンは特定のドメイン(特定の業務分野)に特有であり、使用されるプラットフォームによって異なる実装になる可能性がある。
例えば、MVC アーキテクチャパターンは、フロントエンドしかない Web アプリケーションでは、サーバサイドの実装と比較して異なる実装になる可能性がある。一方、ビデオゲームプログラミングのためのゲームループパターンのように、特定のカテゴリに特化したパターンもある。
これらのパターンは、開発の効率を向上させ、コードの再利用性を促進し、システムの保守と拡張を容易にする役割を果たす。それぞれのドメインやプラットフォームに特化したパターンを学ぶことで、より効果的にソフトウェアを設計、開発することが可能になる。
生成パターン(creational pattern)
生成パターンは、オブジェクトの作成方法に焦点を当てる。
生成パターンは、ソフトウェア開発におけるオブジェクトの作成方法を扱うデザインパターン群。
状況に応じてオブジェクトを作成する最適な方法を見つけることに重点を置いている。
【ファクトリメソッドパターン(factory method pattern)】
オブジェクトを作成するためのインターフェースを提供するが、どのクラスをインスタンス化するかはサブクラスが決定する。
これは、すべての他の部分を同じままに保ちながら、サブクラスによって異なるタイプのオブジェクトを作成したい場合に役立つパターン。
※https://recursionist.io/dashboard/course/32/lesson/1132 より画像引用

デザインパターン
生成パターン(creational pattern)
【ビルダーパターン(builder pattern)】
複雑なオブジェクトの構築とその表現を分離し、メソッドの連鎖を通じて異なる表現を作成するのに同じ構築プロセスを利用できるようにする。
このパターンは、複雑な構造を持つオブジェクトを作成する必要があり、その構築プロセスと表現を分離したい場合に役立つ。
※https://recursionist.io/dashboard/course/32/lesson/1132 より画像引用

デザインパターン
生成パターン(creational pattern)
【抽象ファクトリパターン(abstract factory)】
具体的なクラスを指定せずに、関連するオブジェクトのファミリーを作成するためのインターフェースを提供する。
このパターンは、コードベースの異なる場所でお互いに関連するオブジェクトのセットを作成したい場合、各個のオブジェクトを渡すのではなく、抽象ファクトリーを渡すのに役立つ。
※https://recursionist.io/dashboard/course/32/lesson/1132 より画像引用

デザインパターン
生成パターン(creational pattern)
【プロトタイプパターン(prototype pattern)】
元となるインスタンスをクローンして新しいオブジェクトを効率的に作成する方法。
プロトタイプパターンを使用すると、オブジェクトを新たに生成するコストや時間を削減でき、パフォーマンスの向上とメモリ使用量の最適化に寄与する。
これは、新しいオブジェクトが頻繁に必要とされ、かつその生成に負荷が伴う場合などに特に役立つ。
※https://recursionist.io/dashboard/course/32/lesson/1132 より画像引用

デザインパターン
生成パターン(creational pattern)
【シングルトンパターン(singleton pattern)】
特定のクラスが 1 つのインスタンスしか生成できないようにするもの。
これにより、そのクラスのオブジェクトがプログラム中で 1 つしか存在しないことを保証する。
静的メモリにインスタンスを保存し、それをプログラム全体で共有する。
※https://recursionist.io/dashboard/course/32/lesson/1132 より画像引用

デザインパターン
構造パターン(structural pattern)
構造パターンは、オブジェクトの構成や集約、継承など、オブジェクト間の関係を扱うもの。
ソフトウェア工学の分野では、構造パターンと呼ばれるデザインパターンのセットがある。これらのパターンは、クラスやオブジェクトをどのように組織化し、組み合わせて、より複雑な構造を形成するかを扱う。
構造化パターンは、集約、合成、継承、およびインターフェースを使用して、これらの大きな構造を作成する。
【アダプタパターン(adapter pattern)】
一つのクラスのインターフェースを、他のクラスが期待する形に変換するデザインパターン。
これにより、元々は互換性のなかったクラスやコンポーネントも、アダプタを介してスムーズに連携し、一緒に動作することが可能になる。これは、コードの再利用を促進し、異なるシステムやライブラリ間での連携を容易にする。
アダプタパターンの目的:既存のクラス(例えば古いAPI)を「新しいインターフェース」として使いたい。
→「新しいインターフェース」を実装したアダプタクラスを作る。中では「古いクラス」を 移譲して使う。
※https://recursionist.io/dashboard/course/32/lesson/1133 より画像引用
アダプタ:新しいインターフェースを提供するラッパー
移譲:自分で処理を実行せずに、他のオブジェクトにその処理を任せること。アダプタが処理を自分で実行せず、内部の別のクラス(=既存クラス)に任せる技法。
class PrinterAdapter {
private LegacyPrinter legacyPrinter;
public PrinterAdapter(LegacyPrinter legacyPrinter) {
this.legacyPrinter = legacyPrinter;
}
public void print(String message) {
// 移譲している:自分では印刷せず、LegacyPrinter に任せている
legacyPrinter.oldPrintMethod(message);
}
}
比較項目 | 継承(Inheritance) | 移譲(Delegation) |
---|---|---|
処理の主体 | 子クラス自身が処理する(親クラスの機能を自分のものとして使う) | 他のオブジェクトに処理を任せる(呼び出すだけで中身は任せる) |
関係 | 「〜は〇〇である」(is-a) | 「〜は〇〇を持っている」(has-a) |
例 |
class Dog extends Animal (DogはAnimal) |
class Dog { Animal animal; } (DogはAnimalを使う) |
柔軟性 | 低い(静的に決まる) | 高い(動的に差し替え可能) |
再利用 | 強力だが密結合 | 柔軟で疎結合 |
継承: 再利用が簡単だけど、変更に弱く結合が強くなりがち
移譲: 柔軟で差し替えやすく、テストや保守に強い
まずは「移譲」、必要なら「継承」で考えてみる

デザインパターン
構造パターン(structural pattern)
【ブリッジパターン(bridge pattern)】
抽象化とその実装を分離し、それぞれが独立して変更できるようにするデザインパターン。
具体的には、あるクラスの機能やロジック(抽象化)と、それを具体的に実行する方法(実装)が密接に結びついていると、一方を変更した際に他方に影響を与えてしまう可能性がある。これを防ぐため、ブリッジパターンを用いて抽象化と実装を分けることで、それぞれを独立に変更や拡張ができる構造を目指す。
※https://recursionist.io/dashboard/course/32/lesson/1133 より画像引用

デザインパターン
構造パターン(structural pattern)
【コンポジットパターン(composite pattern)】
個々のオブジェクトとオブジェクトのグループを同じ方法で扱うことができるようにする。
オブジェクトを木のような階層構造で整理して、単一のオブジェクトとオブジェクトの集まり(グループ)に同じ操作を行なうことが可能になる。
※https://recursionist.io/dashboard/course/32/lesson/1133 より画像引用

デザインパターン
構造パターン(structural pattern)
【デコレータパターン(decorator pattern)】
オブジェクトに動的に追加の責任を付与することができるが、そのインターフェースは変更しない。
デコレーターパターンを使用すると、元のコードやクラスを変更することなく、オブジェクトに新しい機能を付け加えることができる。
※https://recursionist.io/dashboard/course/32/lesson/1133 より画像引用

デザインパターン
構造パターン(structural pattern)
【ファサードパターン(facade pattern)】
複雑なシステムやライブラリは、その多様性ゆえに多くのインターフェースやメソッドを持っており、それらを直接操作するのは複雑で手間がかかることがある。ファサードパターンは、そのような複雑な操作をシンプルにまとめ、ユーザーが簡単にアクセスできる一つのインターフェースを提供する役割を持っている。
※https://recursionist.io/dashboard/course/32/lesson/1133 より画像引用

デザインパターン
構造パターン(structural pattern)
【フライウェイトパターン(flyweight pattern)】
大量のオブジェクトを作成する必要がある状況で、メモリ使用量を減らし、パフォーマンスを向上させるために使用される。フライウェイトパターンは、インスタンスごとに新しいオブジェクトを作成するのではなく、似たような特性を持つオブジェクトの状態を共有することでこの問題を解決する。
つまり、同じ特性を持つ複数のオブジェクトを作るのではなく、状態を共有した 1 つのオブジェクトを再利用する。
※https://recursionist.io/dashboard/course/32/lesson/1133 より画像引用

デザインパターン
構造パターン(structural pattern)
【プロキシパターン(proxy pattern)】
元のオブジェクトの代用として機能するプロキシ(代理人)オブジェクトを作成することによって、オブジェクトへのアクセスを制御する方法を提供するもの。
コードで使いたいオブジェクトがあるが、何らかの理由で直接アクセスしたくないような場合に使用する。
プロキシオブジェクトは、元のオブジェクトと同じような外観と動作をするが、キャッシュやロギングなどの追加機能を提供したり、元のオブジェクトへのアクセスを制限することができる。
※https://recursionist.io/dashboard/course/32/lesson/1133 より画像引用

デザインパターン
行動パターン(behavioral pattern)
行動パターンは、オブジェクトが互いにどのように通信し、メッセージを受け渡しするかに焦点を当てる。
オブジェクトやクラスがどのように相互作用して特定の行動を実現するかに焦点を当てたデザインパターンのグループ。
【責任連鎖パターン(chain of responsibility pattern)】
複数のオブジェクトがリクエストを処理することを可能にする。
リクエストは連鎖的に次のオブジェクトに渡され、どれかのオブジェクトがリクエストを処理するまで連鎖が続く。具体的には、一つのリクエストが最初のオブジェクトに送られ、それがリクエストを処理できる場合は処理を行なう。もし処理できない場合は、リクエストは次のオブジェクトに渡され、このプロセスがリクエストが処理されるまで続く。
※https://recursionist.io/dashboard/course/32/lesson/1134 より画像引用

デザインパターン
行動パターン(behavioral pattern)
【コマンドパターン(command pattern)】
コマンドパターンは、アクションのリクエストとそのアクションを実行するオブジェクトを分離する。
リクエストをオブジェクトとしてカプセル化することで、異なる引数でパラメータ化したり、異なるタイミングで実行したりすることができる。
このパターンでは、リクエストと関連するパラメータをカプセル化した Command オブジェクトと、Command オブジェクトを呼び出してリクエストを実行する Invoker オブジェクトに分離される。
Command オブジェクトは、実行した操作を元に戻したり、やり直したりする機能を持つことができ、undo/redo 機能の実装に有用。
※https://recursionist.io/dashboard/course/32/lesson/1134 より画像引用

デザインパターン
行動パターン(behavioral pattern)
【インタープリタパターン(interpreter pattern)】
特定の言語で書かれた文を解釈して実行する方法を提供する。
例えば、計算式やプログラムコードなど、ある特定の言語で表現された「文」を、それを理解して処理できるようにするためのパターン。
具体的には、独自の小さな言語を作成して、それを用いて特定のタスクを解決するケースがある。インタープリタパターンは、その言語で書かれた文や命令を、プログラムが理解して実行できるようにする役割を持っている。
※https://recursionist.io/dashboard/course/32/lesson/1134 より画像引用

デザインパターン
行動パターン(behavioral pattern)
【イテレータパターン(iterator pattern)】
コレクションの要素に順番にアクセスする方法を提供し、その基礎となる実装を公開せずに済むようにする。イテレータパターンを使用すると、コレクションの内部構造を知らなくても、その要素を効率的に探索できる。これは、配列、リスト、木構造など、さまざまなデータ構造に対して同じインターフェースでアクセスできるようにするためのパターン。
※https://recursionist.io/dashboard/course/32/lesson/1134 より画像引用

デザインパターン
行動パターン(behavioral pattern)
【メディエータパターン(mediator pattern)】
メディエータと呼ばれるオブジェクトを定義し、他のオブジェクト同士のコミュニケーションの仲介役として動作させるもの。オブジェクト同士が直接コミュニケーションを行なうのではなく、メディエータにメッセージを送り、メディエータが適切なオブジェクトまたはオブジェクトにメッセージを転送する。
メディエータパターンの利用により、オブジェクト同士の依存関係が緩和され、それぞれのオブジェクトが独立して動作することができる。これにより、システムの柔軟性とメンテナンス性が向上し、オブジェクトの再利用も容易になる。
※https://recursionist.io/dashboard/course/32/lesson/1134 より画像引用

デザインパターン
行動パターン(behavioral pattern)
【メメントパターン(memento pattern)】
メメントパターンは、オブジェクトの内部状態をカプセル化を侵害することなくキャプチャして保存し、後で必要に応じてその状態を復元する方法を提供する。
このパターンは、元に戻す・やり直す機能の実装や、オブジェクトの状態を特定のポイントでチェックして、状態の変更やエラーから復元する必要がある場合によく使われる。
※https://recursionist.io/dashboard/course/32/lesson/1134 より画像引用

デザインパターン
行動パターン(behavioral pattern)
【オブザーバパターン(observer pattern)】
オブジェクト間に一対多の関係を定義し、あるオブジェクトの状態が変わった際に、その依存オブジェクトすべてに自動的に通知して更新される仕組みを提供する。
これは「パブリッシュ/サブスクライブ」としても知られている。
このパターンは、オブジェクトの状態の変化を他のオブジェクトに効率的に伝える必要がある場合、特にリアルタイムでの更新が求められる場合に有効。これにより、システム内のオブジェクトが互いに密に結びつくことなく、状態の変更を効率的に管理と共有することができる。
※https://recursionist.io/dashboard/course/32/lesson/1135 より画像引用

デザインパターン
行動パターン(behavioral pattern)
【ステートパターン(state pattern)】
オブジェクトの内部状態が変更された際に、そのオブジェクトの振る舞いを変更させることができる。これにより、オブジェクトがクラスを変更したかのように見える。
このパターンにより、状態に依存したコードを各状態のクラスにカプセル化することができ、メインのオブジェクト自体は、状態の変更によって振る舞いを変更することができる。
※https://recursionist.io/dashboard/course/32/lesson/1135 より画像引用

デザインパターン
行動パターン(behavioral pattern)
【ストラテジーパターン(strategy pattern)】
アルゴリズムの家族を定義してカプセル化し、それらを互換性を持って交換可能にするもの。
異なるアルゴリズムが使用されても、クライアントは同じ出力を受け取ることができる。
具体的には、ストラテジーパターンを使用すると、特定のタスクを実行するための複数のアルゴリズムや方法を、同一のインターフェースまたは抽象クラスにカプセル化して定義する。クライアントは、このインターフェースまたは抽象クラスを通じてアルゴリズムを利用するため、アルゴリズムの内部実装に依存することなく、同じ方法でタスクを実行することができる。
※https://recursionist.io/dashboard/course/32/lesson/1135 より画像引用

デザインパターン
行動パターン(behavioral pattern)
【テンプレートメソッドパターン(template method pattern)】
スーパークラスでアルゴリズムの骨格を定義し、サブクラスでそのアルゴリズムの特定のステップをカスタマイズできるようにするもの。これにより、アルゴリズムの構造自体は変更せずに、その一部を変更・拡張できる。
※https://recursionist.io/dashboard/course/32/lesson/1135 より画像引用

アンチパターン(anti-pattern)
優れたソフトウェア設計の原則に反するため、避けるべきパターン群も存在し、スパゲッティコード、神オブジェクト、金色のハンマー、マジックナンバー、散弾銃手術などのパターンがある。
大きな泥の塊(big ball of mud)
コードが絡み合って無秩序になったソフトウェアシステムを表すのに使われる用語。
開発者が適切な計画やアーキテクチャを持たずに、システムにどんどん機能や特徴を追加していくため、時間の経過とともに徐々に発生するのが一般的。
その結果、理解、維持、更新が難しく、バグやエラーが発生しやすいシステムになってしまう。
また、泥の塊のようなシステムは、そのプロセスを導く明確な組織やアーキテクチャがないため、リファクタリングや再構築が困難な場合もある。
スパゲッティコード(spaghetti code)
非常に複雑に絡み合ったソースコードのことで、理解、修正、保守が困難なソースコードを表す言葉として使われている。スパゲッティには、複雑に絡み合った多くの繊維があるように、スパゲッティコードには、複雑で相互依存的なプログラミング構成要素、関数、モジュールが多数あり、追跡やデバッグが困難になる。
スパゲッティコードは、プログラマがコードを書く際に、整理が不十分であったり、理解しにくい複雑な論理を使用していたりする場合に発生することが多い。また、コードベースを適切に再構築せずに多くの変更を加えた結果、コードが絡み合ってしまうこともある。
スパゲッティコードは、エラーやバグを引き起こすだけでなく、他のプログラマがそのコードを扱うことを困難にするため、一般に悪い習慣と考えられている。
神オブジェクト(god object)
責任や機能が大きすぎるオブジェクトやクラスのことを指す。万能で全知全な存在として認識されるため、理解、保守、テストが難しくなることから神オブジェクトと呼ばれている。
1 つのオブジェクトが多くの機能を担当しすぎると、オブジェクトが変化する理由は一つであるべきという「単一責任」の原則に反することになる。
このため、コードベースが密結合になり、神オブジェクトに加えられた変更が、システムの他の部分で意図しない結果をもたらす可能性がある。また、神オブジェクトの複雑な相互作用や依存関係を完全に理解していない可能性があるため、他の開発者が同じコードベースで作業する際の妨げになる可能性がある。
金色のハンマー(golden hammer)
開発者のアンチパターンの一つで、特定の技術、ツール、またはアプローチを、それが状況に最適かどうかにかかわらず、すべての問題に対して使用することを指す。
これは、非効率的で効果のないソリューションにつながり、より適したツールやテクノロジーを使う機会を逃すことになりかねない。金色のハンマーという名前は、「ハンマーさえあれば、すべてが釘に見える」ということわざに由来している。
マジックナンバー(magic number)
説明や文脈なしにプログラムのソースコードに直接現れる数値のこと。
特定の日付や最大ファイルサイズなど、特定の値や条件を表すために使用されることが多い。
しかし、このようにマジックナンバーを使用すると、コードが読みにくくなり、理解しにくくなり、保守しにくくなることがある。
代わりに、これらの値に対して名前付きの定数や列挙を定義することが一般的に良い習慣と考えられており、これによりコードがより読みやすくなり、変更も容易になる。これらの値に意味のある名前を付けることで、コードを読んだ他の開発者は、その文脈や目的を解読することなく、数値が何を表しているかをすぐに理解することができる。
また、マジックナンバーが矛盾して使用されたり、コード内の複数の場所で更新する必要がある場合に発生するエラーを防ぐことができる。
散弾銃手術(shotgun surgery)
開発者が新機能を実装したりバグを修正したりするために、複数のファイルにわたって複数の変更を行なう必要がある場合に発生するアンチパターンを表す用語。
これは、コードが適切にモジュール化されていない、あるいは整理されていないことを示すサイン。
一箇所に的を絞って変更を加えるのではなく、開発者は散弾銃のようにコードベース全体に変更を「撃ち込む」必要があり、時間がかかり、エラーが発生しやすく、新しいバグが発生するリスクが高くなる。
また、このようなアプローチでは、変更を追跡したり、コードの異なる部分が互いにどのように関連しているかを理解することが難しくなる。
ソフトウェアに使用するパターンを選択する場合
そのパターンが解決しようとする問題にマッチしているか、ソフトウェアの目的に関連しているか、そのパターンを使用することで起こりうる結果が意図した目標に合致しているかを確認することが重要。
もし、あるパターンが最適かどうかわからない場合は、他の選択肢を検討し、それぞれの利点と欠点を評価した上で決定するのがよい。