デザインパターンは、常に発生する特定の状況における特定の問題に対して繰り返し使用できる特定の解決策パターン (ルーチン) です。この記事では、適用可能なシナリオ、ソリューション、およびそれらに対応する Java 実装を含め、作成、構造、動作の 3 つのカテゴリに従って 24 の一般的な設計パターンを使用するための重要なポイントをまとめています。
デザインパターンとは、常に現れる特定の「文脈」における、特定の「問題」に対する特定の「解決策」です。 " 繰り返し適用できる必要があります。
"問題" には "目標" と "一連の制約" が含まれています。解決策が 2 つのバランスをとっている場合、それは有用なパターンです。
デザイン パターンはそうではありません法的ガイドラインは単なるガイドラインであり、他の人が理解できるようにメモを作成するだけで済みます。
;
6つの原則をまとめた英語の頭文字がSOLID(安定)であることから、SOLID原則とも呼ばれます。
クラスを変更する理由は決して 1 つだけであってはなりません。
1 つのクラスに複数の責任が結合されているのではなく、クラスには 1 つの責任しかありません (インターフェイスとロジックなど)。分離されています)。
拡張に対してオープン、変更に対してクローズ、インターフェイスと抽象クラスを使用します。
親クラスが出現できる場所には、サブクラスも出現できることを確認してください。継承と再利用の基礎。
依存性が低く、各エンティティは可能な限り独立しており、相互作用は最小限に抑えられます。
クライアントは、使用しないメソッドに依存すべきではありません。複数の機能を 1 つのインターフェイスに結合するのではなく、複数のインターフェイスを使用して機能を分割したり組み合わせたりするようにしてください。
両方とも抽象化に依存すべきではありません。詳細は抽象化ではなく抽象化(インターフェイスまたは抽象クラス)に依存する必要があります。コンクリート (コンクリート クラス)。
デザインパターンは一見単純な問題を複雑にします。ただし、「シンプル」設計は柔軟性が低く、現在のプロジェクトで拡張するのに不便で、他のプロジェクトで使用できないため、「ワンタイム コード」に相当します。デザインパターンのコードは明確な構造を持ち、現在のプロジェクトでの拡張が容易であり、他のプロジェクトにも適用可能であり、ユニバーサルデザインです。
多くのプログラマーは、デザインパターンに触れると、後日生まれ変わったように感じ、新たなレベルに到達したように感じます。デザインパターンは、プログラマーのレベルを分ける基準として使用されます。
ただし、パターンの罠に陥って、それを使用するためにパターンを適用することはできません。そうしないと、形式主義に陥ってしまいます。
各設計パターンには、いくつかの OO 原則が含まれており、選択できる適切な設計パターンがない場合は、OO 原則に戻って選択することができます
(3) チームはコミュニケーションにデザインパターンを使用するため、互いの意見を誤解することは容易ではありません。
1.5 重要な本
著者: Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides、後に「Gang of Four」(GoF) として知られています。本が 2 冊あります:
一読を強くお勧めします。英語タイトルは「Head First Design Patterns」。
イエスを信じる人は聖書を読まなければなりませんし、OO(オブジェクト指向)を信じる人はこの4人による「Head First Design Patterns」、公式ウェブサイトHead First Design Patternsを読む必要があります。 2004 年、この本はジョルト賞 (映画分野のオスカーに似ています) を受賞しました。
は、初めてパターンを分類し、ソフトウェア分野で大きな進歩を開始した人物です。
パターン テンプレート: 名前、カテゴリ、意図、動機、適用可能性、クラス図、参加者とそのコラボレーションが含まれます。 、結果、実装、サンプルコード、既知のアプリケーション、関連パターンなど。
英語のタイトルは「Design Patterns: Elements of Reusable Object-Oriented Software」です。こちらもカルテットによる作曲。
は、ソフトウェア エンジニアリングの分野におけるソフトウェア設計に関する本で、ソフトウェア設計パターンと呼ばれる、いくつかの一般的なソフトウェア設計の問題に対する標準的な解決策を提案および要約しています。この本は 1994 年 10 月 21 日に初版が発行され、2012 年 3 月の時点で 40 部印刷されました。
デザイン パターンは 3 つの主要なカテゴリに分類でき、各カテゴリには多数の特定のパターンが含まれています。
混同されやすいいくつかのパターン: 単純なファクトリ S / 抽象ファクトリ A / ファクトリ メソッド F / テンプレート メソッド T
「ファクトリ」: これを使用すると、S/A/F などのインスタンスを作成するためにのみ使用されます。 T;
などの単語「メソッド」を含むものには制限はありません。これを含むものは追加のクライアントの参加を必要とせず、独立して動作できます。たとえば、F/T を含まないものは追加のクライアントを必要とします。 S/A などのコール。
は、オブジェクトの作成作業を別のオブジェクトに置くか、サブクラスに延期します。
クラスにインスタンスが 1 つだけあることを確認し、グローバル アクセス ポイントを提供します。各クラスローダーには独自の独立した名前空間があるため、複数のクラスローダーでシングルトンを使用すると、各タイプのローダーでシングルトンインスタンスが作成されることに注意してください。
JDK のシングルトンには、Runtime.getRuntime()
と NumberFormat.getInstance()
が含まれます。
以下は、4 つのスレッドセーフな Java 実装メソッドをまとめたものです。各実装は、Singleton.getInstance().method();
で呼び出すことができます。 Runtime.getRuntime()
、NumberFormat.getInstance()
下面总结了四种线程安全的 Java 实现方法。每种实现都可以用 Singleton.getInstance().method();
调用。
关键思路:作为类的静态全局变量,加载该类时实例化。
缺点是真正使用该实例之前(也有可能一直没用到),就已经实例化,浪费资源。
对于 Hotspot VM,如果没涉及到该类,实际上是首次调用 getInstance() 时才实例化。
/** * @author: kefeng.wang * @date: 2016-06-07 10:21 **/public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } // 基于 classLoader 机制,自动达到了线程安全的效果 public static Singleton getInstance() { return instance; } public void method() { System.out.println("method() OK."); } }
关键思路:在方法 getInstance() 上实现同步。
缺点是每次调用 getInstance() 都会加锁,但实际上只有首次实例化时才需要,后续的加锁都是浪费,导致性能大降。
/** * @author: kefeng.wang * @date: 2016-06-07 10:21 **/public class Singleton { private static Singleton instance = null; private Singleton() { } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } public void method() { System.out.println("method() OK."); } }
关键思路:不同步的情况下检查到尚未创建,再同步检查到尚未实例化时,才实例化。以便大大减少同步的情况。
缺点是:要求 JDK5+,否则许多 JVM 对 volatile 的实现导致双重加锁失效。不过现在极少开发者会用 JDK5,所以该缺点关系不大。
/** * @author: kefeng.wang * @date: 2016-06-07 10:21 **/public class Singleton { private volatile static Singleton instance = null; // 注意 volatile private Singleton() { } public static Singleton getInstance() { if (instance == null) { // 初步检查:尚未实例化 synchronized (Singleton.class) { // 再次同步(对 Singleton.class) if (instance == null) { // 确认尚未实例化 instance = new Singleton(); } } } return instance; } public void method() { System.out.println("method() OK."); } }
关键思路:全局静态成员放在内部类中,只有该内部类被引用时才实例化,以达到延迟实例化的目的。这是个完美方案:
确保延迟实例化至 getInstance() 的调用;
无需加锁,性能佳;
不受 JDK 版本限制。
/** * @author: kefeng.wang * @date: 2016-06-07 10:21 **/public class Singleton { private static class InstanceHolder { // 延迟加载实例 private static Singleton instance = new Singleton(); } private Singleton() { } public static Singleton getInstance() { return InstanceHolder.instance; } public void method() { System.out.println("method() OK."); } }
将对象的创建过程,封装到一个生成器对象中,客户按步骤调用它完成创建。
Java 实现请参考 StringBuilder
的源码,这里给出其使用效果:
StringBuilder sb = new StringBuilder(); sb.append("Hello world!").append(123).append('!'); System.out.println(sb.toString());
不是真正的“设计模式”。自身是工厂实现类,直接提供创建方法(可多个),可以是静态方法。JDK 中有 Boolean.valueOf(String)
、Class.forName(String)
。
/** * @author: kefeng.wang * @date: 2016-06-09 19:42 **/public class DPC3_SimpleFactoryPattern { private static class SimpleFactory { public CommonProduct createProduct(int type) { // 工厂方法,返回“产品”接口,形参可无 if (type == 1) { return new CommonProductImplA(); // 产品具体类 } else if (type == 2) { return new CommonProductImplB(); } else if (type == 3) { return new CommonProductImplC(); } else { return null; } } } private static class SimpleFactoryClient { private SimpleFactory factory = null; public SimpleFactoryClient(SimpleFactory factory) { this.factory = factory; } public final void run() { CommonProduct commonProduct1 = factory.createProduct(1); CommonProduct commonProduct2 = factory.createProduct(2); CommonProduct commonProduct3 = factory.createProduct(3); System.out.println(commonProduct1 + ", " + commonProduct2 + ", " + commonProduct3); } } public static void main(String[] args) { SimpleFactory factory = new SimpleFactory(); // 工厂实例 new SimpleFactoryClient(factory).run(); // 传入客户类 } }
一个抽象类,定义创建对象的抽象方法。继承后的多个实现类中,实现创建对象的方法。
客户端灵活选择实现类,完成对象的创建。
JDK 中采用此模式的有 NumberFormat.getInstance()
。
创建方法的对于抽象类和实现类的分工,与“抽象工厂”类似。
区别在于:本模式无需客户端,自身方法即可完成对象创建前后的操作。
当创建实例的过程很复杂或很昂贵时,可通过克隆实现。比如 Java 的 Object.clone()
StringBuilder
のソース コードを参照してください。 使用上の効果は次のとおりです: 🎜rrreee🎜2.1.3 Simple Factory (Simple Factory) ★🎜🎜 は実際の「デザイン パターン」ではありません。 。これはそれ自体がファクトリ実装クラスであり、静的メソッドである作成メソッド (複数可) を直接提供します。 JDK には Boolean.valueOf(String)
と Class.forName(String)
があります。 🎜rrreee🎜2.1.4 抽象ファクトリー ★🎜🎜オブジェクトを作成するための抽象メソッドを定義する抽象クラスです。オブジェクトを作成するためのメソッドを複数の継承された実装クラスに実装します。 🎜クライアントは柔軟に実装クラスを選択し、オブジェクトの作成を完了できます。 🎜このモードを使用する JDK は NumberFormat.getInstance()
です。 🎜🎜2.1.5 ファクトリメソッド ★🎜🎜メソッド作成における抽象クラスと実装クラスの分業は「抽象ファクトリ」と似ています。 🎜 違いは、このモードはクライアントを必要とせず、独自のメソッドでオブジェクト作成の前後に操作を完了できることです。 🎜🎜2.1.6 プロトタイプ🎜🎜 インスタンスの作成プロセスが複雑またはコストがかかる場合は、クローン作成によって実現できます。たとえば、Java の Object.clone()
です。 🎜は、クラスやオブジェクトの組み合わせ関係に使用されます。
あるインターフェースを別の予想されるインターフェースに適応させると、インターフェースの不一致によって引き起こされる互換性の問題を排除できます。
たとえば、Enumeration<E>
を Iterator<E>
に、Arrays.asList()
を T[] に適応させます。 code> は <code>List<T>
に適合します。 Enumeration<E>
适配成 Iterator<E>
,Arrays.asList()
把 T[]
适配成 List<T>
。
事物由多个因子组合而成,而每个因子都有一个抽象类和多个实现类,最终这多个因子可以自由组合。
比如多种遥控器+多种电视机、多种车型+多种路况+多种驾驶员。JDK 中的 JDBC
和 AWT
。
把对象的“部分/整体”以树形结构组织,以便统一对待单个对象或多个对象组合。
比如多级菜单、二叉树等。
运行时动态地将职责附加到装饰者上。
扩展功能有两种方式,类继承是编译时静态决定,而装饰者模式是运行时动态决定,有独特优势。
比如 StringReader
被 LineNumberReader
装饰后,为字符流扩展出了 line
相关接口。
提供了一个统一的高层接口,用来访问子系统中的一群接口,让子系统更容易使用。
比如电脑的启动(或关闭),是调用CPU/内存/磁盘各自的启动(或关闭)接口。
运用共享技术有效地支持大量细粒度的对象。
比如文本处理器,无需为每个字符的多次出现而生成多个字形对象,而是外部数据结构中同一字符的多次出现共用一个字形对象。
JDK 中的 Integer.valueOf(int)
就采用此模式。
proxy 创建并持有 subject 的引用,client 调用 proxy 时,proxy 会转发给 subject。
比如 Java 里的 Collections
集合视图、RMI/RPC 远程调用、缓存代理、防火墙代理等。
用于类或对象的调用关系。
一个请求沿着一条链传递,直到该链上的某个处理者处理它为止。
比如 SpringMVC 中的过滤器。
将命令封装为对象,可以随意存储/加载、传递、执行/撤消、排队、记录日志等,将“动作的请求者”从“动作的执行者”中解耦。
参与方包括 Invoker(调用者) => Command(命令) => Receiver(执行者)。
比如定时任务、线程任务 Runnable
。
用于创建简易的语言解释器,可处理脚本语言和编程语言,为每个规则创建一个类。
比如 JDK 中的 java.util.Pattern
、java.text.Format
。
提供一种方法,顺序访问一个聚合对象中的各个元素,而无需暴露其内部表现。
比如 JDK 中的 java.util.Iterator
和 java.util.Enumeration
。
使用一个中介对象,封装一系列的对象交互,中介对象使各对象无需显式相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
比如 JDK 中的 java.util.Timer
和 java.util.concurrent.ExecutorService.submit()
。
备忘录对象用来存储另一个对象的内部状态的快照,并可在外部存储起来,之后可还原到当初的状态。比如 Java 序列化。
比如 JDK 中的 java.util.Date
和 java.io.Serializable
。
对象间一对多的依赖,被观察者状态改变时,观察者都会收到通知。
参与方包括 Observable(被观察者) / Observer(观察者)。
比如 RMI 中的事件、java.util.EventListener
。
对象的内部状态变化时,其行为也随之改变。其内部实现是,定义一个状态父类,为每种状态扩展出状态子类,当对象内部状态变化时,所选的状态子类也跟着切换,但外部只需与该对象交互,而不知道状态子类的存在。
比如视频播放器的停止/播放/暂停等状态。
定义一组算法,分别封装起来,独立于客户之外,算法更改时不影响客户使用。
比如游戏中的不同角色,可以使用各种装备,这些装备可以策略的方式封装起来。
比如 JDK 中的 java.util.Comparator#compare()
JDBC
と AWT
。 🎜🎜2.2.3 複合 ★🎜🎜単一のオブジェクトまたは複数のオブジェクトの組み合わせを均一に扱えるように、オブジェクトの「部分/全体」をツリー構造に整理します。 🎜マルチレベルメニュー、バイナリツリーなど。 🎜🎜2.2.4 デコレーター🎜🎜 実行時に責任をデコレーターに動的に割り当てます。 🎜 関数を拡張するには 2 つの方法があります。クラスの継承はコンパイル時に静的に決定されますが、デコレーター モードは実行時に動的に決定され、これには独自の利点があります。 🎜たとえば、StringReader
が LineNumberReader
で装飾された後、line
関連のインターフェイスが文字ストリーム用に拡張されます。 🎜🎜2.2.5 ファサード ★🎜🎜 サブシステム内のインターフェイスのグループにアクセスするための統合された高レベルのインターフェイスを提供し、サブシステムを使いやすくします。 🎜たとえば、コンピュータの起動 (またはシャットダウン) は、CPU/メモリ/ディスクのそれぞれの起動 (またはシャットダウン) インターフェイスを呼び出します。 🎜🎜2.2.6 Flyweight🎜🎜共有テクノロジーを使用して、多数のきめの細かいオブジェクトを効果的にサポートします。 🎜たとえば、テキスト プロセッサは、各文字の複数の出現に対して複数のグリフ オブジェクトを生成する必要はありません。代わりに、外部データ構造内の同じ文字の複数の出現が 1 つのグリフ オブジェクトを共有します。 JDK の 🎜Integer.valueOf(int)
はこのモードを採用しています。 🎜🎜2.2.7 プロキシ🎜🎜プロキシは、クライアントがプロキシを呼び出すと、それをサブジェクトに転送します。 🎜たとえば、Java の Collections
コレクション ビュー、RMI/RPC リモート呼び出し、キャッシュ プロキシ、ファイアウォール プロキシなどです。 🎜🎜2.3 動作パターン🎜🎜は、クラスまたはオブジェクトの呼び出し関係に使用されます。 🎜🎜2.3.1 責任の連鎖🎜🎜 リクエストは、チェーン上のプロセッサが処理するまでチェーンに沿って渡されます。 🎜たとえば、SpringMVC のフィルター。 🎜🎜2.3.2 Command (コマンド) 🎜🎜 コマンドをオブジェクトとしてカプセル化し、保存/ロード、転送、実行/元に戻す、キューに入れる、ログに記録するなどの操作が可能で、「アクションの要求者」と「アクションの要求者」が分離されます。 「アクションの実行者」 分離されました。 🎜参加者には、Invoker (呼び出し元) => Command (コマンド) => Receiver (実行者) が含まれます。 🎜たとえば、スケジュールされたタスクとスレッド タスクは実行可能
です。 🎜🎜2.3.3 インタプリタモード(Interpreter)🎜🎜は、スクリプト言語とプログラミング言語を処理できる単純な言語インタプリタを作成するために使用され、ルールごとにクラスを作成します。 🎜たとえば、JDK の java.util.Pattern
、java.text.Format
などです。 🎜🎜2.3.4 Iterator (反復子)🎜🎜内部表現を公開せずに集合オブジェクト内の個々の要素に順次アクセスするメソッドを提供します。 🎜たとえば、JDK の java.util.Iterator
と java.util.Enumeration
です。 🎜🎜2.3.5 メディエーター🎜🎜 仲介オブジェクトを使用して、一連のオブジェクトの相互作用をカプセル化します。これにより、オブジェクトが相互に明示的に参照する必要がなくなり、相互作用が緩和され、独立して変更できるようになります。 🎜たとえば、JDK の java.util.Timer
と java.util.concurrent.ExecutorService.submit()
です。 🎜🎜2.3.6 Memento🎜🎜memento オブジェクトは、別のオブジェクトの内部状態のスナップショットを保存するために使用され、外部に保存でき、後で元の状態に復元できます。 Java のシリアル化など。 🎜たとえば、JDK の java.util.Date
と java.io.Serializable
です。 🎜🎜2.3.7 オブザーバー🎜🎜 オブジェクト間の 1 対多の依存関係。監視対象のオブジェクトのステータスが変化すると、オブザーバーに通知されます。 🎜参加者にはObservable/Observerが含まれます。 🎜たとえば、RMI のイベント、java.util.EventListener
。 🎜🎜2.3.8 状態🎜🎜オブジェクトの内部状態が変化すると、その動作も変化します。その内部実装では、状態の親クラスを定義し、各状態の状態サブクラスを拡張します。オブジェクトの内部状態が変化すると、選択された状態サブクラスも切り替わりますが、外部はオブジェクトと対話するだけでよく、そのことはわかりません。状態サブクラスの存在。 🎜たとえば、ビデオプレーヤーの停止/再生/一時停止ステータス。 🎜🎜2.3.9 戦略🎜🎜 個別にカプセル化され、顧客から独立した一連のアルゴリズムを定義します。アルゴリズムが変更されても、顧客の使用には影響しません。 🎜たとえば、ゲーム内のさまざまなキャラクターはさまざまな装備を使用でき、これらの装備は戦略的な方法でパッケージ化できます。 🎜たとえば、JDK の java.util.Comparator#compare()
です。 🎜 最上位の論理フレームワーク (「テンプレート メソッド」と呼ばれる) は、抽象クラスで定義されます。一部のステップ (インスタンスやその他の操作を作成できる) は、サブクラスの実装に遅れます。そして独立して動作することができます。
サブクラスで実装される操作がインスタンスの作成である場合、テンプレートメソッドはファクトリメソッドのパターンとなるため、ファクトリメソッドは特殊なテンプレートメソッドとなります。
訪問者のデータ構造を変更せずに、訪問者がアクセス操作をカプセル化することが重要なポイントです。
該当するシナリオは、訪問者が安定しているが柔軟であるか、訪問者がさまざまな種類の操作を行っている場合です。
2 つ以上のモードを組み合わせてソリューションを形成し、よく発生する一般的な問題を解決します。
ユースケース: MVC パターン (モデル/ビュー/コントローラー)、オブザーバー、ストラテジー、コンポジット、ファクトリー、デコレーター、その他のパターンを使用します。
ユースケース: 家電製品 = インターフェース + データ + ロジック制御、ショッピングモール = 店舗 + 倉庫 + ロジック制御。
Wikipedia: デザインパターン
Wikipedia: ソフトウェアデザインパターン
チュートリアルポイント: デザインパターン
デザインパターンは、常に発生する特定の状況における特定の問題に対して再利用できる特定の解決策パターン (ルーチン) です。この記事では、適用可能なシナリオ、ソリューション、およびそれらに対応する Java 実装を含め、作成、構造、動作の 3 つのカテゴリに従って 24 の一般的な設計パターンを使用するための重要なポイントをまとめています。
関連記事:
以上が24 の一般的な設計パターンとその Java 実装の主な使用ポイントをまとめます。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。