デザインパターン
- 再利用可能なオブジェクト指向ソフトウェアの基本
デザインパターンは、ほとんどの人に知られている繰り返し使用のセットであり、分類されたカタログ化、およびコードデザインエクスペリエンスです。設計パターンの使用は、コードを再利用し、他の人がコードを理解しやすくし、コードの信頼性を確保することです。デザインパターンが自分自身、他の人、システムにとってwin-winであることは間違いありません。デザインパターンにより、コードコンパイルが真にエンジニアリングされます。設計パターンは、建物のレンガや石のように、ソフトウェアエンジニアリングの基礎です。プロジェクトでのデザインパターンの合理的な使用は、多くの問題を完全に解決できます。各パターンには、対応する対応する原則があります。各パターンは、私たちの周りで絶えず繰り返されている問題と、それが広く使用できる理由でもある問題の中心的な解決策を説明しています。この章は、Java [ReokieからExpertへの進化]シリーズの美しさのデザインモデルです。この章では、理論と実践の組み合わせで研究します。プログラム愛好家が設計モデルをよく学び、優れたソフトウェアエンジニアになることを願っています!
1。設計パターンの分類
全体として、設計パターンは3つのカテゴリに分かれています。
作成モードには、ファクトリーメソッドモード、抽象的な工場モード、シングルトンモード、ビルダーモード、プロトタイプモードの5つのタイプがあります。
アダプターモード、デコレーターモード、プロキシモード、外観モード、ブリッジモード、コンビネーションモード、および楽しみモードの7つの構造モードがあります。
動作モード、合計11:ポリシーモード、テンプレートメソッドモード、オブザーバーモード、反復サブモード、責任チェーンモード、メモモード、ステータスモード、ビジターモード、メディエーターモード、インタープリターモード。
実際、同時モードとスレッドプールモードの2つのカテゴリがあります。写真を使用して全体として説明しましょう。
2。設計モデルの6つの原則
1。密接な原則を開きます
開閉の原則は、拡張機能を開放し、修正に近づけることです。プログラムを拡張する必要がある場合、ホットプラグ効果を達成するために元のコードを変更することはできません。だから一言で言えば、それは次のとおりです。プログラムをより拡張可能かつ維持し、アップグレードしやすくするためです。このような効果を達成するには、インターフェイスと抽象クラスを使用する必要があります。これについては、後で特定のデザインで言及します。
2。リスコフ置換原理
リスコフ代替原理LSPは、オブジェクト指向設計の基本原則の1つです。豊富な代替原則によると、どんな基地クラスでも表示される可能性があると述べています。サブクラスは間違いなく表示される可能性があります。 LSPは、相続と再利用の基礎です。デリバティブクラスがベースクラスを置き換え、ソフトウェアユニットの機能が影響を受けない場合にのみ、基本クラスを本当に再利用でき、デリバティブクラスはベースクラスに基づいて新しい動作を追加することもできます。リヒター置換の原則は、「オープンクロージング」原則の補足です。 「オープンクローズ」原則を実装するための重要なステップは抽象化です。基本クラスとサブクラスの相続関係は、抽象化の具体的な実装であるため、豊富な代替原理は、抽象化を実装するための特定の手順の標準化です。 - バイドゥ百科事典から
3。依存関係の反転原理
これは、開閉の原則の基礎です。特定のコンテンツ:インターフェイスの真のプログラミングは、コンクリートではなく抽象化に依存します。
4。インターフェイス分離原理
この原則は、次のことを意味します。複数の分離インターフェイスを使用すると、単一のインターフェイスを使用するよりも優れています。また、クラス間の結合度を減らすことも意味します。ここから、デザインパターンは、アップグレードとメンテナンスの利便性のために、実際には大規模なソフトウェアアーキテクチャから始まるソフトウェアの設計アイデアであることがわかります。したがって、上記の記事は何度も登場しました。依存を減らし、結合を減らすことです。
5。デメテル原理
なぜ知識のない原則があるのですか?つまり、1つのエンティティは、システム機能モジュールが比較的独立しているように、できる限り他のエンティティと対話する必要があります。
6。複合再利用原理
原則は、継承ではなく合成/集約方法を使用しようとすることです。
3。Javaの23のデザインパターン
このセクションから始めて、Javaの23の設計パターンの概念、アプリケーションシナリオなどを詳細に紹介し、それらの特性とデザインパターンの原則と組み合わせてそれらを分析します。
1。工場メソッド
工場メソッドモードには3つのタイプがあります。
11.通常の工場モデルは、工場クラスを確立し、同じインターフェイスを実装するいくつかのクラスのインスタンスを作成することです。最初に関係図を見てください:
たとえば:(メールやテキストメッセージを送信する例を見てみましょう)
まず、2つの間に共通のインターフェイスを作成します。
public interface sender {public void send(); }第二に、実装クラスを作成します。
パブリッククラスのメールセンダーは送信者{@Override public void send(){system.out.println( "これはmailsender!"); }} public class smssenderは送信者{@override public void send(){system.out.println( "これはsms sender!"); }}最後に、工場の建設:
public class sendfactory {public sender produce(string type){if( "mail" .equals(type)){return new Mailsender(); } else if( "sms" .equals(type)){return new smssender(); } else {system.out.println( "正しいタイプを入力してください!"); nullを返します。 }}}テストしましょう:
public class factorytest {public static void main(string [] args){sendfactory factory = new sendfactory();送信者sender = factory.produce( "sms"); sender.send(); }}出力:これはSMS送信者です!
22.複数の工場メソッドモードは、通常の工場メソッドモードの改善です。通常の工場メソッドモードでは、渡された文字列が間違っている場合、オブジェクトを正しく作成できません。複数の工場メソッドモードは、オブジェクトを個別に作成するための複数の工場メソッドを提供します。関係図:
上記のコードを変更して、次のようにsendFactoryクラスを変更するだけです。
public class sendfactory {public sender produceemail(){return new Mailsender(); } public sender producesms(){return new smssender(); }}テストクラスは次のとおりです。
public class factorytest {public static void main(string [] args){sendfactory factory = new sendfactory();送信者sender = factory.producemail(); sender.send(); }}出力:これはメールセンダーです!
33.静的ファクトリーメソッドモード上記の複数の工場メソッドモードのメソッドを静的に設定します。インスタンスを作成する必要はありません。直接呼び出すだけです。
public class sendfactory {public static sender produceemail(){return new Mailsender(); } public static sender producesms(){return new smssender(); }} Public Class FactoryTest {public static void main(string [] args){sender sender = sendfactory.producemail(); sender.send(); }}出力:これはメールセンダーです!
全体として、工場モデルは適切です。多数の製品を作成し、共通のインターフェイスを持つ必要がある場合、工場メソッドモデルを使用して作成できます。上記の3つのモードのうち、最初のモードは、渡された文字列が正しくない場合、オブジェクトを正しく作成できず、3番目のモードは2番目のモードと比較して工場クラスをインスタンス化する必要はありません。したがって、ほとんどの場合、3番目のもの - 静的工場メソッドモードを選択します。
2。抽象的な工場パターン
ファクトリーメソッドモデルには問題があります。つまり、クラスの作成は工場クラスに依存します。つまり、プログラムを拡張したい場合は、閉鎖原則に違反する工場クラスを変更する必要があります。したがって、設計の観点からは、特定の問題があります。それを解決する方法は?これは、抽象的な工場パターンを使用して、複数の工場クラスを作成します。このようにして、新しい機能が必要になると、以前のコードを変更せずに、新しい工場クラスを直接追加できます。抽象的な工場は理解しやすいため、最初に図を調べてから、コードに従います。これは理解しやすいです。
例を参照してください:
public interface sender {public void send(); }2つの実装クラス:
パブリッククラスのメールセンダーは送信者{@Override public void send(){system.out.println( "これはmailsender!"); }} public class smssenderは送信者{@override public void send(){system.out.println( "これはsms sender!"); }}2つの工場カテゴリ:
Public Class SendMailFactory Implements Provider {@Override Public Sender Produce(){return new MailSender(); }} Public Class sendsmsFactory Implements Provider {@Override public sender produce(){return new smssender(); }}インターフェイスを提供します:
パブリックインターフェイスプロバイダー{public sender produce(); }テストクラス:
public class test {public static void main(string [] args){provider provider = new sendmailfactory();送信者sender = provider.produce(); sender.send(); }}実際、このモデルの利点は、今すぐ関数を追加したい場合は、タイムリーな情報を送信する場合、実装クラスを作成し、送信者インターフェイスを実装し、同時に工場クラスを作成し、プロバイダーインターフェイスを実装し、既製コードを変更する必要がないことです。これを行うと、よりスケーラブルになります!
3。シングルトンモード
シングルトンは、一般的に使用されるデザインパターンです。 Javaアプリケーションでは、SingletonオブジェクトがJVMにオブジェクトのインスタンスが1つしかないことを保証できます。このモデルにはいくつかの利点があります。
1.一部のクラスはより頻繁に作成され、一部の大きなオブジェクトの場合、これは頭上の巨大なシステムです。
2。新しい演算子が排除され、システムメモリの頻度が低下し、GCの圧力が低下します。
3.取引所のコアトレーディングエンジンなどの一部のカテゴリは、取引プロセスを制御します。複数のカテゴリを作成できる場合、システムは完全に台無しになります。 (たとえば、複数の司令官が同時に陸軍司令官に表示される場合、間違いなくカオスになります)したがって、シングルトンモデルを使用することによってのみ、コアトランザクションサーバーがプロセス全体を独立して制御できるようにすることができます。
まず、シンプルなシングルトンクラスを書きましょう。
パブリッククラスのSingleton { /*参照を防ぐために、プライベートな静的インスタンスを保持します。ここでの値はnullであり、怠zyなロードを達成する目的で*/ private static singleton instance = null; /*インスタンス化を防ぐためのプライベートコンストラクター*/ private singleton(){}/*インスタンスを作成する静的エンジニアリング方法*/ public static singleton getInstance(){if(instance == null){instance = new Singleton(); } returnインスタンス; } /*オブジェクトがシリアル化に使用されている場合、シリアル化の前後にオブジェクトが一貫性を保つことを保証できます* / publicオブジェクトreadResolve(){return instance; }}このクラスは基本的な要件を満たすことができますが、マルチスレッド環境でワイヤレスワイヤレスセキュリティ保護を備えたこのクラスを配置すると、間違いなく問題が発生します。それを解決する方法は?最初に、次のように、getinstanceメソッドに同期されたキーワードを追加することを考えます。
public static同期されたSingleton getInstance(){if(instance == null){instance = new Singleton(); } returnインスタンス; }ただし、同期されたキーワードはこのオブジェクトをロックします。この使用量は、getInstance()を呼び出すたびにオブジェクトをロックする必要があるため、パフォーマンスが減少します。実際、オブジェクトが初めて作成された場合にのみ、ロックする必要はないため、この場所を改善する必要があります。次のように変更しましょう。
public static singleton getInstance(){if(instance == null){synchronized(instance){if(instance == null){instance = new Singleton(); }}} return instance; }前述の問題を解決し、内部に同期されたキーワードを追加します。つまり、呼び出しの際にロックする必要はありません。インスタンスがnullで、オブジェクトが作成された場合にのみロックが必要であり、パフォーマンスが一定の改善があります。ただし、この場合、問題がある可能性があります。次の状況を見てください。Java命令でオブジェクトと割り当て操作の作成は、個別に実行されます。つまり、Instance = new Singleton()。ステートメントは2つのステップで実行されます。ただし、JVMはこれら2つの操作の順序を保証するものではありません。つまり、JVMが新しいSingletonインスタンスにスペースを割り当て、インスタンスメンバーに直接割り当ててからSingletonインスタンスを初期化する可能性があります。これによりエラーが発生する可能性があります。例としてAとBのスレッドを取りましょう。
a> aおよびbスレッドは、同時に判断を下します
b> aは最初に同期ブロックに入り、インスタンスはnullであるため、instance = new Singleton()を実行します。
c> JVM内の最適化メカニズムにより、JVMは最初にSingletonインスタンスに割り当てられた空白のメモリを描画し、インスタンスメンバーに割り当てます(JVMは現時点ではこのインスタンスの初期化を開始しないことに注意してください)。
d> bは同期ブロックに入ります。インスタンスは現時点ではヌルではないため、すぐに同期ブロックを残し、結果をメソッドと呼ばれるプログラムに返します。
E>この時点で、Thread BはSingletonインスタンスを使用する予定ですが、初期化されていないことがわかっているため、エラーが発生します。
したがって、プログラムにはまだ可能なエラーがあります。実際、プログラムの実行プロセスは非常に複雑です。この時点から、特にマルチスレッド環境での執筆プログラムでは、より困難で挑戦的であることがわかります。さらに、プログラムを最適化しました。
Private Static Class SingletonFactory {private static Singleton instance = new Singleton(); } public static singleton getInstance(){return singletonfactory.instance; }実際の状況は、シングルトンパターンが内部クラスを使用してシングルトンの実装を維持することです。 JVMの内部メカニズムは、クラスがロードされたときに、このクラスの読み込みプロセスがスレッドに相互に排他的であることを保証できます。このようにして、GetInstanceを初めて呼び出すと、JVMはインスタンスが1回だけ作成されていることを確認し、インスタンスに割り当てられたメモリが初期化されるようにするため、上記の問題について心配する必要はありません。同時に、この方法は、初めて呼び出された場合にのみ相互除外メカニズムを使用し、パフォーマンスが低いという問題を解決します。このようにして、一時的に完璧なシングルトンパターンを要約します。
パブリッククラスSingleton { /*インスタンス化を防ぐためのプライベートコンストラクターメソッド* / private Singleton(){} /*ここでは、Singletonを維持するために内部クラスを使用します* / private static class Singletonfactory {private static Singleton Instance = new Singleton(); } /*インスタンスを取得* / public static singleton getInstance(){return singletonfactory.instance; } /*オブジェクトがシリアル化に使用されている場合、シリアル化の前後にオブジェクトが一貫性を保つことを保証できます* / public object readResolve(){return getInstance(); }}実際、それが完璧であることは必ずしも真実ではありません。コンストラクターに例外がスローされている場合、インスタンスが作成されることはなく、エラーが発生します。したがって、完璧なものは何もありません。実際の状況に基づいたアプリケーションシナリオに最も適した実装方法のみを選択できます。一部の人々もこれを実装します。クラスを作成するときに同期するだけで、作成とgetInstance()を分離し、同期されたキーワードを作成に個別に追加する必要があるため、それも可能です。
Public Class Singletontest {private static singletontestインスタンス= null; private singletontest(){} private static synchronized void syncinit(){if(instance == null){instance = new Singletontest(); }} public static singletontest getInstance(){if(instance == null){syncinit(); } returnインスタンス; }}パフォーマンスを検討した場合、プログラム全体はインスタンスを1回作成するだけで、パフォーマンスは影響を与えません。
サプリメント:「シャドウインスタンス」メソッドは、シングルトンオブジェクトのプロパティを同期するために使用されます
Public Class Singletontest {private static singletontestインスタンス= null;プライベートベクトルプロパティ= null; public vector getProperties(){return Properties; } private singletontest(){} private static synchronized void syncinit(){if(instance == null){instance = new singletontest(); }} public static singletontest getInstance(){if(instance == null){syncinit(); } returnインスタンス; } public void updateProperties(){Singletontest Shadow = new Singletontest();プロパティ= Shadow.getProperties(); }}シングルトンのパターンを学ぶことで、私たちに言います:
1.シングルトンモデルを理解するのは簡単ですが、詳細に実装することは依然として困難です。
2。同期されたキーワードはオブジェクトをロックします。使用する場合は、適切な場所で使用する必要があります(ロックする必要があるオブジェクトとプロセス、およびオブジェクト全体とプロセス全体をロックする必要がある場合があることに注意してください)。
この時点で、シングルトンのパターンは基本的に話されています。最後に、著者は突然別の質問を考えました。これは、静的クラスの方法を使用してシングルトンパターンの効果を実現することです。これも実現可能です。ここの両者の違いは何ですか?
まず、静的クラスはインターフェイスを実装できません。 (クラスの観点からは問題ありませんが、それは静的を破壊します。インターフェイスに許可されている静的な変更方法がないため、実装されていても非静的です)
第二に、シングルトンは初期化される可能性があり、静的クラスは通常、初めてロードされたときに初期化されます。怠zyなロードの理由は、一部のクラスが比較的大きいため、怠zyなロードはパフォーマンスを改善するのに役立ちます。
繰り返しますが、Singletonクラスを継承し、その方法を上書きすることができます。ただし、静的クラスの内部方法は静的であり、上書きすることはできません。
最後のポイント、シングルトンのクラスはより柔軟です。結局のところ、それらは実装の観点から普通のJavaクラスです。シングルトンの基本的なニーズを満たしている限り、他の機能を希望どおりに実装できますが、静的クラスはできません。上記の要約から、基本的に2つの違いを確認できます。ただし、一方で、上記で最終的に実装したシングルトンパターンは、静的クラスで内部的に実装されているため、2つは非常に関連していますが、問題の考慮のレベルは異なります。 2つのアイデアを組み合わせることによってのみ、完璧なソリューションを作成できます。 HashMapが配列 +リンクリストを使用してそれを実装するのと同じように、実際、人生の多くのことはこのようなものです。さまざまな方法を使用して問題に対処するには、常に利点と短所があります。最も完璧な方法は、各方法の利点を組み合わせて問題を最もよく解決することです!
4。ビルダーモード
工場クラスモデルは単一のクラスを作成するパターンを提供し、ビルダーモデルは管理用のさまざまな製品を集中し、それを使用して複合オブジェクトを作成します。いわゆる複合オブジェクトは、異なる属性を持つ特定のクラスを指します。実際、ビルダーモデルは、以前の抽象工場モデルと最終テストを組み合わせることで取得されます。コードを見てみましょう:
また、以前のものと同様に、1つの送信者インターフェイスと2つの実装クラスのMailSenderとSMSSENDERも同様です。最後に、ビルダークラスは次のとおりです。
public class Builder {private list <sender> list = new ArrayList <Sender>(); public void producemailsender(int count){for(int i = 0; i <count; i ++){list.add(new Mailsender()); }} public void produceSmssender(int count){for(int i = 0; i <count; i ++){list.add(new smssender()); }}}テストクラス:
public class test {public static void main(string [] args){builder builder = new Builder(); Builder.ProduceMailsender(10); }}この観点から、ビルダーパターンは多くの機能をクラスに統合し、より複雑なものを作成できます。したがって、エンジニアリングモデルとの違いは、工場モデルが単一の製品の作成に焦点を合わせている一方、ビルダーモデルは適切なオブジェクトと複数の部品の作成に焦点を当てていることです。したがって、工場モデルまたはビルダーモデルを選択するかどうかは、実際の状況に依存します。
5。プロトタイプ
プロトタイプパターンは創造的なパターンですが、エンジニアリングパターンとは何の関係もありません。名前からわかるように、このパターンのアイデアは、オブジェクトをプロトタイプとしてコピーしてクローン化し、元のオブジェクトに似た新しいオブジェクトを作成することです。この要約は、オブジェクトをコピーすることで説明されます。 Javaでは、コピーオブジェクトがClone()を介して実装され、最初にプロトタイプクラスが作成されます。
パブリッククラスのプロトタイプは、cloneable {public object clone()を実装しますclonenotsupportedexception {prototype proto =(prototype)super.clone(); protoを返します。 }}とても簡単です。プロトタイプクラスは、クローン可能なインターフェイスを実装し、クローンメソッドを上書きするだけです。クローンメソッドは任意の名前に変更できます。クローン可能なインターフェイスは空のインターフェイスであるため、CloneaやCloneBなどの実装クラスのメソッド名を任意に定義できます。 super.clone()はオブジェクトclone()メソッドを呼び出し、オブジェクトクラスでは、clone()はネイティブです。具体的に実装する方法は? Javaのローカルメソッドの呼び出しを解釈することに関する別の記事では、それには行きません。ここでは、オブジェクトの浅いコピーとディープコピーを組み合わせます。まず、オブジェクトの深く浅いコピーの概念を理解する必要があります。
浅いコピー:オブジェクトをコピーした後、基本データ型の変数が再作成され、参照型は元のオブジェクトを指します。
ディープコピー:オブジェクトをコピーした後、基本的なデータ型と参照型の両方が再作成されます。簡単に言えば、深いコピーは完全にコピーされますが、浅いコピーは徹底的ではありません。
ここで、コピーの例を詳細に書きます。
パブリッククラスのプロトタイプは、クローン可能、シリアル化可能{プライベート静的最終long serialversionuid = 1l;プライベート文字列文字列; Private SerializableObject obj; /* shallow copy*/ public object clone()throws clonenotsupportedexception {prototype proto =(prototype)super.clone(); protoを返します。 } /*ディープコピー* / public Object deepclone()throws ioException、classNotFoundException { /*バイナリストリームを現在のオブジェクトに書き込みます* / bytearrayoutputStream bos = new bytearrayoutputStream(); ObjectOutputStream OOS = new ObjectOutputStream(BOS); oos.writeobject(this); /*バイナリストリームによって生成された新しいオブジェクトを読み取ります*/ bytearrayinputStream bis = new bytearrayinputStream(bos.tobytearray()); ObjectInputStream ois = new ObjectInputStream(bis); ois.readObject()を返します。 } public string getString(){return string; } public void setString(string string){this.string = string; } public serializableobject getobj(){return obj; } public void setobj(serializableobject obj){this.obj = obj; }} class serializableobjectはserializable {private static final long serialversionuid = 1l; }深いコピーを実現するには、現在のオブジェクトのバイナリ入力をストリームの形式で読み取り、バイナリデータに対応するオブジェクトを書き出す必要があります。
引き続き設計モードについて説明します。前の記事では、5つの作成モードについて話し終えました。この章の冒頭で、アダプターモード、装飾モード、プロキシモード、外観モード、ブリッジモード、コンビネーションモード、楽しみモードの7つの構造モードについて説明します。オブジェクトのアダプターモードは、さまざまなモードの原点です。次の図を見てみましょう。
アダプターパターンは、インターフェイスの不一致によりクラスの互換性の問題を排除することを目的として、クライアントが期待する別のインターフェイス表現にクラスのインターフェイスを変換します。主に、クラスのアダプターモード、オブジェクトのアダプターモード、およびインターフェイスのアダプターモードの3つのカテゴリに分割されます。まず、クラスのアダプターモードを見て、最初にクラス図を見てみましょう。
核となるアイデアは、ターゲットインターフェイスがターゲットにかかっている場合に適応してターゲットに対応できるメソッドを備えたソースクラスがあります。アダプタークラスを通じて、ソース関数はターゲットに拡張され、コードを読み取ります。
public class source {public void method1(){system.out.println( "これは元の方法です!"); }} Public Interface Targetable { /*元のクラスのメソッドと同じ* / public void method1(); /*新しいクラスのメソッド*/ public void method2(); }パブリッククラスアダプター拡張ソース実装ターゲット可能な{@Override public void method2(){system.out.println( "これはターゲット可能な方法です!"); }}アダプタークラスはソースクラスを継承し、ターゲット可能なインターフェイスを実装します。以下はテストクラスです。
public class adaptertest {public static void main(string [] args){ターゲット可能なターゲット= new Adapter();ターゲット.method1(); Target.Method2(); }}出力:
これは元の方法です!
これがターゲット可能な方法です!
このように、ターゲット可能なインターフェイスの実装クラスには、ソースクラスの関数があります。
オブジェクトのアダプターモード
基本的なアイデアは、クラスのアダプターモードと同じです。アダプタークラスが変更されたというだけです。今回は、ソースクラスは継承されませんが、ソースクラスは互換性の問題を解決するために保持されます。写真を見てください:
アダプタークラスのソースコードを変更するだけです。
パブリッククラスラッパーはターゲット可能な{プライベートソースソース;パブリックラッパー(ソースソース){super(); this.source = source; } @Override public void method2(){system.out.println( "これはターゲット可能な方法です!"); } @Override public void method1(){source.method1(); }}テストクラス:
public class adaptertest {public static void main(string [] args){source source = new source();ターゲット可能なターゲット=新しいラッパー(ソース);ターゲット.method1(); Target.Method2(); }}出力は最初のものと同じですが、適応方法は異なります。
3番目のアダプターモードは、インターフェイスのアダプターモードです。インターフェイスのアダプターは次のとおりです。これは、私たちが記述するインターフェイスに複数の抽象的なメソッドがある場合があります。インターフェイスの実装クラスを作成するときは、インターフェイスのすべてのメソッドを実装する必要があります。これは明らかに無駄です。なぜなら、すべての方法が必要であるわけではなく、時には一部のみが必要であるからです。この問題を解決するために、インターフェイスのアダプターモードを導入しました。抽象クラスの助けを借りて、抽象クラスはインターフェイスを実装し、すべての方法を実装します。元のインターフェイスには対処せず、抽象クラスにのみ連絡します。そこで、クラスを書き、抽象クラスを継承し、必要な方法を書き直します。クラス図をご覧ください。
これは理解しやすいです。実際の開発では、このインターフェイスで定義されているメソッドが多すぎることが多いため、いくつかの実装クラスでそれらを必要としない場合があります。コードを見てください:
public Interface sourceable {public void method1(); public void method2(); }抽象クラスwrapper2:
public abstract class wrapper2はsourceable {public void method1(){} public void method2(){}} public class sourcesub1 extends wrapper2 {public void method1(){system.out.println( "最初のsub1!"); }} public class sourcesub2 extends wrapper2 {public void method2(){system.out.println( "Sourcable Interfaceの2番目のsub2!"); }} public class wrappertest {public static void main(string [] args){sourceable source1 = new sourcesub1(); sourceable source2 = new sourcesub2(); source1.method1(); source1.method2(); source2.method1(); source2.method2(); }}テスト出力:
Sourceable Interfaceの最初のsub1!
Sourceable Interfaceの2番目のSub2!
それは私たちの結果を達成しました!
それについて話した後、3つのアダプターモードのアプリケーションシナリオを要約します。
クラスアダプターモード:1つのクラスを別の新しいインターフェイスを満たすクラスに変換する場合、クラスアダプターモードを使用して新しいクラスを作成し、元のクラスを継承し、新しいインターフェイスを実装できます。
オブジェクトアダプターモード:オブジェクトを別の新しいインターフェイスを満たすオブジェクトに変換する場合、ラッパークラスを作成し、元のクラスのインスタンスを保持し、ラッパークラスメソッドでは、インスタンスメソッドを呼び出すだけです。
インターフェイスのアダプターモード:すべてのメソッドをインターフェイスに実装したくない場合は、すべてのメソッドを実装する抽象クラスラッパーを作成できます。他のクラスを書くときは、抽象クラスを継承するだけです。
7。デコレーター
名前が示すように、装飾パターンはオブジェクトにいくつかの新しい関数を追加することであり、それは動的であり、装飾的なオブジェクトと装飾的なオブジェクトが同じインターフェイスを実装する必要があります。装飾的なオブジェクトは、装飾的なオブジェクトのインスタンスを保持します。関係図は次のとおりです。
ソースクラスは装飾クラスであり、デコレータークラスは、ソースクラスにいくつかの機能を動的に追加できる装飾クラスです。コードは次のとおりです。
public Interface sourceable {public void method(); } public class Sourceはsourceable {@override public void method(){system.out.println( "The Original Method!"); }}パブリッククラスのデコレーターは、Sourceable {private Sourcable Source;パブリックデコレーター(Sourceable Source){super(); this.source = source; } @Override public void method(){system.out.println( "dercorator!"); source.method(); system.out.println( "デコレーター後!"); }}テストクラス:
public class decoratortest {public static void main(string [] args){sourcable source = new source(); Sourceable obj = new Decorator(source); obj.method(); }}出力:
デコレーターの前!
元の方法!
デコレーターの後!
デコレーターモードアプリケーションシナリオ:
1.クラスの関数を拡張する必要があります。
2。機能をオブジェクトに動的に追加し、動的に元に戻すこともできます。 (継承はこれを行うことはできません。継承された関数は静的であり、動的に追加および削除することはできません。)
短所:類似のオブジェクトが多すぎると、トラブルシューティングが簡単ではありません!
8。プロキシモード(プロキシ)
実際、各モデル名はモデルの関数を示します。プロキシモデルは、追加のエージェントクラスを追加して、元のオブジェクトで操作を実行することです。たとえば、家を借りるときは、エージェントを見つけるために戻ります。なぜ?あなたはその地域の家に関する情報を包括的に理解していないので、私はあなたがそれを助けるためにもっとよく知っている人を見つけたいと思っています。これがここのエージェントの意味です。たとえば、私たちが訴えるとき、弁護士を雇う必要がある場合があります。なぜなら、弁護士は法律の専門知識を持ち、私たちに代わって活動し、アイデアを表現することができるからです。最初に関係図を見てみましょう:
上記の説明によると、プロキシモードは理解しやすいです。コードを見てみましょう:
public Interface sourceable {public void method(); } public class Sourceはsourceable {@override public void method(){system.out.println( "The Original Method!"); }} public class ProxyはSourceable {private source Source; public proxy(){super(); this.source = new source(); } @Override public void method(){before(); source.method(); atfer(); } private void atfer() { System.out.println("after proxy!"); } private void before() { System.out.println("before proxy!"); }}测试类:
public class ProxyTest { public static void main(String[] args) { Sourceable source = new Proxy(); source.method(); }}出力:
before proxy!
the original method!
after proxy!
代理模式的应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!
9、外观模式(Facade)
外观模式是为了解决类与类之家的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口,看下类图:(我们以一个计算机的启动过程为例)
我们先看下实现类:
public class CPU { public void startup(){ System.out.println("cpu startup!"); } public void shutdown(){ System.out.println("cpu shutdown!"); } } public class Memory { public void startup(){ System.out.println("memory startup!"); } public void shutdown(){ System.out.println("memory shutdown!"); } } public class Disk { public void startup(){ System.out.println("disk startup!"); } public void shutdown(){ System.out.println("disk shutdown!"); } } public class Computer { private CPU cpu; private Memory memory; private Disk disk; public Computer(){ cpu = new CPU(); memory = new Memory(); disk = new Disk(); } public void startup(){ System.out.println("start the computer!"); cpu.startup(); memory.startup(); disk.startup(); System.out.println("start computer finished!"); } public void shutdown(){ System.out.println("begin to close the computer!"); cpu.shutdown(); memory.shutdown(); disk.shutdown(); System.out.println("computer closed!"); }}User类如下:
public class User { public static void main(String[] args) { Computer computer = new Computer(); computer.startup(); computer.shutdown(); }}出力:
start the computer!
cpu startup!
memory startup!
disk startup!
start computer finished!
begin to close the computer!
cpu shutdown!
memory shutdown!
disk shutdown!
computer closed!
如果我们没有Computer类,那么,CPU、Memory、Disk他们之间将会相互持有实例,产生关系,这样会造成严重的依赖,修改一个类,可能会带来其他类的修改,这不是我们想要看到的,有了Computer类,他们之间的关系被放在了Computer类里,这样就起到了解耦的作用,这,就是外观模式!
10、桥接模式(Bridge)
桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。我们来看看关系图:
实现代码:
先定义接口:
public interface Sourceable { public void method(); }分别定义两个实现类:
public class SourceSub1 implements Sourceable { @Override public void method() { System.out.println("this is the first sub!"); } } public class SourceSub2 implements Sourceable { @Override public void method() { System.out.println("this is the second sub!"); } }定义一个桥,持有Sourceable的一个实例:
public abstract class Bridge { private Sourceable source; public void method(){ source.method(); } public Sourceable getSource() { return source; } public void setSource(Sourceable source) { this.source = source; } } public class MyBridge extends Bridge { public void method(){ getSource().method(); } }测试类:
public class BridgeTest { public static void main(String[] args) { Bridge bridge = new MyBridge(); /*调用第一个对象*/ Sourceable source1 = new SourceSub1(); bridge.setSource(source1); bridge.method(); /*调用第二个对象*/ Sourceable source2 = new SourceSub2(); bridge.setSource(source2); bridge.method(); } }output:
this is the first sub!
this is the second sub!
这样,就通过对Bridge类的调用,实现了对接口Sourceable的实现类SourceSub1和SourceSub2的调用。接下来我再画个图,大家就应该明白了,因为这个图是我们JDBC连接的原理,有数据库学习基础的,一结合就都懂了。
11、组合模式(Composite)
组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便,看看关系图:
直接来看代码:
public class TreeNode { private String name; private TreeNode parent; private Vector<TreeNode> children = new Vector<TreeNode>(); public TreeNode(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public TreeNode getParent() { return parent; } public void setParent(TreeNode parent) { this.parent = parent; } //添加孩子节点public void add(TreeNode node){ children.add(node); } //删除孩子节点public void remove(TreeNode node){ children.remove(node); } //取得孩子节点public Enumeration<TreeNode> getChildren(){ return children.elements(); } } public class Tree { TreeNode root = null; public Tree(String name) { root = new TreeNode(name); } public static void main(String[] args) { Tree tree = new Tree("A"); TreeNode nodeB = new TreeNode("B"); TreeNode nodeC = new TreeNode("C"); nodeB.add(nodeC); tree.root.add(nodeB); System.out.println("build the tree finished!"); } }使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。
12、享元模式(Flyweight)
享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。
FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。
看个例子:
看下数据库连接池的代码:
public class ConnectionPool { private Vector<Connection> pool; /*公有属性*/ private String url = "jdbc:mysql://localhost:3306/test"; private String username = "root"; private String password = "root"; private String driverClassName = "com.mysql.jdbc.Driver"; private int poolSize = 100; private static ConnectionPool instance = null; Connection conn = null; /*构造方法,做一些初始化工作*/ private ConnectionPool() { pool = new Vector<Connection>(poolSize); for (int i = 0; i < poolSize; i++) { try { Class.forName(driverClassName); conn = DriverManager.getConnection(url, username, password); pool.add(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } /* 返回连接到连接池*/ public synchronized void release() { pool.add(conn); } /* 返回连接池中的一个数据库连接*/ public synchronized Connection getConnection() { if (pool.size() > 0) { Connection conn = pool.get(0); pool.remove(conn); return conn; } else { return null; } } }通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!本章讲解了7种结构型模式,因为篇幅的问题,剩下的11种行为型模式,
本章是关于设计模式的最后一讲,会讲到第三种设计模式――行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。这段时间一直在写关于设计模式的东西,终于写到一半了,写博文是个很费时间的东西,因为我得为读者负责,不论是图还是代码还是表述,都希望能尽量写清楚,以便读者理解,我想不论是我还是读者,都希望看到高质量的博文出来,从我本人出发,我会一直坚持下去,不断更新,源源动力来自于读者朋友们的不断支持,我会尽自己的努力,写好每一篇文章!希望大家能不断给出意见和建议,共同打造完美的博文!
先来张图,看看这11中模式的关系:
第一类:通过父类与子类的关系进行实现。第二类:两个类之间。第三类:类的状态。第四类:通过中间类
13、策略模式(strategy)
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数,关系图如下:
图中ICalculator提供同意的方法,
AbstractCalculator是辅助类,提供辅助方法,接下来,依次实现下每个类:
首先统一接口:
public interface ICalculator { public int calculate(String exp); }辅助类:
public abstract class AbstractCalculator { public int[] split(String exp,String opt){ String array[] = exp.split(opt); int arrayInt[] = new int[2]; arrayInt[0] = Integer.parseInt(array[0]); arrayInt[1] = Integer.parseInt(array[1]); return arrayInt; } }三个实现类:
public class Plus extends AbstractCalculator implements ICalculator { @Override public int calculate(String exp) { int arrayInt[] = split(exp,"//+"); return arrayInt[0]+arrayInt[1]; } } public class Minus extends AbstractCalculator implements ICalculator { @Override public int calculate(String exp) { int arrayInt[] = split(exp,"-"); return arrayInt[0]-arrayInt[1]; } } public class Multiply extends AbstractCalculator implements ICalculator { @Override public int calculate(String exp) { int arrayInt[] = split(exp,"//*"); return arrayInt[0]*arrayInt[1]; } }简单的测试类:
public class StrategyTest { public static void main(String[] args) { String exp = "2+8"; ICalculator cal = new Plus(); int result = cal.calculate(exp); System.out.println(result); } }输出:10
策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
14、模板方法模式(Template Method)
解释一下模板方法模式,就是指:一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用,先看个关系图:
就是在AbstractCalculator类中定义一个主方法calculate,calculate()调用spilt()等,Plus和Minus分别继承AbstractCalculator类,通过对AbstractCalculator的调用实现对子类的调用,看下面的例子:
public abstract class AbstractCalculator { /*主方法,实现对本类其它方法的调用*/ public final int calculate(String exp,String opt){ int array[] = split(exp,opt); return calculate(array[0],array[1]); } /*被子类重写的方法*/ abstract public int calculate(int num1,int num2); public int[] split(String exp,String opt){ String array[] = exp.split(opt); int arrayInt[] = new int[2]; arrayInt[0] = Integer.parseInt(array[0]); arrayInt[1] = Integer.parseInt(array[1]); return arrayInt; } } public class Plus extends AbstractCalculator { @Override public int calculate(int num1,int num2) { return num1 + num2; } }测试类:
public class StrategyTest { public static void main(String[] args) { String exp = "8+8"; AbstractCalculator cal = new Plus(); int result = cal.calculate(exp, "//+"); System.out.println(result); } }我跟踪下这个小程序的执行过程:首先将exp和"//+"做参数,调用AbstractCalculator类里的calculate(String,String)方法,在calculate(String,String)里调用同类的split(),之后再调用calculate(int ,int)方法,从这个方法进入到子类中,执行完return num1 + num2后,将值返回到AbstractCalculator类,赋给result,打印出来。正好验证了我们开头的思路。
15、观察者模式(Observer)
包括这个模式在内的接下来的四个模式,都是类和类之间的关系,不涉及到继承,学的时候应该记得归纳,记得本文最开始的那个图。观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。先来看看关系图:
我解释下这些类的作用:MySubject类就是我们的主对象,Observer1和Observer2是依赖于MySubject的对象,当MySubject变化时,Observer1和Observer2必然变化。AbstractSubject类中定义着需要监控的对象列表,可以对其进行修改:增加或删除被监控对象,且当MySubject变化时,负责通知在列表内存在的对象。我们看实现代码:
一个Observer接口:
public interface Observer { public void update(); }两个实现类:
public class Observer1 implements Observer { @Override public void update() { System.out.println("observer1 has received!"); } } public class Observer2 implements Observer { @Override public void update() { System.out.println("observer2 has received!"); } }Subject接口及实现类:
public interface Subject { /*增加观察者*/ public void add(Observer observer); /*删除观察者*/ public void del(Observer observer); /*通知所有的观察者*/ public void notifyObservers(); /*自身的操作*/ public void operation(); } public abstract class AbstractSubject implements Subject { private Vector<Observer> vector = new Vector<Observer>(); @Override public void add(Observer observer) { vector.add(observer); } @Override public void del(Observer observer) { vector.remove(observer); } @Override public void notifyObservers() { Enumeration<Observer> enumo = vector.elements(); while(enumo.hasMoreElements()){ enumo.nextElement().update(); } } } public class MySubject extends AbstractSubject { @Override public void operation() { System.out.println("update self!"); notifyObservers(); } }测试类:
public class ObserverTest { public static void main(String[] args) { Subject sub = new MySubject(); sub.add(new Observer1()); sub.add(new Observer2()); sub.operation(); } }出力:
update self!
observer1 has received!
observer2 has received!
这些东西,其实不难,只是有些抽象,不太容易整体理解,建议读者:根据关系图,新建项目,自己写代码(或者参考我的代码),按照总体思路走一遍,这样才能体会它的思想,理解起来容易!
16、迭代子模式(Iterator)
顾名思义,迭代器模式就是顺序访问聚集中的对象,一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松。这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。我们看下关系图:
这个思路和我们常用的一模一样,MyCollection中定义了集合的一些操作,MyIterator中定义了一系列迭代操作,且持有Collection实例,我们来看看实现代码:
两个接口:
public interface Collection { public Iterator iterator(); /*取得集合元素*/ public Object get(int i); /*取得集合大小*/ public int size(); } public interface Iterator { //前移public Object previous(); //后移public Object next(); public boolean hasNext(); //取得第一个元素public Object first(); }两个实现:
public class MyCollection implements Collection { public String string[] = {"A","B","C","D","E"}; @Override public Iterator iterator() { return new MyIterator(this); } @Override public Object get(int i) { return string[i]; } @Override public int size() { return string.length; } } public class MyIterator implements Iterator { private Collection collection; private int pos = -1; public MyIterator(Collection collection){ this.collection = collection; } @Override public Object previous() { if(pos > 0){ pos--; } return collection.get(pos); } @Override public Object next() { if(pos<collection.size()-1){ pos++; } return collection.get(pos); } @Override public boolean hasNext() { if(pos<collection.size()-1){ return true; }else{ return false; } } @Override public Object first() { pos = 0; return collection.get(pos); } }测试类:
public class Test { public static void main(String[] args) { Collection collection = new MyCollection(); Iterator it = collection.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } }输出:ABCDE
此处我们貌似模拟了一个集合类的过程,感觉是不是很爽?其实JDK中各个类也都是这些基本的东西,加一些设计模式,再加一些优化放到一起的,只要我们把这些东西学会了,掌握好了,我们也可以写出自己的集合类,甚至框架!
17、责任链模式(Chain of Responsibility)
接下来我们将要谈谈责任链模式,有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。先看看关系图:
Abstracthandler类提供了get和set方法,方便MyHandle类设置和修改引用对象,MyHandle类是核心,实例化后生成一系列相互持有的对象,构成一条链。
public interface Handler { public void operator(); } public abstract class AbstractHandler { private Handler handler; public Handler getHandler() { return handler; } public void setHandler(Handler handler) { this.handler = handler; } } public class MyHandler extends AbstractHandler implements Handler { private String name; public MyHandler(String name) { this.name = name; } @Override public void operator() { System.out.println(name+"deal!"); if(getHandler()!=null){ getHandler().operator(); } } } public class Test { public static void main(String[] args) { MyHandler h1 = new MyHandler("h1"); MyHandler h2 = new MyHandler("h2"); MyHandler h3 = new MyHandler("h3"); h1.setHandler(h2); h2.setHandler(h3); h1.operator(); } }出力:
h1deal!
h2deal!
h3deal!
此处强调一点就是,链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。
18、命令模式(Command)
命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。我们看看关系图:
Invoker是调用者(司令员),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象,看实现代码:
public interface Command { public void exe(); } public class MyCommand implements Command { private Receiver receiver; public MyCommand(Receiver receiver) { this.receiver = receiver; } @Override public void exe() { receiver.action(); } } public class Receiver { public void action(){ System.out.println("command received!"); } } public class Invoker { private Command command; public Invoker(Command command) { this.command = command; } public void action(){ command.exe(); } } public class Test { public static void main(String[] args) { Receiver receiver = new Receiver(); Command cmd = new MyCommand(receiver); Invoker invoker = new Invoker(cmd); invoker.action(); } }输出:command received!
这个很哈理解,命令模式的目的就是达到命令的发出者和执行者之间解耦,实现请求和执行分开,熟悉Struts的同学应该知道,Struts其实就是一种将请求和呈现分离的技术,其中必然涉及命令模式的思想!
其实每个设计模式都是很重要的一种思想,看上去很熟,其实是因为我们在学到的东西中都有涉及,尽管有时我们并不知道,其实在Java本身的设计之中处处都有体现,像AWT、JDBC、集合类、IO管道或者是Web框架,里面设计模式无处不在。因为我们篇幅有限,很难讲每一个设计模式都讲的很详细,不过我会尽我所能,尽量在有限的空间和篇幅内,把意思写清楚了,更好让大家明白。本章不出意外的话,应该是设计模式最后一讲了,首先还是上一下上篇开头的那个图:
本章讲讲第三类和第四类。
19、备忘录模式(Memento)
主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象,个人觉得叫备份模式更形象些,通俗的讲下:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C呢,就是一个用来存储备忘录的,且只能存储,不能修改等操作。做个图来分析一下:
Original类是原始类,里面有需要保存的属性value及创建一个备忘录类,用来保存value值。Memento类是备忘录类,Storage类是存储备忘录的类,持有Memento类的实例,该模式很好理解。直接看源码:
public class Original { private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } public Original(String value) { this.value = value; } public Memento createMemento(){ return new Memento(value); } public void restoreMemento(Memento memento){ this.value = memento.getValue(); } } public class Memento { private String value; public Memento(String value) { this.value = value; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } public class Storage { private Memento memento; public Storage(Memento memento) { this.memento = memento; } public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }测试类:
public class Test { public static void main(String[] args) { // 创建原始类Original origi = new Original("egg"); // 创建备忘录Storage storage = new Storage(origi.createMemento()); // 修改原始类的状态System.out.println("初始化状态为:" + origi.getValue()); origi.setValue("niu"); System.out.println("修改后的状态为:" + origi.getValue()); // 回复原始类的状态origi.restoreMemento(storage.getMemento()); System.out.println("恢复后的状态为:" + origi.getValue()); } }出力:
初始化状态为:egg
修改后的状态为:niu
恢复后的状态为:egg
简单描述下:新建原始类时,value被初始化为egg,后经过修改,将value的值置为niu,最后倒数第二行进行恢复状态,结果成功恢复了。其实我觉得这个模式叫“备份-恢复”模式最形象。
20、状态模式(State)
核心思想就是:当对象的状态改变时,同时改变其行为,很好理解!就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:1、可以通过改变状态来获得不同的行为。2、你的好友能同时看到你的变化。看图:
State类是个状态类,Context类可以实现切换,我们来看看代码:
package com.xtfggef.dp.state; /** * 状态类的核心类* 2012-12-1 * @author erqing * */ public class State { private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } public void method1(){ System.out.println("execute the first opt!"); } public void method2(){ System.out.println("execute the second opt!"); } } package com.xtfggef.dp.state; /** * 状态模式的切换类2012-12-1 * @author erqing * */ public class Context { private State state; public Context(State state) { this.state = state; } public State getState() { return state; } public void setState(State state) { this.state = state; } public void method() { if (state.getValue().equals("state1")) { state.method1(); } else if (state.getValue().equals("state2")) { state.method2(); } } }测试类:
public class Test { public static void main(String[] args) { State state = new State(); Context context = new Context(state); //设置第一种状态state.setValue("state1"); context.method(); //设置第二种状态state.setValue("state2"); context.method(); } }出力:
execute the first opt!
execute the second opt!
根据这个特性,状态模式在日常开发中用的挺多的,尤其是做网站的时候,我们有时希望根据对象的某一属性,区别开他们的一些功能,比如说简单的权限控制等。
21、访问者模式(Visitor)
访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。访问者模式适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。其缺点就是增加新的数据结构很困难。―― From 百科
简单来说,访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。简单关系图:
来看看原码:一个Visitor类,存放要访问的对象,
public interface Visitor { public void visit(Subject sub); } public class MyVisitor implements Visitor { @Override public void visit(Subject sub) { System.out.println("visit the subject:"+sub.getSubject()); } }Subject类,accept方法,接受将要访问它的对象,getSubject()获取将要被访问的属性,
public interface Subject { public void accept(Visitor visitor); public String getSubject(); } public class MySubject implements Subject { @Override public void accept(Visitor visitor) { visitor.visit(this); } @Override public String getSubject() { return "love"; } }テスト:
public class Test { public static void main(String[] args) { Visitor visitor = new MyVisitor(); Subject sub = new MySubject(); sub.accept(visitor); } }输出:visit the subject:love
该模式适用场景:如果我们想为一个现有的类增加新功能,不得不考虑几个事情:1、新功能会不会与现有功能出现兼容性问题?2、以后会不会再需要添加?3、如果类不允许修改代码怎么办?面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦,
22、中介者模式(Mediator)
中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行,这有点像spring容器的作用。先看看图:
User类统一接口,User1和User2分别是不同的对象,二者之间有关联,如果不采用中介者模式,则需要二者相互持有引用,这样二者的耦合度很高,为了解耦,引入了Mediator类,提供统一接口,MyMediator为其实现类,里面持有User1和User2的实例,用来实现对User1和User2的控制。这样User1和User2两个对象相互独立,他们只需要保持好和Mediator之间的关系就行,剩下的全由MyMediator类来维护!基本实现:
public interface Mediator { public void createMediator(); public void workAll(); } public class MyMediator implements Mediator { private User user1; private User user2; public User getUser1() { return user1; } public User getUser2() { return user2; } @Override public void createMediator() { user1 = new User1(this); user2 = new User2(this); } @Override public void workAll() { user1.work(); user2.work(); } } public abstract class User { private Mediator mediator; public Mediator getMediator(){ return mediator; } public User(Mediator mediator) { this.mediator = mediator; } public abstract void work(); } public class User1 extends User { public User1(Mediator mediator){ super(mediator); } @Override public void work() { System.out.println("user1 exe!"); } } public class User2 extends User { public User2(Mediator mediator){ super(mediator); } @Override public void work() { System.out.println("user2 exe!"); } }测试类:
public class Test { public static void main(String[] args) { Mediator mediator = new MyMediator(); mediator.createMediator(); mediator.workAll(); } }出力:
user1 exe!
user2 exe!
23、解释器模式(Interpreter)
解释器模式是我们暂时的最后一讲,一般主要应用在OOP开发中的编译器的开发中,所以适用面比较窄。
Context类是一个上下文环境类,Plus和Minus分别是用来计算的实现,代码如下:
public interface Expression { public int interpret(Context context); } public class Plus implements Expression { @Override public int interpret(Context context) { return context.getNum1()+context.getNum2(); } } public class Minus implements Expression { @Override public int interpret(Context context) { return context.getNum1()-context.getNum2(); } } public class Context { private int num1; private int num2; public Context(int num1, int num2) { this.num1 = num1; this.num2 = num2; } public int getNum1() { return num1; } public void setNum1(int num1) { this.num1 = num1; } public int getNum2() { return num2; } public void setNum2(int num2) { this.num2 = num2; } } public class Test { public static void main(String[] args) { // 计算9+2-8的值int result = new Minus().interpret((new Context(new Plus() .interpret(new Context(9, 2)), 8))); System.out.println(result); } }最后输出正确的结果:3。
基本就这样,解释器模式用来做各种各样的解释器,如正则表达式等的解释器等等!
原文链接:http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。