デザインパターンの概要、パート1:デザインパターンの履歴と分類

特にメンテナンスの分野で、ソフトウェアの設計を簡素化し、コストを削減するために、数多くの戦略が考案されています。再利用可能なソフトウェアコンポーネント(ソフトウェア集積回路と呼ばれることもあります)を識別して操作する方法を学ぶことは、1つの戦略です。デザインパターンの使用は別です。

この記事では、デザインパターンに関する3部構成のシリーズを紹介します。このパートでは、デザインパターンの概念フレームワークを紹介し、特定のユースケースのデザインパターンを評価するデモンストレーションをウォークスルーします。また、デザインパターンとアンチパターンの歴史についても説明します。最後に、過去数十年にわたって発見および文書化された、最も使用されているソフトウェアデザインパターンを分類して要約します。

デザインパターンとは何ですか?

既存のシステムをモデル化する再利用可能なオブジェクト指向ソフトウェアを設計することは、本当に困難です。ソフトウェア開発者は、システムのエンティティを、パブリックインターフェイスが過度に複雑でないクラスに分解し、クラス間の関係を確立し、継承階層を公開する必要があります。ほとんどのソフトウェアは作成後も長く使用されているため、ソフトウェア開発者は、コードとインフラストラクチャを将来のニーズを満たすのに十分な柔軟性を保ちながら、現在のアプリケーション要件に対処する必要もあります。

経験豊富なオブジェクト指向開発者は、ソフトウェアデザインパターンが安定した堅牢なソフトウェアシステムのコーディングを容易にすることを発見しました。新しいソリューションを最初から絶えず開発するのではなく、これらのデザインパターンを再利用することは効率的であり、エラーのリスクの一部を軽減します。すべてのデザインパターンは、特定のアプリケーションコンテキストで繰り返し発生するデザインの問題を特定し、さまざまなアプリケーションシナリオに適用できる一般化された再利用可能なソリューションを提供します。

デザインパターンは、特定のコンテキストで一般的なデザインの問題を解決するために使用されるクラスと相互作用するオブジェクトを記述します。」

一部の開発者は、デザインパターンをクラスエンコードされたエンティティ(リンクリストやビットベクトルなど)として定義しますが、他の開発者は、デザインパターンがアプリケーションまたはサブシステム全体にあると言います。私の見解では、デザインパターンは、特定のコンテキストで一般的なデザインの問題を解決するために使用されるクラスと相互作用するオブジェクトを記述します。より正式には、デザインパターンは、次の4つの基本要素で構成される説明として指定されます。

  1. 名前デザインパターンを説明し、私たちにそれを議論するための語彙を提供します
  2. 問題問題が発生する状況と一緒に解決される必要設計問題を識別する
  3. 問題の解決策。(ソフトウェアデザインパターンのコンテキストで)設計に寄与するクラスとオブジェクトを、それらの関係やその他の要因とともに特定する必要があります。
  4. デザインパターンを使用した場合の結果の説明

使用する適切なデザインパターンを特定するには、まず、解決しようとしている問題を明確に特定する必要があります。ここで、デザインパターン記述の問題要素が役立ちます。あるデザインパターンを別のデザインパターンよりも選択することは、通常、アプリケーションまたはシステムの柔軟性と将来のメンテナンスに影響を与える可能性のあるトレードオフを伴います。そのため、実装を開始する前に、特定のデザインパターンを使用した場合の結果を理解することが重要です。

デザインパターンの評価

ボタン、テキストフィールド、およびその他の非コンテナコンポーネントを使用して、複雑なユーザーインターフェイスを設計するタスクを検討してください。コンポジットデザインパターンは、コンテナーをコンポーネントと見なします。これにより、コンテナーとそのコンポーネント(コンテナーと非コンテナー)を他のコンテナー内にネストし、再帰的にネストできます。複合パターンを使用しないことを選択した場合、多くの特殊な非コンテナーコンポーネント(たとえば、パスワードテキストフィールドとログインボタンを組み合わせた単一のコンポーネント)を作成する必要がありますが、これは実現が困難です。

これを熟考したことで、解決しようとしている問題と、Compositeパターンによって提供される解決策を理解しました。しかし、このパターンを使用した結果はどうなりますか?

Compositeを使用すると、クラス階層でコンテナコンポーネントと非コンテナコンポーネントが混在することになります。より単純なクライアントは、コンテナコンポーネントと非コンテナコンポーネントを均一に扱います。また、新しい種類のコンポーネントをUIに導入するのが簡単になります。コンポジットはまた、過度に一般化された設計につながる可能性があり、コンテナに追加できるコンポーネントの種類を制限することが難しくなります。コンパイラーに依存して型制約を適用することはできないため、実行時型チェックを使用する必要があります。

