この記事は、 Lion Long によるHuawei クラウド コミュニティ「設計パターンと設計原則の詳細な分析: 保守可能でスケーラブルなコードの構築の重要性」から共有されたものです。
1. なぜデザインパターンが必要なのでしょうか?
1.1. デザインパターンの定義
デザインパターンは約23種類あります。
デザイン パターンとは、ソフトウェア開発の特定の環境で繰り返し発生する特定の問題に対する実証済みの解決策を指します。
定義からわかるように、デザイン パターンの使用には多くの制限があります。使用する前に、それがどのような問題を解決するのかを必ず理解してください。デザイン パターンがどのような問題を解決するかわからない場合は、安易にデザイン パターンを使用しないでください。
平たく言えば、デザイン パターンは、ソフトウェア開発プロセスにおけるいくつかの問題を解決するための固定ルーチンです。デザイン パターンを過度にカプセル化したり使用したりしないでください。デザイン パターンは、要件の変化の具体的な方向が明確で、方向の変化点が繰り返し現れる場合にのみ使用されます。つまり、デザイン パターンは慎重に使用してください。
デザイン パターンを熟達するには、ある程度の量のエンジニアリング コードが必要です。ただし、デザインパターンを理解する必要があります。
1.2. デザインパターンの由来
デザイン パターンの起源は、コンピュータ科学者のエリック ガンマらが初めて提案した 1980 年代に遡ります。彼らは、一般的な問題や設計上の課題に対する再利用可能なソリューションとして設計パターンを定義します。
デザイン パターンは、ソフトウェア開発におけるいくつかの一般的な問題を解決し、開発者が保守可能で拡張可能なコードをより効率的に作成できるようにするために登場しました。デザイン パターンを使用することで、開発者は以前の成功から学び、車輪の再発明を回避しながら、コードの読みやすさと理解しやすさを向上させることができます。
デザイン パターンの目標は、特定の状況における一般的な問題に対する実証済みのソリューションを提供することです。デザイン パターンは、特定のアルゴリズムやコードの一部ではなく、特定の状況におけるソリューションのテンプレートです。さまざまなプログラミング言語や開発環境に適用できます。
デザインパターンは通常、創造パターン、構造パターン、行動パターンの 3 つのタイプに分類されます。
- 作成パターンはオブジェクト作成メカニズムに焦点を当てています。
- 構造パターンは、オブジェクトの関係と組織に焦点を当てます。
- 行動パターンは、オブジェクト間の相互作用とコミュニケーションに焦点を当てます。
一般的な設計パターンには、シングルトン パターン、ファクトリ パターン、オブザーバー パターン、ストラテジ パターンなどがあります。
一言で言えば、設計原則を満たした後、ゆっくりと反復されるということです。
1.3. デザインパターンによって解決される問題
デザインパターンを使用するための前提条件: 特定の要件には、安定点と変化点の両方があります。
(1) 安定点、つまり変わらないもの。すべてが安定していれば、デザインパターンは必要ありません。
(2)変更点、つまり頻繁な変更。それらがすべて変化点であり、その変化に特定の方向性がない場合、デザインパターンは必要ありません。たとえば、ゲーム開発では、スクリプトを再コンパイルする必要がなく、ホット アップデートできるため、すべての変更を解決するためにスクリプト言語が使用されます。
デザイン パターンが特に問題を解決するシナリオ: 少量のコードを変更することで要件の変化に適応したいと考えています。たとえば、片付いた部屋に活発な猫がいる場合、部屋が片付いていることを確認するにはどうすればよいでしょうか? 猫をケージに入れて、限られた範囲内で猫が動けるようにします。
つまり、限られた範囲内で変化点が変化するようにデザインパターンを利用するのです。
2. デザインパターンの基礎
デザインパターンは開発言語に関連しており、言語の特性を利用してデザインパターンを実装します。
C++ の場合、設計パターンの基礎は次のとおりです。
(1) オブジェクト指向の思考。オブジェクト指向の 3 つの特徴は、カプセル化 (実装の詳細を隠し、モジュール性を実現することを目的としています)、継承 (元のクラスを変更せずに継承による機能拡張を実現することを目的としています。C++ は多重継承が可能です)、ポリモーフィズム (静的ポリモーフィズム) です。関数のオーバーロードです。関数名は同じですが、パラメーターが異なるため、同時に異なる形式が表示されます。動的ポリモーフィズムは、継承における仮想関数の書き換えです)。多くの設計機能は動的ポリモーフィズムに依存しています
(2) 設計原則。
2.1. C++ ポリモーフィズム仮想関数の書き換え
2 つの仮想関数を持つ基本クラスを想定します。
class Base{ public: virtual void func1(){} virtual void func2(){} int a; };
その仮想関数テーブルとメモリ レイアウトは次のとおりです。
現時点では、Base を継承するサブクラスがあります。
class Subject : public Base{ public: virtual void func2(){} virtual void func3(){} int b; };
その仮想関数テーブルとメモリ レイアウトは次のとおりです。
メモリ レイアウトからわかるように、仮想関数がある場合、そのクラスに対して仮想関数テーブル ポインタが生成されます。仮想関数テーブルはコンパイル中にコンパイラによって自動的に生成されます。仮想関数テーブルは実際には 1 次元の配列であり、配列の要素に格納された仮想関数のアドレスは、オフセットを通じて対応する関数を呼び出すことができます。
Base クラスの場合、仮想関数テーブルには func1 と func2 が含まれます。サブジェクトは Base を継承し、その仮想関数テーブルには Base の仮想関数もあり、仮想関数テーブル内の Base の仮想関数はサブジェクトの仮想関数の前にあります。 。
サブジェクトがベース仮想関数をオーバーライドしない場合、仮想関数テーブルに保存される仮想関数アドレスは同じになります (例の func1 など)。
サブジェクトが Base 仮想関数を書き換えると、仮想関数テーブルで置換が発生し、Base 内の対応する仮想関数のアドレスがサブジェクトの新しい仮想関数 (例の func2 など) のアドレスに置き換えられます。 )。
サブジェクトに独自の新しい仮想関数がある場合、それも仮想関数テーブルに追加する必要があります。
2.2. 多態性の反映
(1) 早期バインディング。Base *p=new Subject がある場合、Subject が Base 仮想関数をオーバーライドしない場合、Subject 型は早期バインディングである Base 型に変換されます。
(2) 遅延バインディング。Base *p=new Subject がある場合、Subject が Base 仮想関数を書き換える場合、p は実際には Subject オブジェクトを指しますが、これは遅延バインディングです。
2.3. 拡張方法
(1) 継承。
(2) 組み合わせ。
2.4. 多態性の組み合わせ
// 継承 クラス Subject : public Base{ }; // 結合 クラス Subject{ private: Base Base; };
デザイン パターンでの構成は、通常、基本クラス ポインターの構成を指します。利点は、Base の機能を拡張し、ポリモーフィズムを通じて組み合わせを分離できることです。
// 基底クラスのポインタを結合します class Subject{ private: Base *base; };
3. 設計原則
設計原則は、設計パターンが作成される前に存在するということです。設計原則は、複数世代のプログラマによって要約された開発原則です。
3.1. 依存関係の逆転
実装はインターフェイスに依存し、インターフェイスは抽象化に変換できます。つまり、特定の実装コードはこの抽象化に依存する必要があります。特定の使用インターフェース (クライアント) もこの抽象化に依存します。
高レベルのモジュールは低レベルのモジュールに依存すべきではなく、両方とも抽象化に依存する必要があります。
抽象化は具体的な実装に依存すべきではなく、具体的な実装は抽象化に依存する必要があります。
自動運転システム企業を頂点とし、自動車メーカーを下位とし、相互に依存するのではなく、一方が変われば他方も変わる、代わりに自動運転業界標準を抽象化し、両者を両立させるべきである。自動運転システムと自動車メーカーの二者間の変更はすべて具体的な実装であり、すべて自動運転業界標準に依存する必要があります (要約) 。
3.2. オープンとクローズ
クラスは拡張 (合成と継承) に対してオープンであり、変更に対してクローズである必要があります。カプセル化とポリモーフィズム用。
3.3. インターフェース指向
変数の型は、特定の具象クラスとしてではなく、インターフェイスとして宣言されます。クライアント プログラムは、オブジェクトの特定の型を知る必要はなく、オブジェクトが持つインターフェイスだけを知る必要があります。これにより、システムのさまざまな部分の依存関係が軽減されます。主にパッケージング向けに「高凝集・疎結合」タイプの設計ソリューションを実現します。
3.4. パッケージの変更
安定点と変化点を分離し、変化点を拡張・修正し、安定点と変化点の実装レベルを分離します。主にカプセル化とポリモーフィズム用です。
3.5. 単一の責任
クラスが変更される理由は 1 つだけである必要があります。主に梱包用です。
3.6. リヒターの置き換え
サブタイプはその親タイプを置き換えることができる必要があります。これは主に、サブクラスが親クラスの実装をオーバーライドするときに発生し、親タイプを使用する元のプログラムでエラーが発生する可能性があります。親クラスのメソッドはオーバーライドされますが、その責任は親にあります。クラスメソッドは実装されていません。
主にポリモーフィズムで仮想関数を書き換えることを目的としています。
3.7. インターフェースの分離
(1) クライアントは、使用していない方法への依存を強制されるべきではありません。
(2) 一般に、多くのインターフェイスを持つクラスを処理するために使用され、これらのインターフェイスには多くの責任が伴います。
(3) クライアントは、必要のないインターフェイスに依存すべきではありません。あるクラスの別のクラスへの依存関係は、最小のインターフェイスに基づく必要があります。
修飾子によって分離します。クラスはインターフェイスに依存し、クラスはインターフェイスを通じて分離されます。
3.8. 合成は継承よりも優れています
継承結合は高く、合成結合は低い。
3.9. 最低限の既知の原則
ユーザーが必要のないインターフェイスを選択しないようにしてください。
要約する
設計パターンの定義、分類、適用シナリオ、および設計原則の役割を紹介することで、ソフトウェア開発における設計原則の重要性が強調されます。デザイン パターンは、開発者が一般的な問題や設計上の課題を解決し、コードの可読性、理解性、保守性を向上させるのに役立つ再利用可能なソリューションを提供します。設計原則は、単一責任原則、オープンおよびクローズド原則などの設計パターンの指針を提供します。設計パターンと設計原則を適用することで、開発者は高品質で保守可能でスケーラブルなソフトウェア システムを構築し、作業の重複を避け、コードの再利用性と柔軟性を向上させることができます。
クリックしてフォローし、できるだけ早くHuawei Cloudの新しいテクノロジーについて学びましょう~
Lei Jun: Xiaomi の新しいオペレーティング システム ThePaper OS の正式版がパッケージ化されました。Gome App の抽選ページのポップアップ ウィンドウは創設者を侮辱しています。Ubuntu 23.10 が正式にリリースされました。金曜日を利用してアップグレードするのもいいでしょう! Ubuntu 23.10 リリース エピソード: ヘイトスピーチが含まれていたため、ISO イメージが緊急に「リコール」されました 23 歳の博士課程の学生が Firefox で 22 年間続いた「ゴーストバグ」を修正しました RustDesk リモート デスクトップ 1.2.3 がリリースされましたWayland を強化して TiDB 7.4 をサポート リリース: MySQL 8.0 と正式互換. Logitech USB レシーバーを取り外した後、Linux カーネルがクラッシュしました. マスターは Scratch を使用して RISC-V シミュレータをこすり、Linux カーネルを正常に実行しました. JetBrains が Writerside ツールを開始しました技術文書の作成に。