一些設計模式闡述,偏JAVA實現

設計模式主要是基於以下的面向對象設計原則:

  • 對接口編程而不是對實現編程。
  • 優先使用對象組合而不是繼承。

23 種設計模式。這些模式可以分爲三大類:創建型模式(Creational Patterns)、結構型模式(Structural Patterns)、行爲型模式(Behavioral Patterns)。當然,我們還會討論另一類設計模式:J2EE 設計模式。

 

 

下面用一個圖片來整體描述一下設計模式之間的關係:

 

設計模式的六大原則

1、開閉原則(Open Close Principle

開閉原則的意思是:對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。簡言之,是爲了使程序的擴展性好,易於維護和升級。想要達到這樣的效果,我們需要使用接口和抽象類,後面的具體設計中我們會提到這點。

2、里氏代換原則(Liskov Substitution Principle

里氏代換原則是面向對象設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。LSP 是繼承複用的基石,只有當派生類可以替換掉基類,且軟件單位的功能不受到影響時,基類才能真正被複用,而派生類也能夠在基類的基礎上增加新的行爲。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。

3、依賴倒轉原則(Dependence Inversion Principle

這個原則是開閉原則的基礎,具體內容:針對接口編程,依賴於抽象而不依賴於具體。

4、接口隔離原則(Interface Segregation Principle

這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。它還有另外一個意思是:降低類之間的耦合度。由此可見,其實設計模式就是從大型軟件架構出發、便於升級和維護的軟件設計思想,它強調降低依賴,降低耦合。

5、迪米特法則,又稱最少知道原則(Demeter Principle

最少知道原則是指:一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模塊相對獨立。

6、合成複用原則(Composite Reuse Principle

合成複用原則是指:儘量使用合成/聚合的方式,而不是使用繼承。

工廠模式

工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。

在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,並且是通過使用一個共同的接口來指向新創建的對象。

介紹

意圖:定義一個創建對象的接口,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創建過程延遲到子類進行。

主要解決:主要解決接口選擇的問題。

何時使用:我們明確地計劃不同條件下創建不同實例時。

如何解決:讓其子類實現工廠接口,返回的也是一個抽象的產品。

關鍵代碼:創建過程在其子類執行。

應用實例: 1、您需要一輛汽車,可以直接從工廠裏面提貨,而不用去管這輛汽車是怎麼做出來的,以及這個汽車裏面的具體實現。 2Hibernate 換數據庫只需換方言和驅動就可以。

優點: 1、一個調用者想創建一個對象,只要知道其名稱就可以了。 2、擴展性高,如果想增加一個產品,只要擴展一個工廠類就可以。 3、屏蔽產品的具體實現,調用者只關心產品的接口。

缺點:每次增加一個產品時,都需要增加一個具體類和對象實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事。

使用場景: 1、日誌記錄器:記錄可能記錄到本地硬盤、系統事件、遠程服務器等,用戶可以選擇記錄日誌到什麼地方。 2、數據庫訪問,當用戶不知道最後系統採用哪一類數據庫,以及數據庫可能有變化時。 3、設計一個連接服務器的框架,需要三個協議,"POP3""IMAP""HTTP",可以把這三個作爲產品類,共同實現一個接口。

注意事項:作爲一種創建類模式,在任何需要生成複雜對象的地方,都可以使用工廠方法模式。有一點需要注意的地方就是複雜對象適合使用工廠模式,而簡單對象,特別是只需要通過 new 就可以完成創建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的複雜度。

實現

我們將創建一個 Shape 接口和實現 Shape 接口的實體類。下一步是定義工廠類 ShapeFactory

FactoryPatternDemo,我們的演示類使用 ShapeFactory 來獲取 Shape 對象。它將向 ShapeFactory 傳遞信息(CIRCLE / RECTANGLE / SQUARE),以便獲取它所需對象的類型。

步驟 1

創建一個接口:

Shape.java

public interface Shape { void draw(); }

步驟 2

創建實現接口的實體類。

Rectangle.java

public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } }

Square.java

public class Square implements Shape { @Override public void draw() { System.out.println("Inside Square::draw() method."); } }

Circle.java

public class Circle implements Shape { @Override public void draw() { System.out.println("Inside Circle::draw() method."); } }

步驟 3

創建一個工廠,生成基於給定信息的實體類的對象。

ShapeFactory.java

public class ShapeFactory { //使用 getShape 方法獲取形狀類型的對象 public Shape getShape(String shapeType){ if(shapeType == null){ return null; } if(shapeType.equalsIgnoreCase("CIRCLE")){ return new Circle(); } else if(shapeType.equalsIgnoreCase("RECTANGLE")){ return new Rectangle(); } else if(shapeType.equalsIgnoreCase("SQUARE")){ return new Square(); } return null; } }

步驟 4

使用該工廠,通過傳遞類型信息來獲取實體類的對象。

FactoryPatternDemo.java

public class FactoryPatternDemo { public static void main(String[] args) { ShapeFactory shapeFactory = new ShapeFactory(); //獲取 Circle 的對象,並調用它的 draw 方法 Shape shape1 = shapeFactory.getShape("CIRCLE"); //調用 Circle draw 方法 shape1.draw(); //獲取 Rectangle 的對象,並調用它的 draw 方法 Shape shape2 = shapeFactory.getShape("RECTANGLE"); //調用 Rectangle draw 方法 shape2.draw(); //獲取 Square 的對象,並調用它的 draw 方法 Shape shape3 = shapeFactory.getShape("SQUARE"); //調用 Square draw 方法 shape3.draw(); } }

步驟 5

執行程序,輸出結果:

Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.

抽象工廠模式

抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠創建其他工廠。該超級工廠又稱爲其他工廠的工廠。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。

在抽象工廠模式中,接口是負責創建一個相關對象的工廠,不需要顯式指定它們的類。每個生成的工廠都能按照工廠模式提供對象。

介紹

意圖:提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。

主要解決:主要解決接口選擇的問題。

何時使用:系統的產品有多於一個的產品族,而系統只消費其中某一族的產品。

如何解決:在一個產品族裏面,定義多個產品。

關鍵代碼:在一個工廠裏聚合多個同類產品。

應用實例:工作了,爲了參加一些聚會,肯定有兩套或多套衣服吧,比如說有商務裝(成套,一系列具體產品)、時尚裝(成套,一系列具體產品),甚至對於一個家庭來說,可能有商務女裝、商務男裝、時尚女裝、時尚男裝,這些也都是成套的,即一系列具體產品。假設一種情況(現實中是不存在的,要不然,沒法進入共產主義了,但有利於說明抽象工廠模式),在您的家中,某一個衣櫃(具體工廠)只能存放某一種這樣的衣服(成套,一系列具體產品),每次拿這種成套的衣服時也自然要從這個衣櫃中取出了。用 OOP 的思想去理解,所有的衣櫃(具體工廠)都是衣櫃類的(抽象工廠)某一個,而每一件成套的衣服又包括具體的上衣(某一具體產品),褲子(某一具體產品),這些具體的上衣其實也都是上衣(抽象產品),具體的褲子也都是褲子(另一個抽象產品)。

優點:當一個產品族中的多個對象被設計成一起工作時,它能保證客戶端始終只使用同一個產品族中的對象。

缺點:產品族擴展非常困難,要增加一個系列的某一產品,既要在抽象的 Creator 里加代碼,又要在具體的裏面加代碼。

使用場景: 1QQ 換皮膚,一整套一起換。 2、生成不同操作系統的程序。

注意事項:產品族難擴展,產品等級易擴展。

實現

我們將創建 Shape  Color 接口和實現這些接口的實體類。下一步是創建抽象工廠類 AbstractFactory。接着定義工廠類 ShapeFactory  ColorFactory,這兩個工廠類都是擴展了 AbstractFactory。然後創建一個工廠創造器/生成器類 FactoryProducer

AbstractFactoryPatternDemo,我們的演示類使用 FactoryProducer 來獲取 AbstractFactory 對象。它將向 AbstractFactory 傳遞形狀信息 ShapeCIRCLE / RECTANGLE / SQUARE),以便獲取它所需對象的類型。同時它還向 AbstractFactory 傳遞顏色信息 ColorRED / GREEN / BLUE),以便獲取它所需對象的類型。

步驟 1

爲形狀創建一個接口。

Shape.java

public interface Shape { void draw(); }

步驟 2

創建實現接口的實體類。

Rectangle.java

Rectangle.java

public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } }

Square.java

public class Square implements Shape { @Override public void draw() { System.out.println("Inside Square::draw() method."); } }

Circle.java

public class Circle implements Shape { @Override public void draw() { System.out.println("Inside Circle::draw() method."); } }

步驟 3

爲顏色創建一個接口。

Color.java

public interface Color { void fill(); }

步驟4

創建實現接口的實體類。

Red.java

public class Red implements Color { @Override public void fill() { System.out.println("Inside Red::fill() method."); } }

Green.java

public class Green implements Color { @Override public void fill() { System.out.println("Inside Green::fill() method."); } }

Blue.java

public class Blue implements Color { @Override public void fill() { System.out.println("Inside Blue::fill() method."); } }

步驟 5

Color Shape 對象創建抽象類來獲取工廠。

AbstractFactory.java

public abstract class AbstractFactory { public abstract Color getColor(String color); public abstract Shape getShape(String shape) ; }

步驟 6

創建擴展了 AbstractFactory 的工廠類,基於給定的信息生成實體類的對象。

ShapeFactory.java

public class ShapeFactory extends AbstractFactory { @Override public Shape getShape(String shapeType){ if(shapeType == null){ return null; } if(shapeType.equalsIgnoreCase("CIRCLE")){ return new Circle(); } else if(shapeType.equalsIgnoreCase("RECTANGLE")){ return new Rectangle(); } else if(shapeType.equalsIgnoreCase("SQUARE")){ return new Square(); } return null; } @Override public Color getColor(String color) { return null; } }

ColorFactory.java

public class ColorFactory extends AbstractFactory { @Override public Shape getShape(String shapeType){ return null; } @Override public Color getColor(String color) { if(color == null){ return null; } if(color.equalsIgnoreCase("RED")){ return new Red(); } else if(color.equalsIgnoreCase("GREEN")){ return new Green(); } else if(color.equalsIgnoreCase("BLUE")){ return new Blue(); } return null; } }

步驟 7

創建一個工廠創造器/生成器類,通過傳遞形狀或顏色信息來獲取工廠。

FactoryProducer.java

public class FactoryProducer { public static AbstractFactory getFactory(String choice){ if(choice.equalsIgnoreCase("SHAPE")){ return new ShapeFactory(); } else if(choice.equalsIgnoreCase("COLOR")){ return new ColorFactory(); } return null; } }

步驟 8

使用 FactoryProducer 來獲取 AbstractFactory,通過傳遞類型信息來獲取實體類的對象。

AbstractFactoryPatternDemo.java

public class AbstractFactoryPatternDemo { public static void main(String[] args) { //獲取形狀工廠 AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE"); //獲取形狀爲 Circle 的對象 Shape shape1 = shapeFactory.getShape("CIRCLE"); //調用 Circle draw 方法 shape1.draw(); //獲取形狀爲 Rectangle 的對象 Shape shape2 = shapeFactory.getShape("RECTANGLE"); //調用 Rectangle draw 方法 shape2.draw(); //獲取形狀爲 Square 的對象 Shape shape3 = shapeFactory.getShape("SQUARE"); //調用 Square draw 方法 shape3.draw(); //獲取顏色工廠 AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR"); //獲取顏色爲 Red 的對象 Color color1 = colorFactory.getColor("RED"); //調用 Red fill 方法 color1.fill(); //獲取顏色爲 Green 的對象 Color color2 = colorFactory.getColor("Green"); //調用 Green fill 方法 color2.fill(); //獲取顏色爲 Blue 的對象 Color color3 = colorFactory.getColor("BLUE"); //調用 Blue fill 方法 color3.fill(); } }

步驟 9

執行程序,輸出結果:

Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
Inside Red::fill() method.
Inside Green::fill() method.
Inside Blue::fill() method.

單例模式

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。

這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。

注意:

  • 1、單例類只能有一個實例。
  • 2、單例類必須自己創建自己的唯一實例。
  • 3、單例類必須給所有其他對象提供這一實例。

介紹

意圖:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

主要解決:一個全局使用的類頻繁地創建與銷燬。

何時使用:當您想控制實例數目,節省系統資源的時候。

如何解決:判斷系統是否已經有這個單例,如果有則返回,如果沒有則創建。

關鍵代碼:構造函數是私有的。

應用實例:

  • 1、一個班級只有一個班主任。
  • 2Windows 是多進程多線程的,在操作一個文件的時候,就不可避免地出現多個進程或線程同時操作一個文件的現象,所以所有文件的處理必須通過唯一的實例來進行。
  • 3、一些設備管理器常常設計爲單例模式,比如一個電腦有兩臺打印機,在輸出的時候就要處理不能兩臺打印機打印同一個文件。

優點:

  • 1、在內存裏只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷燬實例(比如管理學院首頁頁面緩存)。
  • 2、避免對資源的多重佔用(比如寫文件操作)。

缺點:沒有接口,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。

使用場景:

  • 1、要求生產唯一序列號。
  • 2WEB 中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。
  • 3、創建的一個對象需要消耗的資源過多,比如 I/O 與數據庫的連接等。

注意事項:getInstance() 方法中需要使用同步鎖 synchronized (Singleton.class) 防止多線程同時進入造成 instance 被多次實例化。

實現

我們將創建一個 SingleObject 類。SingleObject 類有它的私有構造函數和本身的一個靜態實例。

SingleObject 類提供了一個靜態方法,供外界獲取它的靜態實例。SingletonPatternDemo,我們的演示類使用 SingleObject 類來獲取 SingleObject對象。

步驟 1

創建一個 Singleton 類。

SingleObject.java

public class SingleObject { //創建 SingleObject 的一個對象 private static SingleObject instance = new SingleObject(); //讓構造函數爲 private,這樣該類就不會被實例化 private SingleObject(){} //獲取唯一可用的對象 public static SingleObject getInstance(){ return instance; } public void showMessage(){ System.out.println("Hello World!"); } }

步驟 2

singleton 類獲取唯一的對象。

SingletonPatternDemo.java

public class SingletonPatternDemo { public static void main(String[] args) { //不合法的構造函數 //編譯時錯誤:構造函數 SingleObject() 是不可見的 //SingleObject object = new SingleObject(); //獲取唯一可用的對象 SingleObject object = SingleObject.getInstance(); //顯示消息 object.showMessage(); } }

步驟 3

執行程序,輸出結果:

Hello World!

單例模式的幾種實現方式

單例模式的實現有多種方式,如下所示:

1、懶漢式,線程不安全

是否 Lazy 初始化:

是否多線程安全:

實現難度:

描述:這種方式是最基本的實現方式,這種實現最大的問題就是不支持多線程。因爲沒有加鎖 synchronized,所以嚴格意義上它並不算單例模式。
這種方式 lazy loading 很明顯,不要求線程安全,在多線程不能正常工作。

實例

public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

接下來介紹的幾種實現方式都支持多線程,但是在性能上有所差異。

2、懶漢式,線程安全

是否 Lazy 初始化:

是否多線程安全:

實現難度:

描述:這種方式具備很好的 lazy loading,能夠在多線程中很好的工作,但是,效率很低,99% 情況下不需要同步。
優點:第一次調用才初始化,避免內存浪費。
缺點:必須加鎖 synchronized 才能保證單例,但加鎖會影響效率。
getInstance() 的性能對應用程序不是很關鍵(該方法使用不太頻繁)。

實例

public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

3、餓漢式

是否 Lazy 初始化:

是否多線程安全:

實現難度:

描述:這種方式比較常用,但容易產生垃圾對象。
優點:沒有加鎖,執行效率會提高。
缺點:類加載時就初始化,浪費內存。
它基於 classloader 機制避免了多線程的同步問題,不過,instance 在類裝載時就實例化,雖然導致類裝載的原因有很多種,在單例模式中大多數都是調用 getInstance 方法, 但是也不能確定有其他的方式(或者其他的靜態方法)導致類裝載,這時候初始化 instance 顯然沒有達到 lazy loading 的效果。

實例

public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }

4、雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking

JDK 版本:JDK1.5

是否 Lazy 初始化:

是否多線程安全:

實現難度:較複雜

描述:這種方式採用雙鎖機制,安全且在多線程情況下能保持高性能。
getInstance() 的性能對應用程序很關鍵。

實例

public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }

5、登記式/靜態內部類

是否 Lazy 初始化:

是否多線程安全:

實現難度:一般

描述:這種方式能達到雙檢鎖方式一樣的功效,但實現更簡單。對靜態域使用延遲初始化,應使用這種方式而不是雙檢鎖方式。這種方式只適用於靜態域的情況,雙檢鎖方式可在實例域需要延遲初始化時使用。
這種方式同樣利用了 classloader 機制來保證初始化 instance 時只有一個線程,它跟第 3 種方式不同的是:第 3 種方式只要 Singleton 類被裝載了,那麼 instance 就會被實例化(沒有達到 lazy loading 效果),而這種方式是 Singleton 類被裝載了,instance 不一定被初始化。因爲 SingletonHolder 類沒有被主動使用,只有通過顯式調用 getInstance 方法時,纔會顯式裝載 SingletonHolder 類,從而實例化 instance。想象一下,如果實例化 instance 很消耗資源,所以想讓它延遲加載,另外一方面,又不希望在 Singleton 類加載時就實例化,因爲不能確保 Singleton 類還可能在其他的地方被主動使用從而被加載,那麼這個時候實例化 instance 顯然是不合適的。這個時候,這種方式相比第 3 種方式就顯得很合理。

實例

public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }

6、枚舉

JDK 版本:JDK1.5

是否 Lazy 初始化:

是否多線程安全:

實現難度:

描述:這種實現方式還沒有被廣泛採用,但這是實現單例模式的最佳方法。它更簡潔,自動支持序列化機制,絕對防止多次實例化。
這種方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不僅能避免多線程同步問題,而且還自動支持序列化機制,防止反序列化重新創建新的對象,絕對防止多次實例化。不過,由於 JDK1.5 之後才加入 enum 特性,用這種方式寫不免讓人感覺生疏,在實際工作中,也很少用。
不能通過 reflection attack 來調用私有構造方法。

實例

public enum Singleton { INSTANCE; public void whateverMethod() { } }

經驗之談:一般情況下,不建議使用第 1 種和第 2 種懶漢方式,建議使用第 3 種餓漢方式。只有在要明確實現 lazy loading 效果時,纔會使用第 5 種登記方式。如果涉及到反序列化創建對象時,可以嘗試使用第 6 種枚舉方式。如果有其他特殊的需求,可以考慮使用第 4 種雙檢鎖方式。

建造者模式

建造者模式(Builder Pattern)使用多個簡單的對象一步一步構建成一個複雜的對象。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。

一個 Builder 類會一步一步構造最終的對象。該 Builder 類是獨立於其他對象的。

介紹

意圖:將一個複雜的構建與其表示相分離,使得同樣的構建過程可以創建不同的表示。

主要解決:主要解決在軟件系統中,有時候面臨着"一個複雜對象"的創建工作,其通常由各個部分的子對象用一定的算法構成;由於需求的變化,這個複雜對象的各個部分經常面臨着劇烈的變化,但是將它們組合在一起的算法卻相對穩定。

何時使用:一些基本部件不會變,而其組合經常變化的時候。

如何解決:將變與不變分離開。

關鍵代碼:建造者:創建和提供實例,導演:管理建造出來的實例的依賴關係。

應用實例: 1、去肯德基,漢堡、可樂、薯條、炸雞翅等是不變的,而其組合是經常變化的,生成出所謂的"套餐" 2JAVA 中的 StringBuilder

優點: 1、建造者獨立,易擴展。 2、便於控制細節風險。

缺點: 1、產品必須有共同點,範圍有限制。 2、如內部變化複雜,會有很多的建造類。

使用場景: 1、需要生成的對象具有複雜的內部結構。 2、需要生成的對象內部屬性本身相互依賴。

注意事項:與工廠模式的區別是:建造者模式更加關注與零件裝配的順序。

實現

我們假設一個快餐店的商業案例,其中,一個典型的套餐可以是一個漢堡(Burger)和一杯冷飲(Cold drink)。漢堡(Burger)可以是素食漢堡(Veg Burger)或雞肉漢堡(Chicken Burger),它們是包在紙盒中。冷飲(Cold drink)可以是可口可樂(coke)或百事可樂(pepsi),它們是裝在瓶子中。

我們將創建一個表示食物條目(比如漢堡和冷飲)的 Item 接口和實現 Item 接口的實體類,以及一個表示食物包裝的 Packing 接口和實現 Packing接口的實體類,漢堡是包在紙盒中,冷飲是裝在瓶子中。

然後我們創建一個 Meal 類,帶有 Item  ArrayList 和一個通過結合 Item 來創建不同類型的 Meal 對象的 MealBuilderBuilderPatternDemo,我們的演示類使用 MealBuilder 來創建一個 Meal

步驟 1

創建一個表示食物條目和食物包裝的接口。

Item.java

public interface Item { public String name(); public Packing packing(); public float price(); }

Packing.java

public interface Packing { public String pack(); }

步驟 2

創建實現 Packing 接口的實體類。

Wrapper.java

public class Wrapper implements Packing { @Override public String pack() { return "Wrapper"; } }

Bottle.java

public class Bottle implements Packing { @Override public String pack() { return "Bottle"; } }

步驟 3

創建實現 Item 接口的抽象類,該類提供了默認的功能。

Burger.java

public abstract class Burger implements Item { @Override public Packing packing() { return new Wrapper(); } @Override public abstract float price(); }

ColdDrink.java

public abstract class ColdDrink implements Item { @Override public Packing packing() { return new Bottle(); } @Override public abstract float price(); }

步驟 4

創建擴展了 Burger ColdDrink 的實體類。

VegBurger.java

public class VegBurger extends Burger { @Override public float price() { return 25.0f; } @Override public String name() { return "Veg Burger"; } }

ChickenBurger.java

public class ChickenBurger extends Burger { @Override public float price() { return 50.5f; } @Override public String name() { return "Chicken Burger"; } }

Coke.java

public class Coke extends ColdDrink { @Override public float price() { return 30.0f; } @Override public String name() { return "Coke"; } }

Pepsi.java

public class Pepsi extends ColdDrink { @Override public float price() { return 35.0f; } @Override public String name() { return "Pepsi"; } }

步驟 5

創建一個 Meal 類,帶有上面定義的 Item 對象。

Meal.java

import java.util.ArrayList; import java.util.List; public class Meal { private List<Item> items = new ArrayList<Item>(); public void addItem(Item item){ items.add(item); } public float getCost(){ float cost = 0.0f; for (Item item : items) { cost += item.price(); } return cost; } public void showItems(){ for (Item item : items) { System.out.print("Item : "+item.name()); System.out.print(", Packing : "+item.packing().pack()); System.out.println(", Price : "+item.price()); } } }

步驟 6

創建一個 MealBuilder 類,實際的 builder 類負責創建 Meal 對象。

MealBuilder.java

public class MealBuilder { public Meal prepareVegMeal (){ Meal meal = new Meal(); meal.addItem(new VegBurger()); meal.addItem(new Coke()); return meal; } public Meal prepareNonVegMeal (){ Meal meal = new Meal(); meal.addItem(new ChickenBurger()); meal.addItem(new Pepsi()); return meal; } }

步驟 7

BuiderPatternDemo 使用 MealBuider 來演示建造者模式(Builder Pattern)。

BuilderPatternDemo.java

public class BuilderPatternDemo { public static void main(String[] args) { MealBuilder mealBuilder = new MealBuilder(); Meal vegMeal = mealBuilder.prepareVegMeal(); System.out.println("Veg Meal"); vegMeal.showItems(); System.out.println("Total Cost: " +vegMeal.getCost()); Meal nonVegMeal = mealBuilder.prepareNonVegMeal(); System.out.println("\n\nNon-Veg Meal"); nonVegMeal.showItems(); System.out.println("Total Cost: " +nonVegMeal.getCost()); } }

步驟 8

執行程序,輸出結果:

Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0

 

 
Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5

原型模式

原型模式(Prototype Pattern)是用於創建重複的對象,同時又能保證性能。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。

這種模式是實現了一個原型接口,該接口用於創建當前對象的克隆。當直接創建對象的代價比較大時,則採用這種模式。例如,一個對象需要在一個高代價的數據庫操作之後被創建。我們可以緩存該對象,在下一個請求時返回它的克隆,在需要的時候更新數據庫,以此來減少數據庫調用。

介紹

意圖:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。

主要解決:在運行期建立和刪除原型。

何時使用: 1、當一個系統應該獨立於它的產品創建,構成和表示時。 2、當要實例化的類是在運行時刻指定時,例如,通過動態裝載。 3、爲了避免創建一個與產品類層次平行的工廠類層次時。 4、當一個類的實例只能有幾個不同狀態組合中的一種時。建立相應數目的原型並克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。

如何解決:利用已有的一個原型對象,快速地生成和原型對象一樣的實例。

關鍵代碼: 1、實現克隆操作,在 JAVA 繼承 Cloneable,重寫 clone(),在 .NET 中可以使用 Object 類的 MemberwiseClone() 方法來實現對象的淺拷貝或通過序列化的方式來實現深拷貝。 2、原型模式同樣用於隔離類對象的使用者和具體類型(易變類)之間的耦合關係,它同樣要求這些"易變類"擁有穩定的接口。

應用實例: 1、細胞分裂。 2JAVA 中的 Object clone() 方法。

優點: 1、性能提高。 2、逃避構造函數的約束。

缺點: 1、配備克隆方法需要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不一定很容易,特別當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。 2、必須實現 Cloneable 接口。

使用場景: 1、資源優化場景。 2、類初始化需要消化非常多的資源,這個資源包括數據、硬件資源等。 3、性能和安全要求的場景。 4、通過 new 產生一個對象需要非常繁瑣的數據準備或訪問權限,則可以使用原型模式。 5、一個對象多個修改者的場景。 6、一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用。 7、在實際項目中,原型模式很少單獨出現,一般是和工廠方法模式一起出現,通過 clone 的方法創建一個對象,然後由工廠方法提供給調用者。原型模式已經與 Java 融爲渾然一體,大家可以隨手拿來使用。

注意事項:與通過對一個類進行實例化來構造新對象不同的是,原型模式是通過拷貝一個現有對象生成新對象的。淺拷貝實現 Cloneable,重寫,深拷貝是通過實現 Serializable 讀取二進制流。

實現

我們將創建一個抽象類 Shape 和擴展了 Shape 類的實體類。下一步是定義類 ShapeCache,該類把 shape 對象存儲在一個 Hashtable 中,並在請求的時候返回它們的克隆。

PrototypePatternDemo,我們的演示類使用 ShapeCache 類來獲取 Shape 對象。

步驟 1

創建一個實現了 Cloneable 接口的抽象類。

Shape.java

public abstract class Shape implements Cloneable { private String id; protected String type; abstract void draw(); public String getType(){ return type; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } }

步驟 2

創建擴展了上面抽象類的實體類。

Rectangle.java

public class Rectangle extends Shape { public Rectangle(){ type = "Rectangle"; } @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } }

Square.java

public class Square extends Shape { public Square(){ type = "Square"; } @Override public void draw() { System.out.println("Inside Square::draw() method."); } }

Circle.java

public class Circle extends Shape { public Circle(){ type = "Circle"; } @Override public void draw() { System.out.println("Inside Circle::draw() method."); } }

步驟 3

創建一個類,從數據庫獲取實體類,並把它們存儲在一個 Hashtable 中。

ShapeCache.java

import java.util.Hashtable; public class ShapeCache { private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>(); public static Shape getShape(String shapeId) { Shape cachedShape = shapeMap.get(shapeId); return (Shape) cachedShape.clone(); } // 對每種形狀都運行數據庫查詢,並創建該形狀 // shapeMap.put(shapeKey, shape); // 例如,我們要添加三種形狀 public static void loadCache() { Circle circle = new Circle(); circle.setId("1"); shapeMap.put(circle.getId(),circle); Square square = new Square(); square.setId("2"); shapeMap.put(square.getId(),square); Rectangle rectangle = new Rectangle(); rectangle.setId("3"); shapeMap.put(rectangle.getId(),rectangle); } }

步驟 4

PrototypePatternDemo 使用 ShapeCache 類來獲取存儲在 Hashtable 中的形狀的克隆。

PrototypePatternDemo.java

public class PrototypePatternDemo { public static void main(String[] args) { ShapeCache.loadCache(); Shape clonedShape = (Shape) ShapeCache.getShape("1"); System.out.println("Shape : " + clonedShape.getType()); Shape clonedShape2 = (Shape) ShapeCache.getShape("2"); System.out.println("Shape : " + clonedShape2.getType()); Shape clonedShape3 = (Shape) ShapeCache.getShape("3"); System.out.println("Shape : " + clonedShape3.getType()); } }

步驟 5

執行程序,輸出結果:

Shape : Circle
Shape : Square
Shape : Rectangle

適配器模式

適配器模式(Adapter Pattern)是作爲兩個不兼容的接口之間的橋樑。這種類型的設計模式屬於結構型模式,它結合了兩個獨立接口的功能。

這種模式涉及到一個單一的類,該類負責加入獨立的或不兼容的接口功能。舉個真實的例子,讀卡器是作爲內存卡和筆記本之間的適配器。您將內存卡插入讀卡器,再將讀卡器插入筆記本,這樣就可以通過筆記本來讀取內存卡。

我們通過下面的實例來演示適配器模式的使用。其中,音頻播放器設備只能播放 mp3 文件,通過使用一個更高級的音頻播放器來播放 vlc mp4 文件。

介紹

意圖:將一個類的接口轉換成客戶希望的另外一個接口。適配器模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。

主要解決:主要解決在軟件系統中,常常要將一些"現存的對象"放到新的環境中,而新環境要求的接口是現對象不能滿足的。

何時使用: 1、系統需要使用現有的類,而此類的接口不符合系統的需要。 2、想要建立一個可以重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作,這些源類不一定有一致的接口。 3、通過接口轉換,將一個類插入另一個類系中。(比如老虎和飛禽,現在多了一個飛虎,在不增加實體的需求下,增加一個適配器,在裏面包容一個虎對象,實現飛的接口。)

如何解決:繼承或依賴(推薦)。

關鍵代碼:適配器繼承或依賴已有的對象,實現想要的目標接口。

應用實例: 1、美國電器 110V,中國 220V,就要有一個適配器將 110V 轉化爲 220V 2JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 JDK,則要將以前系統的 Enumeration 接口轉化爲 Iterator 接口,這時就需要適配器模式。 3、在 LINUX 上運行 WINDOWS 程序。 4JAVA 中的 jdbc

優點: 1、可以讓任何兩個沒有關聯的類一起運行。 2、提高了類的複用。 3、增加了類的透明度。 4、靈活性好。

缺點: 1、過多地使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是 A 接口,其實內部被適配成了 B 接口的實現,一個系統如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統進行重構。 2.由於 JAVA 至多繼承一個類,所以至多隻能適配一個適配者類,而且目標類必須是抽象類。

使用場景:有動機地修改一個正常運行的系統的接口,這時應該考慮使用適配器模式。

注意事項:適配器不是在詳細設計時添加的,而是解決正在服役的項目的問題。

實現

我們有一個 MediaPlayer 接口和一個實現了 MediaPlayer 接口的實體類 AudioPlayer。默認情況下,AudioPlayer 可以播放 mp3 格式的音頻文件。

我們還有另一個接口 AdvancedMediaPlayer 和實現了 AdvancedMediaPlayer 接口的實體類。該類可以播放 vlc mp4 格式的文件。

我們想要讓 AudioPlayer 播放其他格式的音頻文件。爲了實現這個功能,我們需要創建一個實現了 MediaPlayer 接口的適配器類 MediaAdapter,並使用 AdvancedMediaPlayer 對象來播放所需的格式。

AudioPlayer 使用適配器類 MediaAdapter 傳遞所需的音頻類型,不需要知道能播放所需格式音頻的實際類。AdapterPatternDemo,我們的演示類使用 AudioPlayer 類來播放各種格式。

步驟 1

爲媒體播放器和更高級的媒體播放器創建接口。

MediaPlayer.java

public interface MediaPlayer { public void play(String audioType, String fileName); }

AdvancedMediaPlayer.java

public interface AdvancedMediaPlayer { public void playVlc(String fileName); public void playMp4(String fileName); }

步驟 2

創建實現了 AdvancedMediaPlayer 接口的實體類。

VlcPlayer.java

public class VlcPlayer implements AdvancedMediaPlayer{ @Override public void playVlc(String fileName) { System.out.println("Playing vlc file. Name: "+ fileName); } @Override public void playMp4(String fileName) { //什麼也不做 } }

Mp4Player.java

public class Mp4Player implements AdvancedMediaPlayer{ @Override public void playVlc(String fileName) { //什麼也不做 } @Override public void playMp4(String fileName) { System.out.println("Playing mp4 file. Name: "+ fileName); } }

步驟 3

創建實現了 MediaPlayer 接口的適配器類。

MediaAdapter.java

public class MediaAdapter implements MediaPlayer { AdvancedMediaPlayer advancedMusicPlayer; public MediaAdapter(String audioType){ if(audioType.equalsIgnoreCase("vlc") ){ advancedMusicPlayer = new VlcPlayer(); } else if (audioType.equalsIgnoreCase("mp4")){ advancedMusicPlayer = new Mp4Player(); } } @Override public void play(String audioType, String fileName) { if(audioType.equalsIgnoreCase("vlc")){ advancedMusicPlayer.playVlc(fileName); }else if(audioType.equalsIgnoreCase("mp4")){ advancedMusicPlayer.playMp4(fileName); } } }

步驟 4

創建實現了 MediaPlayer 接口的實體類。

AudioPlayer.java

public class AudioPlayer implements MediaPlayer { MediaAdapter mediaAdapter; @Override public void play(String audioType, String fileName) { //播放 mp3 音樂文件的內置支持 if(audioType.equalsIgnoreCase("mp3")){ System.out.println("Playing mp3 file. Name: "+ fileName); } //mediaAdapter 提供了播放其他文件格式的支持 else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){ mediaAdapter = new MediaAdapter(audioType); mediaAdapter.play(audioType, fileName); } else{ System.out.println("Invalid media. "+ audioType + " format not supported"); } } }

步驟 5

使用 AudioPlayer 來播放不同類型的音頻格式。

AdapterPatternDemo.java

public class AdapterPatternDemo { public static void main(String[] args) { AudioPlayer audioPlayer = new AudioPlayer(); audioPlayer.play("mp3", "beyond the horizon.mp3"); audioPlayer.play("mp4", "alone.mp4"); audioPlayer.play("vlc", "far far away.vlc"); audioPlayer.play("avi", "mind me.avi"); } }

步驟 6

執行程序,輸出結果:

Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported

橋接模式

橋接(Bridge)是用於把抽象化與實現化解耦,使得二者可以獨立變化。這種類型的設計模式屬於結構型模式,它通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦。

這種模式涉及到一個作爲橋接的接口,使得實體類的功能獨立於接口實現類。這兩種類型的類可被結構化改變而互不影響。

我們通過下面的實例來演示橋接模式(Bridge Pattern)的用法。其中,可以使用相同的抽象類方法但是不同的橋接實現類,來畫出不同顏色的圓。

介紹

意圖:將抽象部分與實現部分分離,使它們都可以獨立的變化。

主要解決:在有多種可能會變化的情況下,用繼承會造成類爆炸問題,擴展起來不靈活。

何時使用:實現系統可能有多個角度分類,每一種角度都可能變化。

如何解決:把這種多角度分類分離出來,讓它們獨立變化,減少它們之間耦合。

關鍵代碼:抽象類依賴實現類。

應用實例: 1、豬八戒從天蓬元帥轉世投胎到豬,轉世投胎的機制將塵世劃分爲兩個等級,即:靈魂和肉體,前者相當於抽象化,後者相當於實現化。生靈通過功能的委派,調用肉體對象的功能,使得生靈可以動態地選擇。 2、牆上的開關,可以看到的開關是抽象的,不用管裏面具體怎麼實現的。

優點: 1、抽象和實現的分離。 2、優秀的擴展能力。 3、實現細節對客戶透明。

缺點:橋接模式的引入會增加系統的理解與設計難度,由於聚合關聯關係建立在抽象層,要求開發者針對抽象進行設計與編程。

使用場景: 1、如果一個系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態的繼承聯繫,通過橋接模式可以使它們在抽象層建立一個關聯關係。 2、對於那些不希望使用繼承或因爲多層次繼承導致系統類的個數急劇增加的系統,橋接模式尤爲適用。 3、一個類存在兩個獨立變化的維度,且這兩個維度都需要進行擴展。

注意事項:對於兩個獨立變化的維度,使用橋接模式再適合不過了。

實現

我們有一個作爲橋接實現的 DrawAPI 接口和實現了 DrawAPI 接口的實體類 RedCircleGreenCircleShape 是一個抽象類,將使用 DrawAPI 的對象。BridgePatternDemo,我們的演示類使用 Shape 類來畫出不同顏色的圓。

步驟 1

創建橋接實現接口。

DrawAPI.java

public interface DrawAPI { public void drawCircle(int radius, int x, int y); }

步驟 2

創建實現了 DrawAPI 接口的實體橋接實現類。

RedCircle.java

public class RedCircle implements DrawAPI { @Override public void drawCircle(int radius, int x, int y) { System.out.println("Drawing Circle[ color: red, radius: " + radius +", x: " +x+", "+ y +"]"); } }

GreenCircle.java

public class GreenCircle implements DrawAPI { @Override public void drawCircle(int radius, int x, int y) { System.out.println("Drawing Circle[ color: green, radius: " + radius +", x: " +x+", "+ y +"]"); } }

步驟 3

使用 DrawAPI 接口創建抽象類 Shape

Shape.java

public abstract class Shape { protected DrawAPI drawAPI; protected Shape(DrawAPI drawAPI){ this.drawAPI = drawAPI; } public abstract void draw(); }

步驟 4

創建實現了 Shape 接口的實體類。

Circle.java

public class Circle extends Shape { private int x, y, radius; public Circle(int x, int y, int radius, DrawAPI drawAPI) { super(drawAPI); this.x = x; this.y = y; this.radius = radius; } public void draw() { drawAPI.drawCircle(radius,x,y); } }

步驟 5

使用 Shape  DrawAPI 類畫出不同顏色的圓。

BridgePatternDemo.java

public class BridgePatternDemo { public static void main(String[] args) { Shape redCircle = new Circle(100,100, 10, new RedCircle()); Shape greenCircle = new Circle(100,100, 10, new GreenCircle()); redCircle.draw(); greenCircle.draw(); } }

步驟 6

執行程序,輸出結果:

Drawing Circle[ color: red, radius: 10, x: 100, 100]
Drawing Circle[  color: green, radius: 10, x: 100, 100]

過濾器模式

過濾器模式(Filter Pattern)或標準模式(Criteria Pattern)是一種設計模式,這種模式允許開發人員使用不同的標準來過濾一組對象,通過邏輯運算以解耦的方式把它們連接起來。這種類型的設計模式屬於結構型模式,它結合多個標準來獲得單一標準。

實現

我們將創建一個 Person 對象、Criteria 接口和實現了該接口的實體類,來過濾 Person 對象的列表。CriteriaPatternDemo,我們的演示類使用 Criteria 對象,基於各種標準和它們的結合來過濾 Person 對象的列表。

步驟 1

創建一個類,在該類上應用標準。

Person.java

public class Person { private String name; private String gender; private String maritalStatus; public Person(String name,String gender,String maritalStatus){ this.name = name; this.gender = gender; this.maritalStatus = maritalStatus; } public String getName() { return name; } public String getGender() { return gender; } public String getMaritalStatus() { return maritalStatus; } }

步驟 2

爲標準(Criteria)創建一個接口。

Criteria.java

import java.util.List; public interface Criteria { public List<Person> meetCriteria(List<Person> persons); }

步驟 3

創建實現了 Criteria 接口的實體類。

CriteriaMale.java

import java.util.ArrayList; import java.util.List; public class CriteriaMale implements Criteria { @Override public List<Person> meetCriteria(List<Person> persons) { List<Person> malePersons = new ArrayList<Person>(); for (Person person : persons) { if(person.getGender().equalsIgnoreCase("MALE")){ malePersons.add(person); } } return malePersons; } }

CriteriaFemale.java

import java.util.ArrayList; import java.util.List; public class CriteriaFemale implements Criteria { @Override public List<Person> meetCriteria(List<Person> persons) { List<Person> femalePersons = new ArrayList<Person>(); for (Person person : persons) { if(person.getGender().equalsIgnoreCase("FEMALE")){ femalePersons.add(person); } } return femalePersons; } }

CriteriaSingle.java

import java.util.ArrayList; import java.util.List; public class CriteriaSingle implements Criteria { @Override public List<Person> meetCriteria(List<Person> persons) { List<Person> singlePersons = new ArrayList<Person>(); for (Person person : persons) { if(person.getMaritalStatus().equalsIgnoreCase("SINGLE")){ singlePersons.add(person); } } return singlePersons; } }

AndCriteria.java

import java.util.List; public class AndCriteria implements Criteria { private Criteria criteria; private Criteria otherCriteria; public AndCriteria(Criteria criteria, Criteria otherCriteria) { this.criteria = criteria; this.otherCriteria = otherCriteria; } @Override public List<Person> meetCriteria(List<Person> persons) { List<Person> firstCriteriaPersons = criteria.meetCriteria(persons); return otherCriteria.meetCriteria(firstCriteriaPersons); } }

OrCriteria.java

import java.util.List; public class OrCriteria implements Criteria { private Criteria criteria; private Criteria otherCriteria; public OrCriteria(Criteria criteria, Criteria otherCriteria) { this.criteria = criteria; this.otherCriteria = otherCriteria; } @Override public List<Person> meetCriteria(List<Person> persons) { List<Person> firstCriteriaItems = criteria.meetCriteria(persons); List<Person> otherCriteriaItems = otherCriteria.meetCriteria(persons); for (Person person : otherCriteriaItems) { if(!firstCriteriaItems.contains(person)){ firstCriteriaItems.add(person); } } return firstCriteriaItems; } }

步驟4

使用不同的標準(Criteria)和它們的結合來過濾 Person 對象的列表。

CriteriaPatternDemo.java

import java.util.ArrayList; import java.util.List; public class CriteriaPatternDemo { public static void main(String[] args) { List<Person> persons = new ArrayList<Person>(); persons.add(new Person("Robert","Male", "Single")); persons.add(new Person("John","Male", "Married")); persons.add(new Person("Laura","Female", "Married")); persons.add(new Person("Diana","Female", "Single")); persons.add(new Person("Mike","Male", "Single")); persons.add(new Person("Bobby","Male", "Single")); Criteria male = new CriteriaMale(); Criteria female = new CriteriaFemale(); Criteria single = new CriteriaSingle(); Criteria singleMale = new AndCriteria(single, male); Criteria singleOrFemale = new OrCriteria(single, female); System.out.println("Males: "); printPersons(male.meetCriteria(persons)); System.out.println("\nFemales: "); printPersons(female.meetCriteria(persons)); System.out.println("\nSingle Males: "); printPersons(singleMale.meetCriteria(persons)); System.out.println("\nSingle Or Females: "); printPersons(singleOrFemale.meetCriteria(persons)); } public static void printPersons(List<Person> persons){ for (Person person : persons) { System.out.println("Person : [ Name : " + person.getName() +", Gender : " + person.getGender() +", Marital Status : " + person.getMaritalStatus() +" ]"); } } }

步驟 5

執行程序,輸出結果:

Males: 
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : John, Gender : Male, Marital Status : Married ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]

 
Females: 
Person : [ Name : Laura, Gender : Female, Marital Status : Married ]
Person : [ Name : Diana, Gender : Female, Marital Status : Single ]

 
Single Males: 
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]

 
Single Or Females: 
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : Diana, Gender : Female, Marital Status : Single ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]
Person : [ Name : Laura, Gender : Female, Marital Status : Married ]

組合模式

組合模式(Composite Pattern),又叫部分整體模式,是用於把一組相似的對象當作一個單一的對象。組合模式依據樹形結構來組合對象,用來表示部分以及整體層次。這種類型的設計模式屬於結構型模式,它創建了對象組的樹形結構。

這種模式創建了一個包含自己對象組的類。該類提供了修改相同對象組的方式。

我們通過下面的實例來演示組合模式的用法。實例演示了一個組織中員工的層次結構。

介紹

意圖:將對象組合成樹形結構以表示"部分-整體"的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。

主要解決:它在我們樹型結構的問題中,模糊了簡單元素和複雜元素的概念,客戶程序可以像處理簡單元素一樣來處理複雜元素,從而使得客戶程序與複雜元素的內部結構解耦。

何時使用: 1、您想表示對象的部分-整體層次結構(樹形結構)。 2、您希望用戶忽略組合對象與單個對象的不同,用戶將統一地使用組合結構中的所有對象。

如何解決:樹枝和葉子實現統一接口,樹枝內部組合該接口。

關鍵代碼:樹枝內部組合該接口,並且含有內部屬性 List,裏面放 Component

應用實例: 1、算術表達式包括操作數、操作符和另一個操作數,其中,另一個操作符也可以是操作數、操作符和另一個操作數。 2、在 JAVA AWT SWING 中,對於 Button Checkbox 是樹葉,Container 是樹枝。

優點: 1、高層模塊調用簡單。 2、節點自由增加。

缺點:在使用組合模式時,其葉子和樹枝的聲明都是實現類,而不是接口,違反了依賴倒置原則。

使用場景:部分、整體場景,如樹形菜單,文件、文件夾的管理。

注意事項:定義時爲具體類。

實現

我們有一個類 Employee,該類被當作組合模型類。CompositePatternDemo,我們的演示類使用 Employee 類來添加部門層次結構,並打印所有員工。

 

步驟 1

創建 Employee 類,該類帶有 Employee 對象的列表。

Employee.java

import java.util.ArrayList; import java.util.List; public class Employee { private String name; private String dept; private int salary; private List<Employee> subordinates; //構造函數 public Employee(String name,String dept, int sal) { this.name = name; this.dept = dept; this.salary = sal; subordinates = new ArrayList<Employee>(); } public void add(Employee e) { subordinates.add(e); } public void remove(Employee e) { subordinates.remove(e); } public List<Employee> getSubordinates(){ return subordinates; } public String toString(){ return ("Employee :[ Name : "+ name +", dept : "+ dept + ", salary :" + salary+" ]"); } }

步驟 2

使用 Employee 類來創建和打印員工的層次結構。

CompositePatternDemo.java

public class CompositePatternDemo { public static void main(String[] args) { Employee CEO = new Employee("John","CEO", 30000); Employee headSales = new Employee("Robert","Head Sales", 20000); Employee headMarketing = new Employee("Michel","Head Marketing", 20000); Employee clerk1 = new Employee("Laura","Marketing", 10000); Employee clerk2 = new Employee("Bob","Marketing", 10000); Employee salesExecutive1 = new Employee("Richard","Sales", 10000); Employee salesExecutive2 = new Employee("Rob","Sales", 10000); CEO.add(headSales); CEO.add(headMarketing); headSales.add(salesExecutive1); headSales.add(salesExecutive2); headMarketing.add(clerk1); headMarketing.add(clerk2); //打印該組織的所有員工 System.out.println(CEO); for (Employee headEmployee : CEO.getSubordinates()) { System.out.println(headEmployee); for (Employee employee : headEmployee.getSubordinates()) { System.out.println(employee); } } } }

步驟 3

執行程序,輸出結果爲:

Employee :[ Name : John, dept : CEO, salary :30000 ]
Employee :[ Name : Robert, dept : Head Sales, salary :20000 ]
Employee :[ Name : Richard, dept : Sales, salary :10000 ]
Employee :[ Name : Rob, dept : Sales, salary :10000 ]
Employee :[ Name : Michel, dept : Head Marketing, salary :20000 ]
Employee :[ Name : Laura, dept : Marketing, salary :10000 ]
Employee :[ Name : Bob, dept : Marketing, salary :10000 ]

裝飾器模式

裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是作爲現有的類的一個包裝。

這種模式創建了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。

我們通過下面的實例來演示裝飾器模式的用法。其中,我們將把一個形狀裝飾上不同的顏色,同時又不改變形狀類。

介紹

意圖:動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更爲靈活。

主要解決:一般的,我們爲了擴展一個類經常使用繼承方式實現,由於繼承爲類引入靜態特徵,並且隨着擴展功能的增多,子類會很膨脹。

何時使用:在不想增加很多子類的情況下擴展類。

如何解決:將具體功能職責劃分,同時繼承裝飾者模式。

關鍵代碼: 1Component 類充當抽象角色,不應該具體實現。 2、修飾類引用和繼承 Component 類,具體擴展類重寫父類方法。

應用實例: 1、孫悟空有 72 變,當他變成"廟宇"後,他的根本還是一隻猴子,但是他又有了廟宇的功能。 2、不論一幅畫有沒有畫框都可以掛在牆上,但是通常都是有畫框的,並且實際上是畫框被掛在牆上。在掛在牆上之前,畫可以被蒙上玻璃,裝到框子裏;這時畫、玻璃和畫框形成了一個物體。

優點:裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴展一個實現類的功能。

缺點:多層裝飾比較複雜。

使用場景: 1、擴展一個類的功能。 2、動態增加功能,動態撤銷。

注意事項:可代替繼承。

實現

我們將創建一個 Shape 接口和實現了 Shape 接口的實體類。然後我們創建一個實現了 Shape 接口的抽象裝飾類 ShapeDecorator,並把 Shape對象作爲它的實例變量。

RedShapeDecorator 是實現了 ShapeDecorator 的實體類。

DecoratorPatternDemo,我們的演示類使用 RedShapeDecorator 來裝飾 Shape 對象。

 

步驟 1

創建一個接口:

Shape.java

public interface Shape { void draw(); }

步驟 2

創建實現接口的實體類。

Rectangle.java

public class Rectangle implements Shape { @Override public void draw() { System.out.println("Shape: Rectangle"); } }

Circle.java

public class Circle implements Shape { @Override public void draw() { System.out.println("Shape: Circle"); } }

步驟 3

創建實現了 Shape 接口的抽象裝飾類。

ShapeDecorator.java

public abstract class ShapeDecorator implements Shape { protected Shape decoratedShape; public ShapeDecorator(Shape decoratedShape){ this.decoratedShape = decoratedShape; } public void draw(){ decoratedShape.draw(); } }

步驟 4

創建擴展了 ShapeDecorator 類的實體裝飾類。

RedShapeDecorator.java

public class RedShapeDecorator extends ShapeDecorator { public RedShapeDecorator(Shape decoratedShape) { super(decoratedShape); } @Override public void draw() { decoratedShape.draw(); setRedBorder(decoratedShape); } private void setRedBorder(Shape decoratedShape){ System.out.println("Border Color: Red"); } }

步驟 5

使用 RedShapeDecorator 來裝飾 Shape 對象。

DecoratorPatternDemo.java

public class DecoratorPatternDemo { public static void main(String[] args) { Shape circle = new Circle(); ShapeDecorator redCircle = new RedShapeDecorator(new Circle()); ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle()); //Shape redCircle = new RedShapeDecorator(new Circle()); //Shape redRectangle = new RedShapeDecorator(new Rectangle()); System.out.println("Circle with normal border"); circle.draw(); System.out.println("\nCircle of red border"); redCircle.draw(); System.out.println("\nRectangle of red border"); redRectangle.draw(); } }

步驟 6

執行程序,輸出結果:

Circle with normal border
Shape: Circle

 
Circle of red border
Shape: Circle
Border Color: Red

 
Rectangle of red border
Shape: Rectangle
Border Color: Red

外觀模式

外觀模式(Facade Pattern)隱藏系統的複雜性,並向客戶端提供了一個客戶端可以訪問系統的接口。這種類型的設計模式屬於結構型模式,它向現有的系統添加一個接口,來隱藏系統的複雜性。

這種模式涉及到一個單一的類,該類提供了客戶端請求的簡化方法和對現有系統類方法的委託調用。

介紹

意圖:爲子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。

主要解決:降低訪問複雜系統的內部子系統時的複雜度,簡化客戶端與之的接口。

何時使用: 1、客戶端不需要知道系統內部的複雜聯繫,整個系統只需提供一個"接待員"即可。 2、定義系統的入口。

如何解決:客戶端不與系統耦合,外觀類與系統耦合。

關鍵代碼:在客戶端和複雜系統之間再加一層,這一層將調用順序、依賴關係等處理好。

應用實例: 1、去醫院看病,可能要去掛號、門診、劃價、取藥,讓患者或患者家屬覺得很複雜,如果有提供接待人員,只讓接待人員來處理,就很方便。 2JAVA 的三層開發模式。

優點: 1、減少系統相互依賴。 2、提高靈活性。 3、提高了安全性。

缺點:不符合開閉原則,如果要改東西很麻煩,繼承重寫都不合適。

使用場景: 1、爲複雜的模塊或子系統提供外界訪問的模塊。 2、子系統相對獨立。 3、預防低水平人員帶來的風險。

注意事項:在層次化結構中,可以使用外觀模式定義系統中每一層的入口。

實現

我們將創建一個 Shape 接口和實現了 Shape 接口的實體類。下一步是定義一個外觀類 ShapeMaker

ShapeMaker 類使用實體類來代表用戶對這些類的調用。FacadePatternDemo,我們的演示類使用 ShapeMaker 類來顯示結果。

 

步驟 1

創建一個接口。

Shape.java

public interface Shape { void draw(); }

步驟 2

創建實現接口的實體類。

Rectangle.java

public class Rectangle implements Shape { @Override public void draw() { System.out.println("Rectangle::draw()"); } }

Square.java

public class Square implements Shape { @Override public void draw() { System.out.println("Square::draw()"); } }

Circle.java

public class Circle implements Shape { @Override public void draw() { System.out.println("Circle::draw()"); } }

步驟 3

創建一個外觀類。

ShapeMaker.java

public class ShapeMaker { private Shape circle; private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); rectangle = new Rectangle(); square = new Square(); } public void drawCircle(){ circle.draw(); } public void drawRectangle(){ rectangle.draw(); } public void drawSquare(){ square.draw(); } }

步驟 4

使用該外觀類畫出各種類型的形狀。

FacadePatternDemo.java

public class FacadePatternDemo { public static void main(String[] args) { ShapeMaker shapeMaker = new ShapeMaker(); shapeMaker.drawCircle(); shapeMaker.drawRectangle(); shapeMaker.drawSquare(); } }

步驟 5

執行程序,輸出結果:

Circle::draw()
Rectangle::draw()
Square::draw()

享元模式

享元模式(Flyweight Pattern)主要用於減少創建對象的數量,以減少內存佔用和提高性能。這種類型的設計模式屬於結構型模式,它提供了減少對象數量從而改善應用所需的對象結構的方式。

享元模式嘗試重用現有的同類對象,如果未找到匹配的對象,則創建新對象。我們將通過創建 5 個對象來畫出 20 個分佈於不同位置的圓來演示這種模式。由於只有 5 種可用的顏色,所以 color 屬性被用來檢查現有的 Circle 對象。

介紹

意圖:運用共享技術有效地支持大量細粒度的對象。

主要解決:在有大量對象時,有可能會造成內存溢出,我們把其中共同的部分抽象出來,如果有相同的業務請求,直接返回在內存中已有的對象,避免重新創建。

何時使用: 1、系統中有大量對象。 2、這些對象消耗大量內存。 3、這些對象的狀態大部分可以外部化。 4、這些對象可以按照內蘊狀態分爲很多組,當把外蘊對象從對象中剔除出來時,每一組對象都可以用一個對象來代替。 5、系統不依賴於這些對象身份,這些對象是不可分辨的。

如何解決:用唯一標識碼判斷,如果在內存中有,則返回這個唯一標識碼所標識的對象。

關鍵代碼: HashMap 存儲這些對象。

應用實例: 1JAVA 中的 String,如果有則返回,如果沒有則創建一個字符串保存在字符串緩存池裏面。 2、數據庫的數據池。

優點:大大減少對象的創建,降低系統的內存,使效率提高。

缺點:提高了系統的複雜度,需要分離出外部狀態和內部狀態,而且外部狀態具有固有化的性質,不應該隨着內部狀態的變化而變化,否則會造成系統的混亂。

使用場景: 1、系統有大量相似對象。 2、需要緩衝池的場景。

注意事項: 1、注意劃分外部狀態和內部狀態,否則可能會引起線程安全問題。 2、這些類必須有一個工廠對象加以控制。

實現

我們將創建一個 Shape 接口和實現了 Shape 接口的實體類 Circle。下一步是定義工廠類 ShapeFactory

ShapeFactory 有一個 Circle  HashMap,其中鍵名爲 Circle 對象的顏色。無論何時接收到請求,都會創建一個特定顏色的圓。ShapeFactory 檢查它的 HashMap 中的 circle 對象,如果找到 Circle 對象,則返回該對象,否則將創建一個存儲在 hashmap 中以備後續使用的新對象,並把該對象返回到客戶端。

FlyWeightPatternDemo,我們的演示類使用 ShapeFactory 來獲取 Shape 對象。它將向 ShapeFactory 傳遞信息(red / green / blue/ black / white),以便獲取它所需對象的顏色。

 

步驟 1

創建一個接口。

Shape.java

public interface Shape { void draw(); }

步驟 2

創建實現接口的實體類。

Circle.java

public class Circle implements Shape { private String color; private int x; private int y; private int radius; public Circle(String color){ this.color = color; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public void setRadius(int radius) { this.radius = radius; } @Override public void draw() { System.out.println("Circle: Draw() [Color : " + color +", x : " + x +", y :" + y +", radius :" + radius); } }

步驟 3

創建一個工廠,生成基於給定信息的實體類的對象。

ShapeFactory.java

import java.util.HashMap; public class ShapeFactory { private static final HashMap<String, Shape> circleMap = new HashMap<>(); public static Shape getCircle(String color) { Circle circle = (Circle)circleMap.get(color); if(circle == null) { circle = new Circle(color); circleMap.put(color, circle); System.out.println("Creating circle of color : " + color); } return circle; } }

步驟 4

使用該工廠,通過傳遞顏色信息來獲取實體類的對象。

FlyweightPatternDemo.java

public class FlyweightPatternDemo { private static final String colors[] = { "Red", "Green", "Blue", "White", "Black" }; public static void main(String[] args) { for(int i=0; i < 20; ++i) { Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor()); circle.setX(getRandomX()); circle.setY(getRandomY()); circle.setRadius(100); circle.draw(); } } private static String getRandomColor() { return colors[(int)(Math.random()*colors.length)]; } private static int getRandomX() { return (int)(Math.random()*100 ); } private static int getRandomY() { return (int)(Math.random()*100); } }

步驟 5

執行程序,輸出結果:

Creating circle of color : Black
Circle: Draw() [Color : Black, x : 36, y :71, radius :100
Creating circle of color : Green
Circle: Draw() [Color : Green, x : 27, y :27, radius :100
Creating circle of color : White
Circle: Draw() [Color : White, x : 64, y :10, radius :100
Creating circle of color : Red
Circle: Draw() [Color : Red, x : 15, y :44, radius :100
Circle: Draw() [Color : Green, x : 19, y :10, radius :100
Circle: Draw() [Color : Green, x : 94, y :32, radius :100
Circle: Draw() [Color : White, x : 69, y :98, radius :100
Creating circle of color : Blue
Circle: Draw() [Color : Blue, x : 13, y :4, radius :100
Circle: Draw() [Color : Green, x : 21, y :21, radius :100
Circle: Draw() [Color : Blue, x : 55, y :86, radius :100
Circle: Draw() [Color : White, x : 90, y :70, radius :100
Circle: Draw() [Color : Green, x : 78, y :3, radius :100
Circle: Draw() [Color : Green, x : 64, y :89, radius :100
Circle: Draw() [Color : Blue, x : 3, y :91, radius :100
Circle: Draw() [Color : Blue, x : 62, y :82, radius :100
Circle: Draw() [Color : Green, x : 97, y :61, radius :100
Circle: Draw() [Color : Green, x : 86, y :12, radius :100
Circle: Draw() [Color : Green, x : 38, y :93, radius :100
Circle: Draw() [Color : Red, x : 76, y :82, radius :100
Circle: Draw() [Color : Blue, x : 95, y :82, radius :100

代理模式

在代理模式(Proxy Pattern)中,一個類代表另一個類的功能。這種類型的設計模式屬於結構型模式。

在代理模式中,我們創建具有現有對象的對象,以便向外界提供功能接口。

介紹

意圖:爲其他對象提供一種代理以控制對這個對象的訪問。

主要解決:在直接訪問對象時帶來的問題,比如說:要訪問的對象在遠程的機器上。在面向對象系統中,有些對象由於某些原因(比如對象創建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層。

何時使用:想在訪問一個類時做一些控制。

如何解決:增加中間層。

關鍵代碼:實現與被代理類組合。

應用實例: 1Windows 裏面的快捷方式。 2、豬八戒去找高翠蘭結果是孫悟空變的,可以這樣理解:把高翠蘭的外貌抽象出來,高翠蘭本人和孫悟空都實現了這個接口,豬八戒訪問高翠蘭的時候看不出來這個是孫悟空,所以說孫悟空是高翠蘭代理類。 3、買火車票不一定在火車站買,也可以去代售點。 4、一張支票或銀行存單是賬戶中資金的代理。支票在市場交易中用來代替現金,並提供對簽發人賬號上資金的控制。 5spring aop

優點: 1、職責清晰。 2、高擴展性。 3、智能化。

缺點: 1、由於在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。 2、實現代理模式需要額外的工作,有些代理模式的實現非常複雜。

使用場景:按職責來劃分,通常有以下使用場景: 1、遠程代理。 2、虛擬代理。 3Copy-on-Write 代理。 4、保護(Protect or Access)代理。 5Cache代理。 6、防火牆(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。

注意事項: 1、和適配器模式的區別:適配器模式主要改變所考慮對象的接口,而代理模式不能改變所代理類的接口。 2、和裝飾器模式的區別:裝飾器模式爲了增強功能,而代理模式是爲了加以控制。

實現

我們將創建一個 Image 接口和實現了 Image 接口的實體類。ProxyImage 是一個代理類,減少 RealImage 對象加載的內存佔用。

ProxyPatternDemo,我們的演示類使用 ProxyImage 來獲取要加載的 Image 對象,並按照需求進行顯示。

 

步驟 1

創建一個接口。

Image.java

public interface Image { void display(); }

步驟 2

創建實現接口的實體類。

RealImage.java

public class RealImage implements Image { private String fileName; public RealImage(String fileName){ this.fileName = fileName; loadFromDisk(fileName); } @Override public void display() { System.out.println("Displaying " + fileName); } private void loadFromDisk(String fileName){ System.out.println("Loading " + fileName); } }

ProxyImage.java

public class ProxyImage implements Image{ private RealImage realImage; private String fileName; public ProxyImage(String fileName){ this.fileName = fileName; } @Override public void display() { if(realImage == null){ realImage = new RealImage(fileName); } realImage.display(); } }

步驟 3

當被請求時,使用 ProxyImage 來獲取 RealImage 類的對象。

ProxyPatternDemo.java

public class ProxyPatternDemo { public static void main(String[] args) { Image image = new ProxyImage("test_10mb.jpg"); // 圖像將從磁盤加載 image.display(); System.out.println(""); // 圖像不需要從磁盤加載 image.display(); } }

步驟 4

執行程序,輸出結果:

Loading test_10mb.jpg
Displaying test_10mb.jpg

 
Displaying test_10mb.jpg

責任鏈模式

顧名思義,責任鏈模式(Chain of Responsibility Pattern)爲請求創建了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬於行爲型模式。

在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。

介紹

意圖:避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,並且沿着這條鏈傳遞請求,直到有對象處理它爲止。

主要解決:職責鏈上的處理者負責處理請求,客戶只需要將請求發送到職責鏈上即可,無須關心請求的處理細節和請求的傳遞,所以職責鏈將請求的發送者和請求的處理者解耦了。

何時使用:在處理消息的時候以過濾很多道。

如何解決:攔截的類都實現統一接口。

關鍵代碼:Handler 裏面聚合它自己,在 HandlerRequest 裏判斷是否合適,如果沒達到條件則向下傳遞,向誰傳遞之前 set 進去。

應用實例: 1、紅樓夢中的"擊鼓傳花" 2JS 中的事件冒泡。 3JAVA WEB Apache Tomcat Encoding 的處理,Struts2 的攔截器,jsp servlet Filter

優點: 1、降低耦合度。它將請求的發送者和接收者解耦。 2、簡化了對象。使得對象不需要知道鏈的結構。 3、增強給對象指派職責的靈活性。通過改變鏈內的成員或者調動它們的次序,允許動態地新增或者刪除責任。 4、增加新的請求處理類很方便。

缺點: 1、不能保證請求一定被接收。 2、系統性能將受到一定影響,而且在進行代碼調試時不太方便,可能會造成循環調用。 3、可能不容易觀察運行時的特徵,有礙於除錯。

使用場景: 1、有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定。 2、在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。 3、可動態指定一組對象處理請求。

注意事項: JAVA WEB 中遇到很多應用。

實現

我們創建抽象類 AbstractLogger,帶有詳細的日誌記錄級別。然後我們創建三種類型的記錄器,都擴展了 AbstractLogger。每個記錄器消息的級別是否屬於自己的級別,如果是則相應地打印出來,否則將不打印並把消息傳給下一個記錄器。

 

步驟 1

創建抽象的記錄器類。

AbstractLogger.java

public abstract class AbstractLogger { public static int INFO = 1; public static int DEBUG = 2; public static int ERROR = 3; protected int level; //責任鏈中的下一個元素 protected AbstractLogger nextLogger; public void setNextLogger(AbstractLogger nextLogger){ this.nextLogger = nextLogger; } public void logMessage(int level, String message){ if(this.level <= level){ write(message); } if(nextLogger !=null){ nextLogger.logMessage(level, message); } } abstract protected void write(String message); }

步驟 2

創建擴展了該記錄器類的實體類。

ConsoleLogger.java

public class ConsoleLogger extends AbstractLogger { public ConsoleLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Standard Console::Logger: " + message); } }

ErrorLogger.java

public class ErrorLogger extends AbstractLogger { public ErrorLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Error Console::Logger: " + message); } }

FileLogger.java

public class FileLogger extends AbstractLogger { public FileLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("File::Logger: " + message); } }

步驟 3

創建不同類型的記錄器。賦予它們不同的錯誤級別,並在每個記錄器中設置下一個記錄器。每個記錄器中的下一個記錄器代表的是鏈的一部分。

ChainPatternDemo.java

public class ChainPatternDemo { private static AbstractLogger getChainOfLoggers(){ AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR); AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG); AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO); errorLogger.setNextLogger(fileLogger); fileLogger.setNextLogger(consoleLogger); return errorLogger; } public static void main(String[] args) { AbstractLogger loggerChain = getChainOfLoggers(); loggerChain.logMessage(AbstractLogger.INFO, "This is an information."); loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information."); loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information."); } }

步驟 4

執行程序,輸出結果:

Standard Console::Logger: This is an information.
File::Logger: This is a debug level information.
Standard Console::Logger: This is a debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.

命令模式

命令模式(Command Pattern)是一種數據驅動的設計模式,它屬於行爲型模式。請求以命令的形式包裹在對象中,並傳給調用對象。調用對象尋找可以處理該命令的合適的對象,並把該命令傳給相應的對象,該對象執行命令。

介紹

意圖:將一個請求封裝成一個對象,從而使您可以用不同的請求對客戶進行參數化。

主要解決:在軟件系統中,行爲請求者與行爲實現者通常是一種緊耦合的關係,但某些場合,比如需要對行爲進行記錄、撤銷或重做、事務等處理時,這種無法抵禦變化的緊耦合的設計就不太合適。

何時使用:在某些場合,比如要對行爲進行"記錄、撤銷/重做、事務"等處理,這種無法抵禦變化的緊耦合是不合適的。在這種情況下,如何將"行爲請求者""行爲實現者"解耦?將一組行爲抽象爲對象,可以實現二者之間的松耦合。

如何解決:通過調用者調用接受者執行命令,順序:調用者接受者命令。

關鍵代碼:定義三個角色:1received 真正的命令執行對象 2Command 3invoker 使用命令對象的入口

應用實例:struts 1 中的 action 核心控制器 ActionServlet 只有一個,相當於 Invoker,而模型層的類會隨着不同的應用有不同的模型類,相當於具體的 Command

優點: 1、降低了系統耦合度。 2、新的命令可以很容易添加到系統中去。

缺點:使用命令模式可能會導致某些系統有過多的具體命令類。

使用場景:認爲是命令的地方都可以使用命令模式,比如: 1GUI 中每一個按鈕都是一條命令。 2、模擬 CMD

注意事項:系統需要支持命令的撤銷(Undo)操作和恢復(Redo)操作,也可以考慮使用命令模式,見命令模式的擴展。

實現

我們首先創建作爲命令的接口 Order,然後創建作爲請求的 Stock 類。實體命令類 BuyStock  SellStock,實現了 Order 接口,將執行實際的命令處理。創建作爲調用對象的類 Broker,它接受訂單並能下訂單。

Broker 對象使用命令模式,基於命令的類型確定哪個對象執行哪個命令。CommandPatternDemo,我們的演示類使用 Broker 類來演示命令模式。

 

步驟 1

創建一個命令接口。

Order.java

public interface Order { void execute(); }

步驟 2

創建一個請求類。

Stock.java

public class Stock { private String name = "ABC"; private int quantity = 10; public void buy(){ System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] bought"); } public void sell(){ System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] sold"); } }

步驟 3

創建實現了 Order 接口的實體類。

BuyStock.java

public class BuyStock implements Order { private Stock abcStock; public BuyStock(Stock abcStock){ this.abcStock = abcStock; } public void execute() { abcStock.buy(); } }

SellStock.java

public class SellStock implements Order { private Stock abcStock; public SellStock(Stock abcStock){ this.abcStock = abcStock; } public void execute() { abcStock.sell(); } }

步驟 4

創建命令調用類。

Broker.java

import java.util.ArrayList; import java.util.List; public class Broker { private List<Order> orderList = new ArrayList<Order>(); public void takeOrder(Order order){ orderList.add(order); } public void placeOrders(){ for (Order order : orderList) { order.execute(); } orderList.clear(); } }

步驟 5

使用 Broker 類來接受並執行命令。

CommandPatternDemo.java

public class CommandPatternDemo { public static void main(String[] args) { Stock abcStock = new Stock(); BuyStock buyStockOrder = new BuyStock(abcStock); SellStock sellStockOrder = new SellStock(abcStock); Broker broker = new Broker(); broker.takeOrder(buyStockOrder); broker.takeOrder(sellStockOrder); broker.placeOrders(); } }

步驟 6

執行程序,輸出結果:

Stock [ Name: ABC, Quantity: 10 ] bought
Stock [ Name: ABC, Quantity: 10 ] sold
  1. Python 方式:
  1. # Command Pattern with Python Code
  2. from abc import  abstractmethod,ABCMeta
  3.  
  4. # 創建一個命令接口Order
  5. class Order(metaclass=ABCMeta):
  6.     @abstractmethod
  7.     def execute(self):
  8.         pass
  9.  
  10. # 創建一個請求類
  11. class Stock():
  12.     _name = "ABC"
  13.     _quantity = 10
  14.     def buy(self):
  15.         print("Stock [Name : {0}, Quantity: {1}] bought.".format(self._name,self._quantity))
  16.     def sell(self):
  17.         print("Stock [Name : {0}, Quantity: {1}] sold.".format(self._name, self._quantity))
  18.  
  19. # 創建實現了Order接口的實體類
  20. class BuyStock(Order):
  21.     _abcStock = None
  22.     def __init__(self,inStock):
  23.         self._abcStock = inStock
  24.     def execute(self):
  25.         self._abcStock.buy()
  26. class SellStock(Order):
  27.     _abcStock = None
  28.     def __init__(self,inStock):
  29.         self._abcStock = inStock
  30.     def execute(self):
  31.         self._abcStock.sell()
  32.  
  33. # 創建命令調用類
  34. class Broker():
  35.     _orderList = []
  36.     def takeOrder(self,inOrder):
  37.         self._orderList.append(inOrder)
  38.     def placeOrders(self):
  39.         for aOrder in self._orderList:
  40.             aOrder.execute()
  41.         self._orderList.clear()
  42.  
  43. # 調用輸出
  44. if __name__ == '__main__':
  45.     abcStock = Stock()
  46.     buyStockOrder = BuyStock(abcStock)
  47.     sellStockOrder = SellStock(abcStock)
  48.  
  49.     broker = Broker()
  50.     broker.takeOrder(buyStockOrder)
  51.     broker.takeOrder(sellStockOrder)
  52.  

    broker.placeOrders()

 

Python 代碼:

# Command Pattern with Python Code

from abc import  abstractmethod,ABCMeta

 

# 創建一個命令接口Order

class Order(metaclass=ABCMeta):

    @abstractmethod

    def execute(self):

        pass

 

# 創建一個請求類

class Stock():

    _name = "ABC"

    _quantity = 10

    def buy(self):

        print("Stock [Name : {0}, Quantity: {1}] bought.".format(self._name,self._quantity))

    def sell(self):

        print("Stock [Name : {0}, Quantity: {1}] sold.".format(self._name, self._quantity))

 

# 創建實現了Order接口的實體類

class BuyStock(Order):

    _abcStock = None

    def __init__(self,inStock):

        self._abcStock = inStock

    def execute(self):

        self._abcStock.buy()

class SellStock(Order):

    _abcStock = None

    def __init__(self,inStock):

        self._abcStock = inStock

    def execute(self):

        self._abcStock.sell()

 

# 創建命令調用類

class Broker():

    _orderList = []

    def takeOrder(self,inOrder):

        self._orderList.append(inOrder)

    def placeOrders(self):

        for aOrder in self._orderList:

            aOrder.execute()

        self._orderList.clear()

 

# 調用輸出

if __name__ == '__main__':

    abcStock = Stock()

    buyStockOrder = BuyStock(abcStock)

    sellStockOrder = SellStock(abcStock)

 

    broker = Broker()

    broker.takeOrder(buyStockOrder)

    broker.takeOrder(sellStockOrder)

 

    broker.placeOrders()

解釋器模式

解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式,它屬於行爲型模式。這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。

介紹

意圖:給定一個語言,定義它的文法表示,並定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子。

主要解決:對於一些固定文法構建一個解釋句子的解釋器。

何時使用:如果一種特定類型的問題發生的頻率足夠高,那麼可能就值得將該問題的各個實例表述爲一個簡單語言中的句子。這樣就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題。

如何解決:構建語法樹,定義終結符與非終結符。

關鍵代碼:構建環境類,包含解釋器之外的一些全局信息,一般是 HashMap

應用實例:編譯器、運算表達式計算。

優點: 1、可擴展性比較好,靈活。 2、增加了新的解釋表達式的方式。 3、易於實現簡單文法。

缺點: 1、可利用場景比較少。 2、對於複雜的文法比較難維護。 3、解釋器模式會引起類膨脹。 4、解釋器模式採用遞歸調用方法。

使用場景: 1、可以將一個需要解釋執行的語言中的句子表示爲一個抽象語法樹。 2、一些重複出現的問題可以用一種簡單的語言來進行表達。 3、一個簡單語法需要解釋的場景。

注意事項:可利用場景比較少,JAVA 中如果碰到可以用 expression4J 代替。

實現

我們將創建一個接口 Expression 和實現了 Expression 接口的實體類。定義作爲上下文中主要解釋器的 TerminalExpression 類。其他的類 OrExpressionAndExpression 用於創建組合式表達式。

InterpreterPatternDemo,我們的演示類使用 Expression 類創建規則和演示表達式的解析。

 

步驟 1

創建一個表達式接口。

Expression.java

public interface Expression { public boolean interpret(String context); }

步驟 2

創建實現了上述接口的實體類。

TerminalExpression.java

public class TerminalExpression implements Expression { private String data; public TerminalExpression(String data){ this.data = data; } @Override public boolean interpret(String context) { if(context.contains(data)){ return true; } return false; } }

OrExpression.java

public class OrExpression implements Expression { private Expression expr1 = null; private Expression expr2 = null; public OrExpression(Expression expr1, Expression expr2) { this.expr1 = expr1; this.expr2 = expr2; } @Override public boolean interpret(String context) { return expr1.interpret(context) || expr2.interpret(context); } }

AndExpression.java

public class AndExpression implements Expression { private Expression expr1 = null; private Expression expr2 = null; public AndExpression(Expression expr1, Expression expr2) { this.expr1 = expr1; this.expr2 = expr2; } @Override public boolean interpret(String context) { return expr1.interpret(context) && expr2.interpret(context); } }

步驟 3

InterpreterPatternDemo 使用 Expression 類來創建規則,並解析它們。

InterpreterPatternDemo.java

public class InterpreterPatternDemo { //規則:Robert John 是男性 public static Expression getMaleExpression(){ Expression robert = new TerminalExpression("Robert"); Expression john = new TerminalExpression("John"); return new OrExpression(robert, john); } //規則:Julie 是一個已婚的女性 public static Expression getMarriedWomanExpression(){ Expression julie = new TerminalExpression("Julie"); Expression married = new TerminalExpression("Married"); return new AndExpression(julie, married); } public static void main(String[] args) { Expression isMale = getMaleExpression(); Expression isMarriedWoman = getMarriedWomanExpression(); System.out.println("John is male? " + isMale.interpret("John")); System.out.println("Julie is a married women? " + isMarriedWoman.interpret("Married Julie")); } }

步驟 4

執行程序,輸出結果:

John is male? true
Julie is a married women? true

Python 代碼:

# Interpreter Pattern with Python Code

from abc import abstractmethod,ABCMeta

#創建一個表達式接口

class Expression(metaclass=ABCMeta):

    @abstractmethod

    def interpret(self, inContext):

        pass

# 創建實現Expression接口的實體類

class TerminalExpression(Expression):

    _data = ""

    def __init__(self,inData):

        self._data = inData

    def interpret(self,inContext):

        if inContext.find(self._data) >= 0:

            return True

        return False

class OrExpression(Expression):

    _expr1 = None

    _expr2 = None

    def __init__(self,inExpr1,inExpr2):

        self._expr1 = inExpr1

        self._expr2 = inExpr2

    def interpret(self, inContext):

        return self._expr1.interpret(inContext) or self._expr2.interpret(inContext)

class AndExpression(Expression):

    _expr1 = None

    _expr2 = None

    def __init__(self,inExpr1,inExpr2):

        self._expr1 = inExpr1

        self._expr2 = inExpr2

    def interpret(self, inContext):

        return self._expr1.interpret(inContext) and self._expr2.interpret(inContext)

# 調用輸出

if __name__ == '__main__':

    # 規則:RobertJohn是男性   

    def getMaleExpression():       

        robert = TerminalExpression("Robert")       

        john = TerminalExpression("John")       

        return OrExpression(robert,john)   

    # 規則:Julie是一個已婚的女性   

    def getMarriedWomanExpression():       

        julie = TerminalExpression("Julie")       

        married = TerminalExpression("Married")       

        return AndExpression(julie,married)   

    isMale = getMaleExpression()   

    isMarriedWoman = getMarriedWomanExpression()   

    print("John is male? " + str(isMale.interpret("John")))   

    print("Julie is a married women? " + str(isMarriedWoman.interpret("Married Julie")))

迭代器模式

迭代器模式(Iterator Pattern)是 Java .Net 編程環境中非常常用的設計模式。這種模式用於順序訪問集合對象的元素,不需要知道集合對象的底層表示。

迭代器模式屬於行爲型模式。

介紹

意圖:提供一種方法順序訪問一個聚合對象中各個元素, 而又無須暴露該對象的內部表示。

主要解決:不同的方式來遍歷整個整合對象。

何時使用:遍歷一個聚合對象。

如何解決:把在元素之間遊走的責任交給迭代器,而不是聚合對象。

關鍵代碼:定義接口:hasNext, next

應用實例:JAVA 中的 iterator

優點: 1、它支持以不同的方式遍歷一個聚合對象。 2、迭代器簡化了聚合類。 3、在同一個聚合上可以有多個遍歷。 4、在迭代器模式中,增加新的聚合類和迭代器類都很方便,無須修改原有代碼。

缺點:由於迭代器模式將存儲數據和遍歷數據的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,這在一定程度上增加了系統的複雜性。

使用場景: 1、訪問一個聚合對象的內容而無須暴露它的內部表示。 2、需要爲聚合對象提供多種遍歷方式。 3、爲遍歷不同的聚合結構提供一個統一的接口。

注意事項:迭代器模式就是分離了集合對象的遍歷行爲,抽象出一個迭代器類來負責,這樣既可以做到不暴露集合的內部結構,又可讓外部代碼透明地訪問集合內部的數據。

實現

我們將創建一個敘述導航方法的 Iterator 接口和一個返回迭代器的 Container 接口。實現了 Container 接口的實體類將負責實現 Iterator 接口。

IteratorPatternDemo,我們的演示類使用實體類 NamesRepository 來打印 NamesRepository 中存儲爲集合的 Names

 

步驟 1

創建接口:

Iterator.java

public interface Iterator { public boolean hasNext(); public Object next(); }

Container.java

public interface Container { public Iterator getIterator(); }

步驟 2

創建實現了 Container 接口的實體類。該類有實現了 Iterator 接口的內部類 NameIterator

NameRepository.java

public class NameRepository implements Container { public String names[] = {"Robert" , "John" ,"Julie" , "Lora"}; @Override public Iterator getIterator() { return new NameIterator(); } private class NameIterator implements Iterator { int index; @Override public boolean hasNext() { if(index < names.length){ return true; } return false; } @Override public Object next() { if(this.hasNext()){ return names[index++]; } return null; } } }

步驟 3

使用 NameRepository 來獲取迭代器,並打印名字。

IteratorPatternDemo.java

public class IteratorPatternDemo { public static void main(String[] args) { NameRepository namesRepository = new NameRepository(); for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){ String name = (String)iter.next(); System.out.println("Name : " + name); } } }

步驟 4

執行程序,輸出結果:

Name : Robert
Name : John
Name : Julie
Name : Lora
  1. StringArrayIterator 結合 headfirst 設計模式,發現上面的迭代器模式還可以擴展。

NameIterator 單獨作爲一個 public 類,專門針對 string[] 數據遍歷的公共 類。

public class StringArrayIterator implements Iterator{

    String[] args;

    int index = 0;

    public StringArrayIterator(String[] argTemp){

        this.args  = argsTemp;

    }

 

    @Override

    public boolean hasNext(){

        if(index < args.length){

            return true;

        }

        return false;

    }

 

    @Override

    public Object next(){

        if(index < args.length){

            return args[index++];

        }

        return null;

    }

}

 

public class NameRepository implements Container {

    public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};

    @Override

    public Iterator getIterator() {

        return new StringArrayIterator(names);

    } 

}

 

Python 方式:

# Iterator Pattern with Python Code

from abc import abstractmethod,ABCMeta

 

# 創建Iterator接口

class Iterator(metaclass=ABCMeta):

    @abstractmethod

    def hasNext(self):

        pass

    @abstractmethod

    def next(self):

        pass

# 創建Container接口

class Container(metaclass=ABCMeta):

    @abstractmethod

    def getIterator(self):

        pass

 

# 創建實現了Iterator接口的類NameIterator

class NameIterator(Iterator):

    index = 0

    aNameRepository = None

    def __init__(self, inNameRepository):

        self.aNameRepository = inNameRepository

 

    def hasNext(self):

        if self.index < len(self.aNameRepository.names):

            return True

        return False

    def next(self):

        if self.hasNext() :

            theName = self.aNameRepository.names[self.index]

            self.index += 1

            return theName

        return None

 

#創建實現了Container接口的實體類。

class NameRepository(Container):

    names = ["Robert","John","Julie","Lora"]

 

    def getIterator(self):

        return NameIterator(self)

 

# 調用輸出

if __name__ == '__main__':

    namesRespository = NameRepository()

    iter = namesRespository.getIterator()

    #print("Name : "+iter.aNameRepository.names[0])

    while iter.hasNext():

        strName = iter.next()

        print("Name : "+strName)

中介者模式

中介者模式(Mediator Pattern)是用來降低多個對象和類之間的通信複雜性。這種模式提供了一箇中介類,該類通常處理不同類之間的通信,並支持松耦合,使代碼易於維護。中介者模式屬於行爲型模式。

介紹

意圖:用一箇中介對象來封裝一系列的對象交互,中介者使各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互。

主要解決:對象與對象之間存在大量的關聯關係,這樣勢必會導致系統的結構變得很複雜,同時若一個對象發生改變,我們也需要跟蹤與之相關聯的對象,同時做出相應的處理。

何時使用:多個類相互耦合,形成了網狀結構。

如何解決:將上述網狀結構分離爲星型結構。

關鍵代碼:對象 Colleague 之間的通信封裝到一個類中單獨處理。

應用實例: 1、中國加入 WTO 之前是各個國家相互貿易,結構複雜,現在是各個國家通過 WTO 來互相貿易。 2、機場調度系統。 3MVC 框架,其中C(控制器)就是 M(模型)和 V(視圖)的中介者。

優點: 1、降低了類的複雜度,將一對多轉化成了一對一。 2、各個類之間的解耦。 3、符合迪米特原則。

缺點:中介者會龐大,變得複雜難以維護。

使用場景: 1、系統中對象之間存在比較複雜的引用關係,導致它們之間的依賴關係結構混亂而且難以複用該對象。 2、想通過一箇中間類來封裝多個類中的行爲,而又不想生成太多的子類。

注意事項:不應當在職責混亂的時候使用。

實現

我們通過聊天室實例來演示中介者模式。實例中,多個用戶可以向聊天室發送消息,聊天室向所有的用戶顯示消息。我們將創建兩個類 ChatRoom UserUser 對象使用 ChatRoom 方法來分享他們的消息。

MediatorPatternDemo,我們的演示類使用 User 對象來顯示他們之間的通信。

 

步驟 1

創建中介類。

ChatRoom.java

import java.util.Date; public class ChatRoom { public static void showMessage(User user, String message){ System.out.println(new Date().toString() + " [" + user.getName() +"] : " + message); } }

步驟 2

創建 user 類。

User.java

public class User { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public User(String name){ this.name = name; } public void sendMessage(String message){ ChatRoom.showMessage(this,message); } }

步驟 3

使用 User 對象來顯示他們之間的通信。

MediatorPatternDemo.java

public class MediatorPatternDemo { public static void main(String[] args) { User robert = new User("Robert"); User john = new User("John"); robert.sendMessage("Hi! John!"); john.sendMessage("Hello! Robert!"); } }

步驟 4

執行程序,輸出結果:

Thu Jan 31 16:05:46 IST 2013 [Robert] : Hi! John!
Thu Jan 31 16:05:46 IST 2013 [John] : Hello! Robert!

備忘錄模式

備忘錄模式(Memento Pattern)保存一個對象的某個狀態,以便在適當的時候恢復對象。備忘錄模式屬於行爲型模式。

介紹

意圖:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。

主要解決:所謂備忘錄模式就是在不破壞封裝的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態,這樣可以在以後將對象恢復到原先保存的狀態。

何時使用:很多時候我們總是需要記錄一個對象的內部狀態,這樣做的目的就是爲了允許用戶取消不確定或者錯誤的操作,能夠恢復到他原先的狀態,使得他有"後悔藥"可喫。

如何解決:通過一個備忘錄類專門存儲對象狀態。

關鍵代碼:客戶不與備忘錄類耦合,與備忘錄管理類耦合。

應用實例: 1、後悔藥。 2、打遊戲時的存檔。 3Windows 裏的 ctri + z 4IE 中的後退。 4、數據庫的事務管理。

優點: 1、給用戶提供了一種可以恢復狀態的機制,可以使用戶能夠比較方便地回到某個歷史的狀態。 2、實現了信息的封裝,使得用戶不需要關心狀態的保存細節。

缺點:消耗資源。如果類的成員變量過多,勢必會佔用比較大的資源,而且每一次保存都會消耗一定的內存。

使用場景: 1、需要保存/恢復數據的相關狀態場景。 2、提供一個可回滾的操作。

注意事項: 1、爲了符合迪米特原則,還要增加一個管理備忘錄的類。 2、爲了節約內存,可使用原型模式+備忘錄模式。

實現

備忘錄模式使用三個類 MementoOriginator  CareTakerMemento 包含了要被恢復的對象的狀態。Originator 創建並在 Memento 對象中存儲狀態。Caretaker 對象負責從 Memento 中恢復對象的狀態。

MementoPatternDemo,我們的演示類使用 CareTaker  Originator 對象來顯示對象的狀態恢復。

 

步驟 1

創建 Memento 類。

Memento.java

public class Memento { private String state; public Memento(String state){ this.state = state; } public String getState(){ return state; } }

步驟 2

創建 Originator 類。

Originator.java

public class Originator { private String state; public void setState(String state){ this.state = state; } public String getState(){ return state; } public Memento saveStateToMemento(){ return new Memento(state); } public void getStateFromMemento(Memento Memento){ state = Memento.getState(); } }

步驟 3

創建 CareTaker 類。

CareTaker.java

import java.util.ArrayList; import java.util.List; public class CareTaker { private List<Memento> mementoList = new ArrayList<Memento>(); public void add(Memento state){ mementoList.add(state); } public Memento get(int index){ return mementoList.get(index); } }

步驟 4

使用 CareTaker  Originator 對象。

MementoPatternDemo.java

public class MementoPatternDemo { public static void main(String[] args) { Originator originator = new Originator(); CareTaker careTaker = new CareTaker(); originator.setState("State #1"); originator.setState("State #2"); careTaker.add(originator.saveStateToMemento()); originator.setState("State #3"); careTaker.add(originator.saveStateToMemento()); originator.setState("State #4"); System.out.println("Current State: " + originator.getState()); originator.getStateFromMemento(careTaker.get(0)); System.out.println("First saved State: " + originator.getState()); originator.getStateFromMemento(careTaker.get(1)); System.out.println("Second saved State: " + originator.getState()); } }

步驟 5

驗證輸出。

Current State: State #4
First saved State: State #2
Second saved State: State #3

Python 方式:

# Memento Pattern with Python Code

from abc import abstractmethod,ABCMeta

 

# 創建Memento

class Memento():

    _state = ""

    def __init__(self,strState):

        self._state = strState

    def getState(self):

        return self._state

 

# 創建Originator

class Originator():

    _state = ""

    def setState(self,strState):

        self._state = strState

    def getState(self):

        return self._state

    def saveStateToMemento(self):

        return Memento(self._state)

    def getStateFromMemento(self,inMemento):

        self._state = inMemento.getState()

 

# 創建CareTaker

class CareTaker():

    _mementoList = []

    def add(self,inMemento):

        self._mementoList.append(inMemento)

    def get(self,inIndex):

        return self._mementoList[inIndex]

 

# 調用輸出

if __name__ == '__main__':

    originator = Originator()

    careTaker = CareTaker()

    originator.setState("State #1")

    originator.setState("State #2")

    careTaker.add(originator.saveStateToMemento())

    originator.setState("State #3")

    careTaker.add(originator.saveStateToMemento())

    originator.setState("State #4")

 

    print("Current State: " + originator.getState())

    originator.getStateFromMemento(careTaker.get(0))

    print("First saved State: " +  originator.getState())

    originator.getStateFromMemento(careTaker.get(1))

    print("First saved State: " +  originator.getState())

觀察者模式

當對象間存在一對多關係時,則使用觀察者模式(Observer Pattern)。比如,當一個對象被修改時,則會自動通知依賴它的對象。觀察者模式屬於行爲型模式。

介紹

意圖:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。

主要解決:一個對象狀態改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協作。

何時使用:一個對象(目標對象)的狀態發生改變,所有的依賴對象(觀察者對象)都將得到通知,進行廣播通知。

如何解決:使用面向對象技術,可以將這種依賴關係弱化。

關鍵代碼:在抽象類裏有一個 ArrayList 存放觀察者們。

應用實例: 1、拍賣的時候,拍賣師觀察最高標價,然後通知給其他競價者競價。 2、西遊記裏面悟空請求菩薩降服紅孩兒,菩薩灑了一地水招來一個老烏龜,這個烏龜就是觀察者,他觀察菩薩灑水這個動作。

優點: 1、觀察者和被觀察者是抽象耦合的。 2、建立一套觸發機制。

缺點: 1、如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。 2、如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。 3、觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。

使用場景:

  • 一個抽象模型有兩個方面,其中一個方面依賴於另一個方面。將這些方面封裝在獨立的對象中使它們可以各自獨立地改變和複用。
  • 一個對象的改變將導致其他一個或多個對象也發生改變,而不知道具體有多少對象將發生改變,可以降低對象之間的耦合度。
  • 一個對象必須通知其他對象,而並不知道這些對象是誰。
  • 需要在系統中創建一個觸發鏈,A對象的行爲將影響B對象,B對象的行爲將影響C對象……,可以使用觀察者模式創建一種鏈式觸發機制。

注意事項: 1JAVA 中已經有了對觀察者模式的支持類。 2、避免循環引用。 3、如果順序執行,某一觀察者錯誤會導致系統卡殼,一般採用異步方式。

實現

觀察者模式使用三個類 SubjectObserver ClientSubject 對象帶有綁定觀察者到 Client 對象和從 Client 對象解綁觀察者的方法。我們創建 Subject 類、Observer 抽象類和擴展了抽象類 Observer 的實體類。

ObserverPatternDemo,我們的演示類使用 Subject 和實體類對象來演示觀察者模式。

 

步驟 1

創建 Subject 類。

Subject.java

import java.util.ArrayList; import java.util.List; public class Subject { private List<Observer> observers = new ArrayList<Observer>(); private int state; public int getState() { return state; } public void setState(int state) { this.state = state; notifyAllObservers(); } public void attach(Observer observer){ observers.add(observer); } public void notifyAllObservers(){ for (Observer observer : observers) { observer.update(); } } }

步驟 2

創建 Observer 類。

Observer.java

public abstract class Observer { protected Subject subject; public abstract void update(); }

步驟 3

創建實體觀察者類。

BinaryObserver.java

public class BinaryObserver extends Observer{ public BinaryObserver(Subject subject){ this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) ); } }

OctalObserver.java

public class OctalObserver extends Observer{ public OctalObserver(Subject subject){ this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) ); } }

HexaObserver.java

public class HexaObserver extends Observer{ public HexaObserver(Subject subject){ this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Hex String: " + Integer.toHexString( subject.getState() ).toUpperCase() ); } }

步驟 4

使用 Subject 和實體觀察者對象。

ObserverPatternDemo.java

public class ObserverPatternDemo { public static void main(String[] args) { Subject subject = new Subject(); new HexaObserver(subject); new OctalObserver(subject); new BinaryObserver(subject); System.out.println("First state change: 15"); subject.setState(15); System.out.println("Second state change: 10"); subject.setState(10); } }

步驟 5

執行程序,輸出結果:

First state change: 15
Hex String: F
Octal String: 17
Binary String: 1111
Second state change: 10
Hex String: A
Octal String: 12
Binary String: 1010

狀態模式

在狀態模式(State Pattern)中,類的行爲是基於它的狀態改變的。這種類型的設計模式屬於行爲型模式。

在狀態模式中,我們創建表示各種狀態的對象和一個行爲隨着狀態對象改變而改變的 context 對象。

介紹

意圖:允許對象在內部狀態發生改變時改變它的行爲,對象看起來好像修改了它的類。

主要解決:對象的行爲依賴於它的狀態(屬性),並且可以根據它的狀態改變而改變它的相關行爲。

何時使用:代碼中包含大量與對象狀態有關的條件語句。

如何解決:將各種具體的狀態類抽象出來。

關鍵代碼:通常命令模式的接口中只有一個方法。而狀態模式的接口中有一個或者多個方法。而且,狀態模式的實現類的方法,一般返回值,或者是改變實例變量的值。也就是說,狀態模式一般和對象的狀態有關。實現類的方法有不同的功能,覆蓋接口中的方法。狀態模式和命令模式一樣,也可以用於消除 if...else 等條件選擇語句。

應用實例: 1、打籃球的時候運動員可以有正常狀態、不正常狀態和超常狀態。 2、曾侯乙編鐘中,'鍾是抽象接口','A'等是具體狀態,'曾侯乙編鐘'是具體環境(Context)。

優點: 1、封裝了轉換規則。 2、枚舉可能的狀態,在枚舉狀態之前需要確定狀態種類。 3、將所有與某個狀態有關的行爲放到一個類中,並且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行爲。 4、允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。 5、可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數。

缺點: 1、狀態模式的使用必然會增加系統類和對象的個數。 2、狀態模式的結構與實現都較爲複雜,如果使用不當將導致程序結構和代碼的混亂。 3、狀態模式對"開閉原則"的支持並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態,而且修改某個狀態類的行爲也需修改對應類的源代碼。

使用場景: 1、行爲隨狀態改變而改變的場景。 2、條件、分支語句的代替者。

注意事項:在行爲受狀態約束的時候使用狀態模式,而且狀態不超過 5 個。

實現

我們將創建一個 State 接口和實現了 State 接口的實體狀態類。Context 是一個帶有某個狀態的類。

StatePatternDemo,我們的演示類使用 Context 和狀態對象來演示 Context 在狀態改變時的行爲變化。

 

步驟 1

創建一個接口。

State.java

public interface State { public void doAction(Context context); }

步驟 2

創建實現接口的實體類。

StartState.java

public class StartState implements State { public void doAction(Context context) { System.out.println("Player is in start state"); context.setState(this); } public String toString(){ return "Start State"; } }

StopState.java

public class StopState implements State { public void doAction(Context context) { System.out.println("Player is in stop state"); context.setState(this); } public String toString(){ return "Stop State"; } }

步驟 3

創建 Context 類。

Context.java

public class Context { private State state; public Context(){ state = null; } public void setState(State state){ this.state = state; } public State getState(){ return state; } }

步驟 4

使用 Context 來查看當狀態 State 改變時的行爲變化。

StatePatternDemo.java

public class StatePatternDemo { public static void main(String[] args) { Context context = new Context(); StartState startState = new StartState(); startState.doAction(context); System.out.println(context.getState().toString()); StopState stopState = new StopState(); stopState.doAction(context); System.out.println(context.getState().toString()); } }

步驟 5

執行程序,輸出結果:

Player is in start state
Start State
Player is in stop state
Stop State

空對象模式

在空對象模式(Null Object Pattern)中,一個空對象取代 NULL 對象實例的檢查。Null 對象不是檢查空值,而是反應一個不做任何動作的關係。這樣的 Null 對象也可以在數據不可用的時候提供默認的行爲。

在空對象模式中,我們創建一個指定各種要執行的操作的抽象類和擴展該類的實體類,還創建一個未對該類做任何實現的空對象類,該空對象類將無縫地使用在需要檢查空值的地方。

實現

我們將創建一個定義操作(在這裏,是客戶的名稱)的 AbstractCustomer 抽象類,和擴展了 AbstractCustomer 類的實體類。工廠類 CustomerFactory 基於客戶傳遞的名字來返回 RealCustomer  NullCustomer 對象。

NullPatternDemo,我們的演示類使用 CustomerFactory 來演示空對象模式的用法。

 

步驟 1

創建一個抽象類。

AbstractCustomer.java

public abstract class AbstractCustomer { protected String name; public abstract boolean isNil(); public abstract String getName(); }

步驟 2

創建擴展了上述類的實體類。

RealCustomer.java

public class RealCustomer extends AbstractCustomer { public RealCustomer(String name) { this.name = name; } @Override public String getName() { return name; } @Override public boolean isNil() { return false; } }

NullCustomer.java

public class NullCustomer extends AbstractCustomer { @Override public String getName() { return "Not Available in Customer Database"; } @Override public boolean isNil() { return true; } }

步驟 3

創建 CustomerFactory 類。

CustomerFactory.java

public class CustomerFactory { public static final String[] names = {"Rob", "Joe", "Julie"}; public static AbstractCustomer getCustomer(String name){ for (int i = 0; i < names.length; i++) { if (names[i].equalsIgnoreCase(name)){ return new RealCustomer(name); } } return new NullCustomer(); } }

步驟 4

使用 CustomerFactory,基於客戶傳遞的名字,來獲取 RealCustomer  NullCustomer 對象。

NullPatternDemo.java

public class NullPatternDemo { public static void main(String[] args) { AbstractCustomer customer1 = CustomerFactory.getCustomer("Rob"); AbstractCustomer customer2 = CustomerFactory.getCustomer("Bob"); AbstractCustomer customer3 = CustomerFactory.getCustomer("Julie"); AbstractCustomer customer4 = CustomerFactory.getCustomer("Laura"); System.out.println("Customers"); System.out.println(customer1.getName()); System.out.println(customer2.getName()); System.out.println(customer3.getName()); System.out.println(customer4.getName()); } }

步驟 5

執行程序,輸出結果:

Customers
Rob
Not Available in Customer Database
Julie
Not Available in Customer Database

策略模式

在策略模式(Strategy Pattern)中,一個類的行爲或其算法可以在運行時更改。這種類型的設計模式屬於行爲型模式。

在策略模式中,我們創建表示各種策略的對象和一個行爲隨着策略對象改變而改變的 context 對象。策略對象改變 context 對象的執行算法。

介紹

意圖:定義一系列的算法,把它們一個個封裝起來, 並且使它們可相互替換。

主要解決:在有多種算法相似的情況下,使用 if...else 所帶來的複雜和難以維護。

何時使用:一個系統有許多許多類,而區分它們的只是他們直接的行爲。

如何解決:將這些算法封裝成一個一個的類,任意地替換。

關鍵代碼:實現同一個接口。

應用實例: 1、諸葛亮的錦囊妙計,每一個錦囊就是一個策略。 2、旅行的出遊方式,選擇騎自行車、坐汽車,每一種旅行方式都是一個策略。 3JAVA AWT 中的 LayoutManager

優點: 1、算法可以自由切換。 2、避免使用多重條件判斷。 3、擴展性良好。

缺點: 1、策略類會增多。 2、所有策略類都需要對外暴露。

使用場景: 1、如果在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式可以動態地讓一個對象在許多行爲中選擇一種行爲。 2、一個系統需要動態地在幾種算法中選擇一種。 3、如果一個對象有很多的行爲,如果不用恰當的模式,這些行爲就只好使用多重的條件選擇語句來實現。

注意事項:如果一個系統的策略多於四個,就需要考慮使用混合模式,解決策略類膨脹的問題。

實現

我們將創建一個定義活動的 Strategy 接口和實現了 Strategy 接口的實體策略類。Context 是一個使用了某種策略的類。

StrategyPatternDemo,我們的演示類使用 Context 和策略對象來演示 Context 在它所配置或使用的策略改變時的行爲變化。

 

步驟 1

創建一個接口。

Strategy.java

public interface Strategy { public int doOperation(int num1, int num2); }

步驟 2

創建實現接口的實體類。

OperationAdd.java

public class OperationAdd implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 + num2; } }

OperationSubtract.java

public class OperationSubtract implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 - num2; } }

OperationMultiply.java

public class OperationMultiply implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 * num2; } }

步驟 3

創建 Context 類。

Context.java

public class Context { private Strategy strategy; public Context(Strategy strategy){ this.strategy = strategy; } public int executeStrategy(int num1, int num2){ return strategy.doOperation(num1, num2); } }

步驟 4

使用 Context 來查看當它改變策略 Strategy 時的行爲變化。

StrategyPatternDemo.java

public class StrategyPatternDemo { public static void main(String[] args) { Context context = new Context(new OperationAdd()); System.out.println("10 + 5 = " + context.executeStrategy(10, 5)); context = new Context(new OperationSubtract()); System.out.println("10 - 5 = " + context.executeStrategy(10, 5)); context = new Context(new OperationMultiply()); System.out.println("10 * 5 = " + context.executeStrategy(10, 5)); } }

步驟 5

執行程序,輸出結果:

10 + 5 = 15
10 - 5 = 5
10 * 5 = 50

模板模式

在模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模板。它的子類可以按需要重寫方法實現,但調用將以抽象類中定義的方式進行。這種類型的設計模式屬於行爲型模式。

介紹

意圖:定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。

主要解決:一些方法通用,卻在每一個子類都重新寫了這一方法。

何時使用:有一些通用的方法。

如何解決:將這些通用算法抽象出來。

關鍵代碼:在抽象類實現,其他步驟在子類實現。

應用實例: 1、在造房子的時候,地基、走線、水管都一樣,只有在建築的後期纔有加壁櫥加柵欄等差異。 2、西遊記裏面菩薩定好的 81 難,這就是一個頂層的邏輯骨架。 3spring 中對 Hibernate 的支持,將一些已經定好的方法封裝起來,比如開啓事務、獲取 Session、關閉 Session 等,程序員不重複寫那些已經規範好的代碼,直接丟一個實體就可以保存。

優點: 1、封裝不變部分,擴展可變部分。 2、提取公共代碼,便於維護。 3、行爲由父類控制,子類實現。

缺點:每一個不同的實現都需要一個子類來實現,導致類的個數增加,使得系統更加龐大。

使用場景: 1、有多個子類共有的方法,且邏輯相同。 2、重要的、複雜的方法,可以考慮作爲模板方法。

注意事項:爲防止惡意操作,一般模板方法都加上 final 關鍵詞。

實現

我們將創建一個定義操作的 Game 抽象類,其中,模板方法設置爲 final,這樣它就不會被重寫。Cricket  Football 是擴展了 Game 的實體類,它們重寫了抽象類的方法。

TemplatePatternDemo,我們的演示類使用 Game 來演示模板模式的用法。

 

步驟 1

創建一個抽象類,它的模板方法被設置爲 final

Game.java

public abstract class Game { abstract void initialize(); abstract void startPlay(); abstract void endPlay(); //模板 public final void play(){ //初始化遊戲 initialize(); //開始遊戲 startPlay(); //結束遊戲 endPlay(); } }

步驟 2

創建擴展了上述類的實體類。

Cricket.java

public class Cricket extends Game { @Override void endPlay() { System.out.println("Cricket Game Finished!"); } @Override void initialize() { System.out.println("Cricket Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Cricket Game Started. Enjoy the game!"); } }

Football.java

public class Football extends Game { @Override void endPlay() { System.out.println("Football Game Finished!"); } @Override void initialize() { System.out.println("Football Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Football Game Started. Enjoy the game!"); } }

步驟 3

使用 Game 的模板方法 play() 來演示遊戲的定義方式。

TemplatePatternDemo.java

public class TemplatePatternDemo { public static void main(String[] args) { Game game = new Cricket(); game.play(); System.out.println(); game = new Football(); game.play(); } }

步驟 4

執行程序,輸出結果:

Cricket Game Initialized! Start playing.
Cricket Game Started. Enjoy the game!
Cricket Game Finished!

 
Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!

訪問者模式

在訪問者模式(Visitor Pattern)中,我們使用了一個訪問者類,它改變了元素類的執行算法。通過這種方式,元素的執行算法可以隨着訪問者改變而改變。這種類型的設計模式屬於行爲型模式。根據模式,元素對象已接受訪問者對象,這樣訪問者對象就可以處理元素對象上的操作。

介紹

意圖:主要將數據結構與數據操作分離。

主要解決:穩定的數據結構和易變的操作耦合問題。

何時使用:需要對一個對象結構中的對象進行很多不同的並且不相關的操作,而需要避免讓這些操作"污染"這些對象的類,使用訪問者模式將這些封裝到類中。

如何解決:在被訪問的類裏面加一個對外提供接待訪問者的接口。

關鍵代碼:在數據基礎類裏面有一個方法接受訪問者,將自身引用傳入訪問者。

應用實例:您在朋友家做客,您是訪問者,朋友接受您的訪問,您通過朋友的描述,然後對朋友的描述做出一個判斷,這就是訪問者模式。

優點: 1、符合單一職責原則。 2、優秀的擴展性。 3、靈活性。

缺點: 1、具體元素對訪問者公佈細節,違反了迪米特原則。 2、具體元素變更比較困難。 3、違反了依賴倒置原則,依賴了具體類,沒有依賴抽象。

使用場景: 1、對象結構中對象對應的類很少改變,但經常需要在此對象結構上定義新的操作。 2、需要對一個對象結構中的對象進行很多不同的並且不相關的操作,而需要避免讓這些操作"污染"這些對象的類,也不希望在增加新操作時修改這些類。

注意事項:訪問者可以對功能進行統一,可以做報表、UI、攔截器與過濾器。

實現

我們將創建一個定義接受操作的 ComputerPart 接口。KeyboardMouseMonitor  Computer 是實現了 ComputerPart 接口的實體類。我們將定義另一個接口 ComputerPartVisitor,它定義了訪問者類的操作。Computer 使用實體訪問者來執行相應的動作。

VisitorPatternDemo,我們的演示類使用 ComputerComputerPartVisitor 類來演示訪問者模式的用法。

 

步驟 1

定義一個表示元素的接口。

ComputerPart.java

public interface ComputerPart { public void accept(ComputerPartVisitor computerPartVisitor); }

步驟 2

創建擴展了上述類的實體類。

Keyboard.java

public class Keyboard implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } }

Monitor.java

public class Monitor implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } }

Mouse.java

public class Mouse implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } }

Computer.java

public class Computer implements ComputerPart { ComputerPart[] parts; public Computer(){ parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()}; } @Override public void accept(ComputerPartVisitor computerPartVisitor) { for (int i = 0; i < parts.length; i++) { parts[i].accept(computerPartVisitor); } computerPartVisitor.visit(this); } }

步驟 3

定義一個表示訪問者的接口。

ComputerPartVisitor.java

public interface ComputerPartVisitor { public void visit(Computer computer); public void visit(Mouse mouse); public void visit(Keyboard keyboard); public void visit(Monitor monitor); }

步驟 4

創建實現了上述類的實體訪問者。

ComputerPartDisplayVisitor.java

public class ComputerPartDisplayVisitor implements ComputerPartVisitor { @Override public void visit(Computer computer) { System.out.println("Displaying Computer."); } @Override public void visit(Mouse mouse) { System.out.println("Displaying Mouse."); } @Override public void visit(Keyboard keyboard) { System.out.println("Displaying Keyboard."); } @Override public void visit(Monitor monitor) { System.out.println("Displaying Monitor."); } }

步驟 5

使用 ComputerPartDisplayVisitor 來顯示 Computer 的組成部分。

VisitorPatternDemo.java

public class VisitorPatternDemo { public static void main(String[] args) { ComputerPart computer = new Computer(); computer.accept(new ComputerPartDisplayVisitor()); } }

步驟 6

執行程序,輸出結果:

Displaying Mouse.
Displaying Keyboard.
Displaying Monitor.
Displaying Computer.

MVC 模式

MVC 模式代表 Model-View-Controller(模型-視圖-控制器) 模式。這種模式用於應用程序的分層開發。

  • Model(模型) - 模型代表一個存取數據的對象或 JAVA POJO。它也可以帶有邏輯,在數據變化時更新控制器。
  • View(視圖) - 視圖代表模型包含的數據的可視化。
  • Controller(控制器) - 控制器作用於模型和視圖上。它控制數據流向模型對象,並在數據變化時更新視圖。它使視圖與模型分離開。

https://www.runoob.com/wp-content/uploads/2014/08/1200px-ModelViewControllerDiagram2.svg_.png

實現

我們將創建一個作爲模型的 Student 對象。StudentView 是一個把學生詳細信息輸出到控制檯的視圖類,StudentController 是負責存儲數據到 Student 對象中的控制器類,並相應地更新視圖 StudentView

MVCPatternDemo,我們的演示類使用 StudentController 來演示 MVC 模式的用法。

 

步驟 1

創建模型。

Student.java

public class Student { private String rollNo; private String name; public String getRollNo() { return rollNo; } public void setRollNo(String rollNo) { this.rollNo = rollNo; } public String getName() { return name; } public void setName(String name) { this.name = name; } }

步驟 2

創建視圖。

StudentView.java

public class StudentView { public void printStudentDetails(String studentName, String studentRollNo){ System.out.println("Student: "); System.out.println("Name: " + studentName); System.out.println("Roll No: " + studentRollNo); } }

步驟 3

創建控制器。

StudentController.java

public class StudentController { private Student model; private StudentView view; public StudentController(Student model, StudentView view){ this.model = model; this.view = view; } public void setStudentName(String name){ model.setName(name); } public String getStudentName(){ return model.getName(); } public void setStudentRollNo(String rollNo){ model.setRollNo(rollNo); } public String getStudentRollNo(){ return model.getRollNo(); } public void updateView(){ view.printStudentDetails(model.getName(), model.getRollNo()); } }

步驟 4

使用 StudentController 方法來演示 MVC 設計模式的用法。

MVCPatternDemo.java

public class MVCPatternDemo { public static void main(String[] args) { //從數據庫獲取學生記錄 Student model = retrieveStudentFromDatabase(); //創建一個視圖:把學生詳細信息輸出到控制檯 StudentView view = new StudentView(); StudentController controller = new StudentController(model, view); controller.updateView(); //更新模型數據 controller.setStudentName("John"); controller.updateView(); } private static Student retrieveStudentFromDatabase(){ Student student = new Student(); student.setName("Robert"); student.setRollNo("10"); return student; } }

步驟 5

執行程序,輸出結果:

Student: 
Name: Robert
Roll No: 10
Student: 
Name: John
Roll No: 10

業務代表模式

業務代表模式(Business Delegate Pattern)用於對錶示層和業務層解耦。它基本上是用來減少通信或對錶示層代碼中的業務層代碼的遠程查詢功能。在業務層中我們有以下實體。

  • 客戶端(Client - 表示層代碼可以是 JSPservlet UI java 代碼。
  • 業務代表(Business Delegate - 一個爲客戶端實體提供的入口類,它提供了對業務服務方法的訪問。
  • 查詢服務(LookUp Service - 查找服務對象負責獲取相關的業務實現,並提供業務對象對業務代表對象的訪問。
  • 業務服務(Business Service - 業務服務接口。實現了該業務服務的實體類,提供了實際的業務實現邏輯。

實現

我們將創建 ClientBusinessDelegateBusinessServiceLookUpServiceJMSService  EJBService 來表示業務代表模式中的各種實體。

BusinessDelegatePatternDemo,我們的演示類使用 BusinessDelegate  Client 來演示業務代表模式的用法。

 

步驟 1

創建 BusinessService 接口。

BusinessService.java

public interface BusinessService { public void doProcessing(); }

步驟 2

創建實體服務類。

EJBService.java

public class EJBService implements BusinessService { @Override public void doProcessing() { System.out.println("Processing task by invoking EJB Service"); } }

JMSService.java

public class JMSService implements BusinessService { @Override public void doProcessing() { System.out.println("Processing task by invoking JMS Service"); } }

步驟 3

創建業務查詢服務。

BusinessLookUp.java

public class BusinessLookUp { public BusinessService getBusinessService(String serviceType){ if(serviceType.equalsIgnoreCase("EJB")){ return new EJBService(); }else { return new JMSService(); } } }

步驟 4

創建業務代表。

BusinessDelegate.java

public class BusinessDelegate { private BusinessLookUp lookupService = new BusinessLookUp(); private BusinessService businessService; private String serviceType; public void setServiceType(String serviceType){ this.serviceType = serviceType; } public void doTask(){ businessService = lookupService.getBusinessService(serviceType); businessService.doProcessing(); } }

步驟 5

創建客戶端。

Client.java

public class Client { BusinessDelegate businessService; public Client(BusinessDelegate businessService){ this.businessService = businessService; } public void doTask(){ businessService.doTask(); } }

步驟 6

使用 BusinessDelegate Client 類來演示業務代表模式。

BusinessDelegatePatternDemo.java

public class BusinessDelegatePatternDemo { public static void main(String[] args) { BusinessDelegate businessDelegate = new BusinessDelegate(); businessDelegate.setServiceType("EJB"); Client client = new Client(businessDelegate); client.doTask(); businessDelegate.setServiceType("JMS"); client.doTask(); } }

步驟 7

執行程序,輸出結果:

Processing task by invoking EJB Service
Processing task by invoking JMS Service

組合實體模式

組合實體模式(Composite Entity Pattern)用在 EJB 持久化機制中。一個組合實體是一個 EJB 實體 bean,代表了對象的圖解。當更新一個組合實體時,內部依賴對象 beans 會自動更新,因爲它們是由 EJB 實體 bean 管理的。以下是組合實體 bean 的參與者。

  • 組合實體(Composite Entity - 它是主要的實體 bean。它可以是粗粒的,或者可以包含一個粗粒度對象,用於持續生命週期。
  • 粗粒度對象(Coarse-Grained Object - 該對象包含依賴對象。它有自己的生命週期,也能管理依賴對象的生命週期。
  • 依賴對象(Dependent Object - 依賴對象是一個持續生命週期依賴於粗粒度對象的對象。
  • 策略(Strategies - 策略表示如何實現組合實體。

實現

我們將創建作爲組合實體的 CompositeEntity 對象。CoarseGrainedObject 是一個包含依賴對象的類。

CompositeEntityPatternDemo,我們的演示類使用 Client 類來演示組合實體模式的用法。

 

步驟 1

創建依賴對象。

DependentObject1.java

public class DependentObject1 { private String data; public void setData(String data){ this.data = data; } public String getData(){ return data; } }

DependentObject2.java

public class DependentObject2 { private String data; public void setData(String data){ this.data = data; } public String getData(){ return data; } }

步驟 2

創建粗粒度對象。

CoarseGrainedObject.java

public class CoarseGrainedObject { DependentObject1 do1 = new DependentObject1(); DependentObject2 do2 = new DependentObject2(); public void setData(String data1, String data2){ do1.setData(data1); do2.setData(data2); } public String[] getData(){ return new String[] {do1.getData(),do2.getData()}; } }

步驟 3

創建組合實體。

CompositeEntity.java

public class CompositeEntity { private CoarseGrainedObject cgo = new CoarseGrainedObject(); public void setData(String data1, String data2){ cgo.setData(data1, data2); } public String[] getData(){ return cgo.getData(); } }

步驟 4

創建使用組合實體的客戶端類。

Client.java

public class Client { private CompositeEntity compositeEntity = new CompositeEntity(); public void printData(){ for (int i = 0; i < compositeEntity.getData().length; i++) { System.out.println("Data: " + compositeEntity.getData()[i]); } } public void setData(String data1, String data2){ compositeEntity.setData(data1, data2); } }

步驟 5

使用 Client 來演示組合實體設計模式的用法。

CompositeEntityPatternDemo.java

public class CompositeEntityPatternDemo { public static void main(String[] args) { Client client = new Client(); client.setData("Test", "Data"); client.printData(); client.setData("Second Test", "Data1"); client.printData(); } }

步驟 6

執行程序,輸出結果:

Data: Test
Data: Data
Data: Second Test
Data: Data1

數據訪問對象模式

數據訪問對象模式(Data Access Object Pattern)或 DAO 模式用於把低級的數據訪問 API 或操作從高級的業務服務中分離出來。以下是數據訪問對象模式的參與者。

  • 數據訪問對象接口(Data Access Object Interface - 該接口定義了在一個模型對象上要執行的標準操作。
  • 數據訪問對象實體類(Data Access Object concrete class - 該類實現了上述的接口。該類負責從數據源獲取數據,數據源可以是數據庫,也可以是 xml,或者是其他的存儲機制。
  • 模型對象/數值對象(Model Object/Value Object - 該對象是簡單的 POJO,包含了 get/set 方法來存儲通過使用 DAO 類檢索到的數據。

實現

我們將創建一個作爲模型對象或數值對象的 Student 對象。StudentDao 是數據訪問對象接口。StudentDaoImpl 是實現了數據訪問對象接口的實體類。DaoPatternDemo,我們的演示類使用 StudentDao 來演示數據訪問對象模式的用法。

 

步驟 1

創建數值對象。

Student.java

public class Student { private String name; private int rollNo; Student(String name, int rollNo){ this.name = name; this.rollNo = rollNo; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getRollNo() { return rollNo; } public void setRollNo(int rollNo) { this.rollNo = rollNo; } }

步驟 2

創建數據訪問對象接口。

StudentDao.java

import java.util.List; public interface StudentDao { public List<Student> getAllStudents(); public Student getStudent(int rollNo); public void updateStudent(Student student); public void deleteStudent(Student student); }

步驟 3

創建實現了上述接口的實體類。

StudentDaoImpl.java

import java.util.ArrayList; import java.util.List; public class StudentDaoImpl implements StudentDao { //列表是當作一個數據庫 List<Student> students; public StudentDaoImpl(){ students = new ArrayList<Student>(); Student student1 = new Student("Robert",0); Student student2 = new Student("John",1); students.add(student1); students.add(student2); } @Override public void deleteStudent(Student student) { students.remove(student.getRollNo()); System.out.println("Student: Roll No " + student.getRollNo() +", deleted from database"); } //從數據庫中檢索學生名單 @Override public List<Student> getAllStudents() { return students; } @Override public Student getStudent(int rollNo) { return students.get(rollNo); } @Override public void updateStudent(Student student) { students.get(student.getRollNo()).setName(student.getName()); System.out.println("Student: Roll No " + student.getRollNo() +", updated in the database"); } }

步驟 4

使用 StudentDao 來演示數據訪問對象模式的用法。

DaoPatternDemo.java

public class DaoPatternDemo { public static void main(String[] args) { StudentDao studentDao = new StudentDaoImpl(); //輸出所有的學生 for (Student student : studentDao.getAllStudents()) { System.out.println("Student: [RollNo : " +student.getRollNo()+", Name : "+student.getName()+" ]"); } //更新學生 Student student =studentDao.getAllStudents().get(0); student.setName("Michael"); studentDao.updateStudent(student); //獲取學生 studentDao.getStudent(0); System.out.println("Student: [RollNo : " +student.getRollNo()+", Name : "+student.getName()+" ]"); } }

步驟 5

執行程序,輸出結果:

Student: [RollNo : 0, Name : Robert ]
Student: [RollNo : 1, Name : John ]
Student: Roll No 0, updated in the database
Student: [RollNo : 0, Name : Michael ]

前端控制器模式

前端控制器模式(Front Controller Pattern)是用來提供一個集中的請求處理機制,所有的請求都將由一個單一的處理程序處理。該處理程序可以做認證/授權/記錄日誌,或者跟蹤請求,然後把請求傳給相應的處理程序。以下是這種設計模式的實體。

  • 前端控制器(Front Controller - 處理應用程序所有類型請求的單個處理程序,應用程序可以是基於 web 的應用程序,也可以是基於桌面的應用程序。
  • 調度器(Dispatcher - 前端控制器可能使用一個調度器對象來調度請求到相應的具體處理程序。
  • 視圖(View - 視圖是爲請求而創建的對象。

實現

我們將創建 FrontControllerDispatcher 分別當作前端控制器和調度器。HomeView  StudentView 表示各種爲前端控制器接收到的請求而創建的視圖。

FrontControllerPatternDemo,我們的演示類使用 FrontController 來演示前端控制器設計模式。

 

步驟 1

創建視圖。

HomeView.java

public class HomeView { public void show(){ System.out.println("Displaying Home Page"); } }

StudentView.java

public class StudentView { public void show(){ System.out.println("Displaying Student Page"); } }

步驟 2

創建調度器 Dispatcher

Dispatcher.java

public class Dispatcher { private StudentView studentView; private HomeView homeView; public Dispatcher(){ studentView = new StudentView(); homeView = new HomeView(); } public void dispatch(String request){ if(request.equalsIgnoreCase("STUDENT")){ studentView.show(); }else{ homeView.show(); } } }

步驟 3

創建前端控制器 FrontController

FrontController.java

public class FrontController { private Dispatcher dispatcher; public FrontController(){ dispatcher = new Dispatcher(); } private boolean isAuthenticUser(){ System.out.println("User is authenticated successfully."); return true; } private void trackRequest(String request){ System.out.println("Page requested: " + request); } public void dispatchRequest(String request){ //記錄每一個請求 trackRequest(request); //對用戶進行身份驗證 if(isAuthenticUser()){ dispatcher.dispatch(request); } } }

步驟 4

使用 FrontController 來演示前端控制器設計模式。

FrontControllerPatternDemo.java

public class FrontControllerPatternDemo { public static void main(String[] args) { FrontController frontController = new FrontController(); frontController.dispatchRequest("HOME"); frontController.dispatchRequest("STUDENT"); } }

步驟 5

執行程序,輸出結果:

Page requested: HOME
User is authenticated successfully.
Displaying Home Page
Page requested: STUDENT
User is authenticated successfully.
Displaying Student Page

攔截過濾器模式

攔截過濾器模式(Intercepting Filter Pattern)用於對應用程序的請求或響應做一些預處理/後處理。定義過濾器,並在把請求傳給實際目標應用程序之前應用在請求上。過濾器可以做認證/授權/記錄日誌,或者跟蹤請求,然後把請求傳給相應的處理程序。以下是這種設計模式的實體。

  • 過濾器(Filter - 過濾器在請求處理程序執行請求之前或之後,執行某些任務。
  • 過濾器鏈(Filter Chain - 過濾器鏈帶有多個過濾器,並在 Target 上按照定義的順序執行這些過濾器。
  • Target - Target 對象是請求處理程序。
  • 過濾管理器(Filter Manager - 過濾管理器管理過濾器和過濾器鏈。
  • 客戶端(Client - Client 是向 Target 對象發送請求的對象。

實現

我們將創建 FilterChainFilterManagerTargetClient 作爲表示實體的各種對象。AuthenticationFilter  DebugFilter 表示實體過濾器。

InterceptingFilterDemo,我們的演示類使用 Client 來演示攔截過濾器設計模式。

 

步驟 1

創建過濾器接口 Filter

Filter.java

public interface Filter { public void execute(String request); }

步驟 2

創建實體過濾器。

AuthenticationFilter.java

public class AuthenticationFilter implements Filter { public void execute(String request){ System.out.println("Authenticating request: " + request); } }

DebugFilter.java

public class DebugFilter implements Filter { public void execute(String request){ System.out.println("request log: " + request); } }

步驟 3

創建 Target

Target.java

public class Target { public void execute(String request){ System.out.println("Executing request: " + request); } }

步驟 4

創建過濾器鏈。

FilterChain.java

import java.util.ArrayList; import java.util.List; public class FilterChain { private List<Filter> filters = new ArrayList<Filter>(); private Target target; public void addFilter(Filter filter){ filters.add(filter); } public void execute(String request){ for (Filter filter : filters) { filter.execute(request); } target.execute(request); } public void setTarget(Target target){ this.target = target; } }

步驟 5

創建過濾管理器。

FilterManager.java

public class FilterManager { FilterChain filterChain; public FilterManager(Target target){ filterChain = new FilterChain(); filterChain.setTarget(target); } public void setFilter(Filter filter){ filterChain.addFilter(filter); } public void filterRequest(String request){ filterChain.execute(request); } }

步驟 6

創建客戶端 Client

Client.java

public class Client { FilterManager filterManager; public void setFilterManager(FilterManager filterManager){ this.filterManager = filterManager; } public void sendRequest(String request){ filterManager.filterRequest(request); } }

步驟 7

使用 Client 來演示攔截過濾器設計模式。

InterceptingFilterDemo.java

public class InterceptingFilterDemo { public static void main(String[] args) { FilterManager filterManager = new FilterManager(new Target()); filterManager.setFilter(new AuthenticationFilter()); filterManager.setFilter(new DebugFilter()); Client client = new Client(); client.setFilterManager(filterManager); client.sendRequest("HOME"); } }

步驟 8

執行程序,輸出結果:

Authenticating request: HOME
request log: HOME
Executing request: HOME

服務定位器模式

服務定位器模式(Service Locator Pattern)用在我們想使用 JNDI 查詢定位各種服務的時候。考慮到爲某個服務查找 JNDI 的代價很高,服務定位器模式充分利用了緩存技術。在首次請求某個服務時,服務定位器在 JNDI 中查找服務,並緩存該服務對象。當再次請求相同的服務時,服務定位器會在它的緩存中查找,這樣可以在很大程度上提高應用程序的性能。以下是這種設計模式的實體。

  • 服務(Service - 實際處理請求的服務。對這種服務的引用可以在 JNDI 服務器中查找到。
  • Context / 初始的 Context - JNDI Context 帶有對要查找的服務的引用。
  • 服務定位器(Service Locator - 服務定位器是通過 JNDI 查找和緩存服務來獲取服務的單點接觸。
  • 緩存(Cache - 緩存存儲服務的引用,以便複用它們。
  • 客戶端(Client - Client 是通過 ServiceLocator 調用服務的對象。

實現

我們將創建 ServiceLocatorInitialContextCacheService 作爲表示實體的各種對象。Service1  Service2 表示實體服務。

ServiceLocatorPatternDemo,我們的演示類在這裏是作爲一個客戶端,將使用 ServiceLocator 來演示服務定位器設計模式。

 

步驟 1

創建服務接口 Service

Service.java

public interface Service { public String getName(); public void execute(); }

步驟 2

創建實體服務。

Service1.java

public class Service1 implements Service { public void execute(){ System.out.println("Executing Service1"); } @Override public String getName() { return "Service1"; } }

Service2.java

public class Service2 implements Service { public void execute(){ System.out.println("Executing Service2"); } @Override public String getName() { return "Service2"; } }

步驟 3

JNDI 查詢創建 InitialContext

InitialContext.java

public class InitialContext { public Object lookup(String jndiName){ if(jndiName.equalsIgnoreCase("SERVICE1")){ System.out.println("Looking up and creating a new Service1 object"); return new Service1(); }else if (jndiName.equalsIgnoreCase("SERVICE2")){ System.out.println("Looking up and creating a new Service2 object"); return new Service2(); } return null; } }

步驟 4

創建緩存 Cache

Cache.java

import java.util.ArrayList; import java.util.List; public class Cache { private List<Service> services; public Cache(){ services = new ArrayList<Service>(); } public Service getService(String serviceName){ for (Service service : services) { if(service.getName().equalsIgnoreCase(serviceName)){ System.out.println("Returning cached "+serviceName+" object"); return service; } } return null; } public void addService(Service newService){ boolean exists = false; for (Service service : services) { if(service.getName().equalsIgnoreCase(newService.getName())){ exists = true; } } if(!exists){ services.add(newService); } } }

步驟 5

創建服務定位器。

ServiceLocator.java

public class ServiceLocator { private static Cache cache; static { cache = new Cache(); } public static Service getService(String jndiName){ Service service = cache.getService(jndiName); if(service != null){ return service; } InitialContext context = new InitialContext(); Service service1 = (Service)context.lookup(jndiName); cache.addService(service1); return service1; } }

步驟 6

使用 ServiceLocator 來演示服務定位器設計模式。

ServiceLocatorPatternDemo.java

public class ServiceLocatorPatternDemo { public static void main(String[] args) { Service service = ServiceLocator.getService("Service1"); service.execute(); service = ServiceLocator.getService("Service2"); service.execute(); service = ServiceLocator.getService("Service1"); service.execute(); service = ServiceLocator.getService("Service2"); service.execute(); } }

步驟 7

執行程序,輸出結果:

Looking up and creating a new Service1 object
Executing Service1
Looking up and creating a new Service2 object
Executing Service2
Returning cached  Service1 object
Executing Service1
Returning cached  Service2 object
Executing Service2

傳輸對象模式

傳輸對象模式(Transfer Object Pattern)用於從客戶端向服務器一次性傳遞帶有多個屬性的數據。傳輸對象也被稱爲數值對象。傳輸對象是一個具有 getter/setter 方法的簡單的 POJO 類,它是可序列化的,所以它可以通過網絡傳輸。它沒有任何的行爲。服務器端的業務類通常從數據庫讀取數據,然後填充 POJO,並把它發送到客戶端或按值傳遞它。對於客戶端,傳輸對象是隻讀的。客戶端可以創建自己的傳輸對象,並把它傳遞給服務器,以便一次性更新數據庫中的數值。以下是這種設計模式的實體。

  • 業務對象(Business Object - 爲傳輸對象填充數據的業務服務。
  • 傳輸對象(Transfer Object - 簡單的 POJO,只有設置/獲取屬性的方法。
  • 客戶端(Client - 客戶端可以發送請求或者發送傳輸對象到業務對象。

實現

我們將創建一個作爲業務對象的 StudentBO 和作爲傳輸對象的 StudentVO,它們都代表了我們的實體。

TransferObjectPatternDemo,我們的演示類在這裏是作爲一個客戶端,將使用 StudentBO  Student 來演示傳輸對象設計模式。

 

步驟 1

創建傳輸對象。

StudentVO.java

public class StudentVO { private String name; private int rollNo; StudentVO(String name, int rollNo){ this.name = name; this.rollNo = rollNo; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getRollNo() { return rollNo; } public void setRollNo(int rollNo) { this.rollNo = rollNo; } }

步驟 2

創建業務對象。

StudentBO.java

import java.util.ArrayList; import java.util.List; public class StudentBO { //列表是當作一個數據庫 List<StudentVO> students; public StudentBO(){ students = new ArrayList<StudentVO>(); StudentVO student1 = new StudentVO("Robert",0); StudentVO student2 = new StudentVO("John",1); students.add(student1); students.add(student2); } public void deleteStudent(StudentVO student) { students.remove(student.getRollNo()); System.out.println("Student: Roll No " + student.getRollNo() +", deleted from database"); } //從數據庫中檢索學生名單 public List<StudentVO> getAllStudents() { return students; } public StudentVO getStudent(int rollNo) { return students.get(rollNo); } public void updateStudent(StudentVO student) { students.get(student.getRollNo()).setName(student.getName()); System.out.println("Student: Roll No " + student.getRollNo() +", updated in the database"); } }

步驟 3

使用 StudentBO 來演示傳輸對象設計模式。

TransferObjectPatternDemo.java

public class TransferObjectPatternDemo { public static void main(String[] args) { StudentBO studentBusinessObject = new StudentBO(); //輸出所有的學生 for (StudentVO student : studentBusinessObject.getAllStudents()) { System.out.println("Student: [RollNo : " +student.getRollNo()+", Name : "+student.getName()+" ]"); } //更新學生 StudentVO student =studentBusinessObject.getAllStudents().get(0); student.setName("Michael"); studentBusinessObject.updateStudent(student); //獲取學生 studentBusinessObject.getStudent(0); System.out.println("Student: [RollNo : " +student.getRollNo()+", Name : "+student.getName()+" ]"); } }

步驟 4

執行程序,輸出結果:

Student: [RollNo : 0, Name : Robert ]
Student: [RollNo : 1, Name : John ]
Student: Roll No 0, updated in the database
Student: [RollNo : 0, Name : Michael ]

設計模式資源

本章列出了設計模式相關的網站、書籍和文章。

設計模式相關的網站

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章