実行時型チェックの何が問題になっていますか?

実行時型チェックには、ifステートメントとinstanceof演算子が含まれるためコードが脆弱になります。アプリケーション要件の進化に合わせて実行時型チェックを更新するのを忘れた場合、その後バグが発生する可能性があります。

適切なデザインパターンを選択して、誤って使用することも可能です。ダブルチェックロッキングパターンは、典型的な例です。ダブルチェックロックは、実際にロックを取得せずに最初にロック基準をテストし、チェックでロックが必要であることが示された場合にのみロックを取得することにより、ロック取得のオーバーヘッドを削減します。紙の上では見栄えがしましたが、JDK1.4のダブルチェックロックにはいくつかの隠れた複雑さがありました。JDK 5がvolatileキーワードのセマンティクスを拡張したとき、開発者はついにダブルチェックロックパターンのメリットを享受することができました。

ダブルチェックロックの詳細

「ダブルチェックロック:賢いが壊れている」および「ダブルチェックロックは修正できますか?」を参照してください。(Brian Goetz、JavaWorld)このパターンがJDK1.4以前で機能しなかった理由の詳細をご覧ください。JDK 5以降でのDCLの指定の詳細については、「「ダブルチェックロックが壊れている」宣言」(メリーランド大学コンピュータサイエンス学部、David Bacon他)を参照してください。

アンチパターン

デザインパターンが一般的に使用されているが、効果がない、および/または逆効果である場合、デザインパターンはアンチパターンとして知られています。JDK1.4以前で使用されていたダブルチェックロックはアンチパターンであると主張する人もいるかもしれません。その文脈では、それは単に悪い考えだったと思います。悪いアイデアがアンチパターンに進化するためには、次の条件を満たす必要があります(「参考文献」を参照)。

  • 最初は有益であるように見えますが、最終的には有益な結果よりも悪い結果をもたらす、アクション、プロセス、または構造の繰り返しパターン。
  • 明確に文書化され、実際に証明され、再現可能な代替ソリューションが存在します。

JDK 1.4のダブルチェックロックはアンチパターンの最初の要件を満たしていましたが、2番目の要件は満たしていませんでしsynchronizedた。マルチスレッド環境での遅延初期化の問題を解決するために使用できますが、そうすると、次の理由が無効になります。そもそもダブルチェックロックを使用します。

デッドロックアンチパターン

アンチパターンを認識することは、それらを回避するための前提条件です。デッドロックを引き起こすことで有名な3つのアンチパターンの紹介については、ObiEzechukwuの3部構成のシリーズをお読みください。

  • 仲裁なし
  • 労働者の集約
  • インクリメンタルロック

デザインパターンの履歴

デザインパターンは1970年代後半にさかのぼり、建築家クリストファーアレクサンダーと他の数人によるパターン言語:町、建物、建設が出版されました。この本は、建築の文脈でデザインパターンを紹介し、著者がパターン言語と呼んだものを集合的に形成した253のパターンを提示しました。

デザインパターンの皮肉

ソフトウェア設計に使用される設計パターンは、その始まりをAパターン言語にまでさかのぼりますが、このアーキテクチャ作業は、コンピュータプログラミングと設計を記述するために当時出現した言語の影響を受けました。

パターン言語の概念は、その後、1986年に発行されたドナルドノーマンとスティーブンドレーパーのユーザー中心システムデザインに登場しました。この本は、インタラクティブなデジタル製品、環境、システムを設計する実践であるインタラクションデザインへのパターン言語の適用を提案しました。、および人間が使用するためのサービス。

一方、ケントベックとウォードカニンガムは、パターンとそのソフトウェア設計への適用性について研究を始めていました。 1987年、彼らは一連の設計パターンを使用して、設計プロジェクトの完了に問題を抱えていたTektronixのSemiconductor Test SystemsGroupを支援しました。ベックとカニンガムは、ユーザー中心設計(プロジェクトのユーザーの代表者に設計結果を決定させる)に関するアレクサンダーのアドバイスに従い、作業を容易にするためのいくつかの設計パターンも提供しました。

Erich Gammaはまた、博士論文に取り組んでいる間、繰り返しデザインパターンの重要性を認識しました。彼は、デザインパターンが再利用可能なオブジェクト指向ソフトウェアを作成するタスクを容易にすることができると信じ、それらを効果的に文書化して伝達する方法を考えました。オブジェクト指向プログラミングに関する1991年の欧州会議の前に、ガンマとリチャードヘルムはパターンのカタログ化を開始しました。

