整體分爲3大類:
建立型模式 (5種):工廠方法、抽象工廠、單例、建造者、原型
結構型模式(7種):適配器、裝飾器、代理、外觀、橋接、組合、享元
行爲型模式(11種):策略、模板方法、觀察者、迭代子、責任鏈、命令、備忘錄、狀態、訪問者、中介者、解釋器
其它(2種):併發型、線程池
node
類的適配器
有一個待適配的Source類擁有一個方法,經過Adapter類將Source的功能擴展到Targetable接口裏。
mysql
public class Source { public void method1(){ System.out.println("this is original method!"); } }
public interface Targetable { /* 與原類中的方法相同 */ void method1(); /* 新類的方法 */ void method2(); }
public class Adapter extends Source implements Targetable { @Override public void method2() { System.out.println("this is the targetable method!"); } }
測試類spring
public class AdapterTest { public static void main(String[] args) { Targetable target = new Adapter(); target.method1(); target.method2(); } }
對象的適配器
Adapter 類持有 Source 類的實例,以達到解決兼容性的問題。sql
public class Wrapper implements Targetable { private Source source; public Wrapper(Source source) { this.source = source; } @Override public void method1() { source.method1(); } @Override public void method2() { System.out.println("this is the targetable method!"); } }
測試類:數據庫
public class AdapterTest { public static void main(String[] args) { Source source = new Source(); Targetable target = new Wrapper(source); target.method1(); target.method2(); } }
接口的適配器segmentfault
藉助一個抽象類實現接口全部的方法,寫一個類繼承該抽象類並重寫咱們須要的方法就行 :
併發
接口app
public interface Sourceable { void method1(); void method2(); }
抽象類ide
public abstract class Wrapper2 implements Sourceable { @Override public void method1() {} @Override public void method2() {} }
public class SourceSub1 extends Wrapper2 { @Override public void method1() { System.out.println("the sourceable interface's first Sub1!"); } }
public class SourceSub2 extends Wrapper2 { @Override public void method2() { System.out.println("the sourceable interface's second Sub2!"); } }
測試類:性能
public class WrapperTest { public static void main(String[] args) { Sourceable source1 = new SourceSub1(); Sourceable source2 = new SourceSub2(); source1.method1(); source1.method2(); source2.method1(); source2.method2(); } }
類的適配器:當但願將一個類轉換成知足另外一個新接口的類時,可使用類的適配器模式,建立一個新類,繼承原有的類,實現新的接口便可。
對象的適配器模式:當但願將一個對象轉換成知足另外一個新接口的對象時,能夠建立一個Wrapper 類,持有原類的一個實例,在 Wrapper 類的方法中,調用實例的方法就行。
接口的適配器模式:當不但願實現一個接口中全部的方法時,能夠建立一個抽象類 Wrapper,實現全部方法,咱們寫別的類的時候,繼承抽象類便可。
動態的給一個對象增長一些新的功能,要求裝飾對象和被裝飾對象實現同一個接口,裝飾對象持有被裝飾對象的實例,關係圖以下:
Source 類是被裝飾類,Decorator 類是一個裝飾類,能夠爲 Source 類動態的添加一些功能,
代碼以下 :
public interface Sourceable { void method(); }
public class Source implements Sourceable { @Override public void method() { System.out.println("the orignal method!"); } }
public class Decorator implements Sourceable { private Sourceable source; public Decorator(Sourceable source) { this.source = source; } @Override public void method() { System.out.println("before decorator!"); source.method(); System.out.println("after decorator!"); } }
測試類:
public class DecoratorTest { public static void main(String[] args) { Sourceable source = new Source(); Sourceable obj = new Decorator(source); obj.method(); } }
應用場景:
一、須要擴展一個類的功能。
二、動態的爲一個對象增長功能,並且還能動態撤銷。(繼承不能作到這一點,繼承的功能是靜態的,不能動態增刪。)
缺點:產生過多類似的對象,不易排錯!
代理類替原對象進行一些操做,好比咱們在租房子的時候回去找中介,爲何呢?由於你對該地區房屋的信息掌握的不夠全面,但願找一個更熟悉的人去幫你作,此處的代理就是這個意思。
public interface Sourceable { void method(); }
public class Source implements Sourceable { @Override public void method() { System.out.println("thi original method!"); } }
public class Proxy implements Sourceable { private Source source; public Proxy() { super(); this.source = new Source(); } @Override public void method() { before(); source.method(); after(); } private void after() { System.out.println("after proxy!"); } private void before() { System.out.println("before proxy!"); } }
測試類:
public class ProxyTest { public static void main(String[] args) { Sourceable source = new Proxy(); source.method(); } }
應用場景:
若是要對原有的方法進行改進,此時有兩種辦法:
一、修改原有的方法來適應。這樣違反了「對擴展開放,對修改關閉」的原則。
二、採用一個代理類調用原有的方法,且對產生的結果進行控制。這種方法就是代理模式。
使用代理模式,能夠將功能劃分的更加清晰,有助於後期維護!
爲了解決類與類之間的依賴關係,像spring同樣能夠將類和類之間的關係配置到配置文件中,而外觀模式就是將他們的關係放在一個Facade類中,下降了類之間的耦合度:(咱們以一個計算機的啓動過程爲例)
public class CPU { public void startup(){ System.out.println("cpu startup!"); } public void shutdown(){ System.out.println("cpu shutdown!"); } }
public class Memory { public void startup(){ System.out.println("memory startup!"); } public void shutdown(){ System.out.println("memory shutdown!"); } }
public class Disk { public void startup(){ System.out.println("disk startup!"); } public void shutdown(){ System.out.println("disk shutdown!"); } }
public class Computer { private CPU cpu; private Memory memory; private Disk disk; public Computer() { cpu = new CPU(); memory = new Memory(); disk = new Disk(); } public void startup(){ System.out.println("start the computer!"); cpu.startup(); memory.startup(); disk.startup(); System.out.println("start computer finished!"); } public void shutdown(){ System.out.println("begin to close the computer!"); cpu.shutdown(); memory.shutdown(); disk.shutdown(); System.out.println("computer closed!"); } }
public class User { public static void main(String[] args) { Computer computer = new Computer(); computer.startup(); computer.shutdown(); } }
把事物和其具體實現分開,使他們能夠各自獨立的變化。橋接的用意是: 將抽象化與實現化解耦,使得兩者能夠獨立變化,像咱們經常使用的 JDBC 橋 DriverManager 同樣,JDBC進行鏈接數據庫的時候,在各個數據庫之間進行切換,基本不須要動太多的代碼,甚至絲絕不用動,緣由就是 JDBC 提供統一接口,每一個數據庫提供各自的實現,用一個叫作數據庫驅動的程序來橋接就好了 :
先定義接口:
public interface Sourceable { void method(); }
定義兩個實現類:
public class SourceSub1 implements Sourceable { @Override public void method() { System.out.println("this is the first sub!"); } }
public class SourceSub2 implements Sourceable { @Override public void method() { System.out.println("this is the second sub!"); } }
定義一個橋,持有 Sourceable 的一個實例:
public abstract class Bridge { private Sourceable source; public void method(){ source.method(); } public Sourceable getSource() { return source; } public void setSource(Sourceable source) { this.source = source; } }
public class MyBridge extends Bridge { @Override public void method() { getSource().method(); } }
測試類:
public class BridgeTest { public static void main(String[] args) { Bridge bridge = new MyBridge(); /*調用第一個對象*/ Sourceable source1 = new SourceSub1(); bridge.setSource(source1); bridge.method(); /*調用第二個對象*/ Sourceable source2 = new SourceSub2(); bridge.setSource(source2); bridge.method(); } }
這樣,就經過對 Bridge 類的調用,實現了對接口 Sourceable 的實現類 SourceSub1 和SourceSub2 的調用。接下來我再畫個圖,你們就應該明白了,由於這個圖是咱們 JDBC 鏈接的原理,有數據庫學習基礎的,一結合就都懂了。
在處理相似樹形結構的問題時比較方便:
public class TreeNode { private String name; private TreeNode parent; private Vector<TreeNode> children = new Vector<TreeNode>(); public TreeNode(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public TreeNode getParent() { return parent; } public void setParent(TreeNode parent) { this.parent = parent; } //添加孩子節點 public void add(TreeNode node){ children.add(node); } //刪除孩子節點 public void remove(TreeNode node){ children.remove(node); } //取得孩子節點 public Enumeration<TreeNode> getChildren(){ return children.elements(); } }
public class Tree { TreeNode root = null; public Tree(String name){ root = new TreeNode(name); } public static void main(String[] args) { Tree tree = new Tree("A"); TreeNode nodeB = new TreeNode("B"); TreeNode nodeC = new TreeNode("C"); nodeB.add(nodeC); tree.root.add(nodeB); System.out.println("build the tree finished!"); } }
使用場景:將多個對象組合在一塊兒進行操做,經常使用於表示樹形結構中,例如二叉樹,數等。
實現對象的共享,即共享池,當系統中對象多的時候能夠減小內存的開銷,一般與工廠模式一塊兒使用。
FlyWeightFactory 負責建立和管理享元單元,當一個客戶端請求時,工廠須要檢查當前對象池中是否有符合條件的對象,若是有,就返回已經存在的對象,若是沒有,則建立一個新對象,FlyWeight 是超類。一提到共享池,咱們很容易聯想到 Java 裏面的 JDBC 鏈接池,想一想每一個鏈接的特色,咱們不難總結出:適用於做共享的一些個對象,他們有一些共有的屬性,就拿數據庫鏈接池來講,url、driverClassName、username、password 及 dbname,這些屬性對於每一個鏈接來講都是同樣的,因此就適合用享元模式來處理,建一個工廠類,將上述相似屬性做爲內部數據,其它的做爲外部數據,在方法調用時,當作參數傳進來,這樣就節省了空間,減小了實例的數量。
看個例子:
看下數據庫鏈接池的代碼:
public class ConnectionPool { private Vector<Connection> pool; /*公有屬性*/ private String url = "jdbc:mysql://localhost:3306/test"; private String username = "root"; private String password = "root"; private String driverClassName = "com.mysql.jdbc.Driver"; private int poolSize = 100; private static ConnectionPool instance = null; Connection conn = null; /*構造方法,作一些初始化工做*/ private ConnectionPool() { pool = new Vector<Connection>(poolSize); for (int i = 0; i < poolSize; i++) { try { Class.forName(driverClassName); conn = DriverManager.getConnection(url, username, password); pool.add(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } /* 返回鏈接到鏈接池 */ public synchronized void release() { pool.add(conn); } /* 返回鏈接池中的一個數據庫鏈接 */ public synchronized Connection getConnection() { if (pool.size() > 0) { Connection conn = pool.get(0); pool.remove(conn); return conn; } else { return null; } } }
經過鏈接池的管理,實現了數據庫鏈接的共享,不須要每一次都從新建立鏈接,節省了數據庫從新建立的開銷,提高了系統的性能!