1991年に開催されたOOPSLAワークショップでは、ガンマとヘルムにラルフ・ジョンソンとジョン・ブリシディーズが参加しました。このGangof Four(GoF)は、その後知られるように、人気のあるデザインパターン:再利用可能なオブジェクト指向ソフトウェアの要素を作成しました。これは、3つのカテゴリで23のデザインパターンを文書化したものです。

デザインパターンの現代的な進化

特にソフトウェア開発者がハードウェアとアプリケーションの要件の変更に関連する新しい課題に直面したため、デザインパターンは元のGoFブック以降進化し続けています。

1994年、ヒルサイドグループとして知られる米国を拠点とする非営利団体は、ソフトウェアデザインパターンの技術を開発および改良することを目的とした年次会議のグループであるPattern Languages ofProgramsを発足させました。これらの進行中の会議は、ドメイン固有のデザインパターンの多くの例を生み出しました。たとえば、同時実行コンテキストでのデザインパターン。

OOPSLAのクリストファーアレクサンダー

OOPSLA 96の基調講演は、建築家のクリストファーアレクサンダーによって行われました。アレクサンダーは、彼の仕事と、オブジェクト指向プログラミングコミュニティが、パターン言語に関する彼のアイデアをソフトウェアに採用および適応する際に、どのように成功し、失敗したかを振り返りました。アレクサンダーの演説を完全に読むことができます:「パターン理論の起源:理論の未来、そして生きている世界の生成」。

1998年、MarkGrandはPatternsinJavaをリリースしました。この本には、並行性パターンを含む、GoF本にはないデザインパターンが含まれていました。Grandはまた、統一モデリング言語(UML)を使用して、デザインパターンとそのソリューションを記述しました。この本の例は、Java言語で表現および説明されています。

分類によるソフトウェアデザインパターン

最新のソフトウェアデザインパターンは、その用途に基づいて、作成、構造、動作、および同時実行の4つのカテゴリに大まかに分類されます。各カテゴリについて説明してから、各カテゴリの顕著なパターンのいくつかをリストして説明します。

他のタイプのデザインパターン

より多くの種類のパターンがあると考えているなら、あなたは正しいです。このシリーズの後半の記事では、相互作用、アーキテクチャ、組織、およびコミュニケーション/プレゼンテーションパターンなどの追加のデザインパターンタイプについて説明します。

作成パターン

生成に関するパターンは、オブジェクトの作成、構成、およびそれらに依存しているコードからどのように表されるかを分離する、インスタンス化のプロセスを抽象化します。クラス作成パターンは継承を使用してインスタンス化されるクラスを変更し、オブジェクト作成パターンはインスタンス化を他のオブジェクトに委任します。

  • 抽象ファクトリ:このパターンは、具体的なクラスを指定せずに、共通のテーマを持つ個々のファクトリのグループをカプセル化するためのインターフェイスを提供します。
  • Builder:複雑なオブジェクトの構築をその表現から分離し、同じ構築プロセスでさまざまな表現を作成できるようにします。オブジェクト構築のステップを抽象化することで、ステップのさまざまな実装でオブジェクトのさまざまな表現を構築できます。
  • ファクトリメソッド:オブジェクトを作成するためのインターフェイスを定義しますが、サブクラスがインスタンス化するクラスを決定できるようにします。このパターンにより、クラスはインスタンス化をサブクラスに延期できます。依存性注入は関連するパターンです。(「参考文献」を参照してください。)
  • 遅延初期化:このパターンは、結果が最初に必要になるまで、オブジェクトの作成、データベースのルックアップ、または別の高価なプロセスを遅らせる方法を提供します。
  • マルチトン:シングルトンの概念を拡張して、名前付きクラスインスタンスのマップをキーと値のペアとして管理し、それらへのグローバルアクセスポイントを提供します。
  • オブジェクトプール:必要に応じて割り当てて破棄するのではなく、初期化されたオブジェクトのセットをすぐに使用できるようにしておきます。目的は、使用されなくなったオブジェクトをリサイクルすることにより、コストのかかるリソースの取得と再利用を回避することです。
  • プロトタイプ:プロトタイプインスタンスを使用して作成するオブジェクトの種類を指定し、このプロトタイプをコピーして新しいオブジェクトを作成します。プロトタイプインスタンスは、新しいオブジェクトを生成するために複製されます。
  • リソースの取得は初期化です:このパターンは、リソースを適切なオブジェクトの存続期間に結び付けることにより、リソースが自動的かつ適切に初期化および再利用されることを保証します。リソースは、オブジェクトの初期化中に、使用可能になる前に使用される可能性がないときに取得され、同じオブジェクトの破棄とともに解放されます。これは、エラーが発生した場合でも発生することが保証されています。
  • シングルトン:クラスにインスタンスが1つしかないことを確認し、このインスタンスへのグローバルアクセスポイントを提供します。