java學習 結構型設計模式
總體來說設計模式分為三大類:建立型模式、結構型模式和行為型模式。
博主的上一篇文章已經提到過建立型模式,此外該文章還有設計模式概況和設計模式的六大原則。設計模式的六大原則是設計模式的核心思想,詳情請看博主的另外一篇文章: Java經典設計模式之五大建立模式(附例項和詳解)。
接下來我們看看結構型模式,共七種:介面卡模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。其中介面卡模式主要分為三類:類的介面卡模式、物件的介面卡模式、介面的介面卡模式。其中的物件的介面卡模式是各種結構型模式的起源。
一、介面卡模式
介面卡模式主要分為三類:類的介面卡模式、物件的介面卡模式、介面的介面卡模式。
介面卡模式將某個類的介面轉換成客戶端期望的另一個介面表示,目的是消除由於介面不匹配所造成的類的相容性問題。有點抽象,我們來看看詳細的內容。
1.1、類的介面卡模式
類的介面卡模式核心思想就是:有一個Source類,擁有一個方法,待適配,目標介面是Targetable,通過Adapter類,將Source的功能擴充套件到Targetable裡。
package com.model.structure;
public class Source {
public void method1() {
System.out.println("this is original method!" );
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
package com.model.structure;
public interface Targetable {
/* 與原類中的方法相同 */
public void method1();
/* 新類的方法 */
public void method2();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
package com.model.structure;
public class Adapter extends Source implements Targetable {
public void method2() {
System.out.println("this is the targetable method!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
package com.model.structure;
public class AdapterTest {
public static void main(String[] args) {
Targetable target = new Adapter();
target.method1();
target.method2();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
AdapterTest的執行結果:
1.2、物件的介面卡模式
物件的介面卡模式的基本思路和類的介面卡模式相同,只是將Adapter類作修改成Wrapper,這次不繼承Source類,而是持有Source類的例項,以達到解決相容性的問題。
package com.model.structure;
public class Wrapper implements Targetable {
private Source source;
public Wrapper(Source source) {
super();
this.source = source;
}
@Override
public void method2() {
System.out.println("this is the targetable method!");
}
@Override
public void method1() {
source.method1();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
package com.model.structure;
public class AdapterTest {
public static void main(String[] args) {
Source source = new Source();
Targetable target = new Wrapper(source);
target.method1();
target.method2();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
執行結果跟類的介面卡模式例子的一樣。
1.3、介面的介面卡模式
介面的介面卡是這樣的:有時我們寫的一個介面中有多個抽象方法,當我們寫該介面的實現類時,必須實現該介面的所有方法,這明顯有時比較浪費,因為並不是所有的方法都是我們需要的,有時只需要某一些,此處為了解決這個問題,我們引入了介面的介面卡模式,藉助於一個抽象類,該抽象類實現了該介面,實現了所有的方法,而我們不和原始的介面打交道,只和該抽象類取得聯絡,所以我們寫一個類,繼承該抽象類,重寫我們需要的方法就行了。
這裡看文字描述已經試夠清楚的了,因此就不貼程式碼例項了。
二、裝飾模式
裝飾模式:在不必改變原類檔案和使用繼承的情況下,動態地擴充套件一個物件的功能。它是通過建立一個包裝物件,也就是裝飾來包裹真實的物件。
裝飾模式的特點:
(1) 裝飾物件和真實物件有相同的介面。這樣客戶端物件就能以和真實物件相同的方式和裝飾物件互動。
(2) 裝飾物件包含一個真實物件的引用(reference)
(3) 裝飾物件接受所有來自客戶端的請求。它把這些請求轉發給真實的物件。
(4) 裝飾物件可以在轉發這些請求以前或以後增加一些附加功能。這樣就確保了在執行時,不用修改給定物件的結構就可以在外部增加附加的功能。在面向物件的設計中,通常是通過繼承來實現對給定類的功能擴充套件。繼承不能做到這一點,繼承的功能是靜態的,不能動態增刪。
具體看看程式碼例項
package com.model.structure;
public interface Sourceable {
public void method();
}
- 1
- 2
- 3
- 4
- 5
- 6
package com.model.structure;
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
package com.model.structure;
public class Decorator implements Sourceable {
private Sourceable source;
public Decorator(Sourceable source) {
super();
this.source = source;
}
@Override
public void method() {
System.out.println("before decorator!");
source.method();
System.out.println("after decorator!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
package com.model.structure;
public class DecoratorTest {
public static void main(String[] args) {
//(1) 裝飾物件和真實物件有相同的介面。這樣客戶端物件就能以和真實物件相同的方式和裝飾物件互動。
//(2) 裝飾物件包含一個真實物件的引用(reference)
//(3) 裝飾物件接受所有來自客戶端的請求。它把這些請求轉發給真實的物件。
//(4) 裝飾物件可以在轉發這些請求以前或以後增加一些附加功能。這樣就確保了在執行時,不用修改給定物件的結構就可以在外部增加附加的功能。
// 在面向物件的設計中,通常是通過繼承來實現對給定類的功能擴充套件。
// 繼承不能做到這一點,繼承的功能是靜態的,不能動態增刪。
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.method();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
執行結果:
before decorator!
the original method!
after decorator!
- 1
- 2
- 3
三、代理模式
代理模式就是多一個代理類出來,替原物件進行一些操作。代理類就像中介,它比我們掌握著更多的資訊。
具體看看程式碼例項。
package com.model.structure;
public interface Sourceable {
public void method();
}
- 1
- 2
- 3
- 4
- 5
- 6
package com.model.structure;
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
package com.model.structure;
public class Proxy implements Sourceable {
private Source source;
public Proxy() {
super();
this.source = new Source();
}
@Override
public void method() {
before();
source.method();
atfer();
}
private void atfer() {
System.out.println("after proxy!");
}
private void before() {
System.out.println("before proxy!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
package com.model.structure;
public class ProxyTest {
public static void main(String[] args) {
Sourceable source = new Proxy();
source.method();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
執行結果:
before proxy!
the original method!
after proxy!
- 1
- 2
- 3
- 4
四、外觀模式
外觀模式是為了解決類與類之間的依賴關係的,像spring一樣,可以將類和類之間的關係配置到配置檔案中,而外觀模式就是將他們的關係放在一個Facade類中,降低了類類之間的耦合度,該模式中沒有涉及到介面。
我們以一個計算機的啟動過程為例,看看如下的程式碼:
package com.model.structure;
public class CPU {
public void startup() {
System.out.println("cpu startup!");
}
public void shutdown() {
System.out.println("cpu shutdown!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
package com.model.structure;
public class Disk {
public void startup() {
System.out.println("disk startup!");
}
public void shutdown() {
System.out.println("disk shutdown!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
package com.model.structure;
public class Memory {
public void startup() {
System.out.println("memory startup!");
}
public void shutdown() {
System.out.println("memory shutdown!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
package com.model.structure;
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!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
package com.model.structure;
public class User {
public static void main(String[] args) {
Computer computer = new Computer();
computer.startup();
computer.shutdown();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
執行結果:
start the computer!
cpu startup!
memory startup!
disk startup!
start computer finished!
begin to close the computer!
cpu shutdown!
memory shutdown!
disk shutdown!
computer closed!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
五、橋接模式
在軟體系統中,某些型別由於自身的邏輯,它具有兩個或多個維度的變化,那麼如何應對這種“多維度的變化”?如何利用面嚮物件的技術來使得該型別能夠輕鬆的沿著多個方向進行變化,而又不引入額外的複雜度?這就要使用Bridge模式。
在提出橋樑模式的時候指出,橋樑模式的用意是”將抽象化(Abstraction)與實現化(Implementation)脫耦,使得二者可以獨立地變化”。這句話有三個關鍵詞,也就是抽象化、實現化和脫耦。
抽象化:存在於多個實體中的共同的概念性聯絡,就是抽象化。作為一個過程,抽象化就是忽略一些資訊,從而把不同的實體當做同樣的實體對待。
實現化:抽象化給出的具體實現,就是實現化。
脫耦:所謂耦合,就是兩個實體的行為的某種強關聯。而將它們的強關聯去掉,就是耦合的解脫,或稱脫耦。在這裡,脫耦是指將抽象化和實現化之間的耦合解脫開,或者說是將它們之間的強關聯改換成弱關聯。
下面我們來看看程式碼例項:
package com.model.structure;
public interface Driver {
public void connect();
}
- 1
- 2
- 3
- 4
- 5
- 6
package com.model.structure;
public class MysqlDriver implements Driver {
@Override
public void connect() {
System.out.println("connect mysql done!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
package com.model.structure;
public class DB2Driver implements Driver {
@Override
public void connect() {
System.out.println("connect db2 done!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
package com.model.structure;
public abstract class DriverManager {
private Driver driver;
public void connect() {
driver.connect();
}
public Driver getDriver() {
return driver;
}
public void setDriver(Driver driver) {
this.driver = driver;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
package com.model.structure;
public class MyDriverManager extends DriverManager {
public void connect() {
super.connect();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
package com.model.structure;
public class Client {
public static void main(String[] args) {
DriverManager driverManager = new MyDriverManager();
Driver driver1 = new MysqlDriver();
driverManager.setDriver(driver1);
driverManager.connect();
Driver driver2 = new DB2Driver();
driverManager.setDriver(driver2);
driverManager.connect();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
執行結果:
connect mysql done!
connect db2 done!
- 1
- 2
如果看完程式碼例項還不是很理解,我們想想如下兩個維度擴充套件:(1)假設我想加一個OracleDriver,這是一個維度,很好理解,不多解釋。(2)假設我們想在連線前後固定輸出點什麼,我們只需要加一個MyDriverManager2,程式碼如下:
package com.model.structure;
public class MyDriverManager2 extends DriverManager {
public void connect() {
System.out.println("before connect");
super.connect();
System.out.println("after connect");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
再將Client程式碼中的MyDriverManager 改成 MyDriverManager2 ,執行結果如下:
before connect
connect mysql done!
after connect
before connect
connect db2 done!
after connect
- 1
- 2
- 3
- 4
- 5
- 6
六、組合模式
組合模式,將物件組合成樹形結構以表示“部分-整體”的層次結構,組合模式使得使用者對單個物件和組合物件的使用具有一致性。掌握組合模式的重點是要理解清楚 “部分/整體” 還有 ”單個物件“ 與 “組合物件” 的含義。
組合模式讓你可以優化處理遞迴或分級資料結構。
《設計模式》:將物件組合成樹形結構以表示“部分整體”的層次結構。組合模式使得使用者對單個物件和組合物件的使用具有一致性。
涉及角色:
Component:是組合中的物件宣告介面,在適當的情況下,實現所有類共有介面的預設行為。宣告一個介面用於訪問和管理Component子部件。
Leaf:在組合中表示葉子結點物件,葉子結點沒有子結點。
Composite:定義有枝節點行為,用來儲存子部件,在Component介面中實現與子部件有關操作,如增加(add)和刪除(remove)等。
比如現實中公司內各部門的層級關係,請看程式碼:
Component:是組合中的物件宣告介面,在適當的情況下,實現所有類共有介面的預設行為。宣告一個介面用於訪問和管理Component子部件。
package com.model.structure;
public abstract class Company {
private String name;
public Company() {
}
public Company(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
protected abstract void add(Company company);
protected abstract void romove(Company company);
protected abstract void display(int depth);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
Composite:定義有枝節點行為,用來儲存子部件,在Component介面中實現與子部件有關操作,如增加(add)和刪除(remove)等。
package com.model.structure;
import java.util.ArrayList;
import java.util.List;
public class ConcreteCompany extends Company {
private List<Company> cList;
public ConcreteCompany() {
cList = new ArrayList();
}
public ConcreteCompany(String name) {
super(name);
cList = new ArrayList();
}
@Override
protected void add(Company company) {
cList.add(company);
}
@Override
protected void display(int depth) {
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < depth; i++) {
sb.append("-");
}
System.out.println(new String(sb) + this.getName());
for (Company c : cList) {
c.display(depth + 2);
}
}
@Override
protected void romove(Company company) {
cList.remove(company);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
Leaf:在組合中表示葉子結點物件,葉子結點沒有子結點。
package com.model.structure;
public class HRDepartment extends Company {
public HRDepartment(String name) {
super(name);
}
@Override
protected void add(Company company) {
}
@Override
protected void display(int depth) {
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < depth; i++) {
sb.append("-");
}
System.out.println(new String(sb) + this.getName());
}
@Override
protected void romove(Company company) {
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
package com.model.structure;
public class FinanceDepartment extends Company {
public FinanceDepartment(String name) {
super(name);
}
@Override
protected void add(Company company) {
}
@Override
protected void display(int depth) {
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < depth; i++) {
sb.append("-");
}
System.out.println(new String(sb) + this.getName());
}
@Override
protected void romove(Company company) {
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
Client:
package com.model.structure;
public class Client {
public static void main(String[] args) {
Company root = new ConcreteCompany();
root.setName("北京總公司");
root.add(new HRDepartment("總公司人力資源部"));
root.add(new FinanceDepartment("總公司財務部"));
Company shandongCom = new ConcreteCompany("山東分公司");
shandongCom.add(new HRDepartment("山東分公司人力資源部"));
shandongCom.add(new FinanceDepartment("山東分公司賬務部"));
Company zaozhuangCom = new ConcreteCompany("棗莊辦事處");
zaozhuangCom.add(new FinanceDepartment("棗莊辦事處財務部"));
zaozhuangCom.add(new HRDepartment("棗莊辦事處人力資源部"));
Company jinanCom = new ConcreteCompany("濟南辦事處");
jinanCom.add(new FinanceDepartment("濟南辦事處財務部"));
jinanCom.add(new HRDepartment("濟南辦事處人力資源部"));
shandongCom.add(jinanCom);
shandongCom.add(zaozhuangCom);
Company huadongCom = new ConcreteCompany("上海華東分公司");
huadongCom.add(new HRDepartment("上海華東分公司人力資源部"));
huadongCom.add(new FinanceDepartment("上海華東分公司賬務部"));
Company hangzhouCom = new ConcreteCompany("杭州辦事處");
hangzhouCom.add(new FinanceDepartment("杭州辦事處財務部"));
hangzhouCom.add(new HRDepartment("杭州辦事處人力資源部"));
Company nanjingCom = new ConcreteCompany("南京辦事處");
nanjingCom.add(new FinanceDepartment("南京辦事處財務部"));
nanjingCom.add(new HRDepartment("南京辦事處人力資源部"));
huadongCom.add(hangzhouCom);
huadongCom.add(nanjingCom);
root.add(shandongCom);
root.add(huadongCom);
root.display(0);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
執行結果:
北京總公司
--總公司人力資源部
--總公司財務部
--山東分公司
----山東分公司人力資源部
----山東分公司賬務部
----濟南辦事處
------濟南辦事處財務部
------濟南辦事處人力資源部
----棗莊辦事處
------棗莊辦事處財務部
------棗莊辦事處人力資源部
--上海華東分公司
----上海華東分公司人力資源部
----上海華東分公司賬務部
----杭州辦事處
------杭州辦事處財務部
------杭州辦事處人力資源部
----南京辦事處
------南京辦事處財務部
------南京辦事處人力資源部
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
七、享元模式
享元模式的主要目的是實現物件的共享,即共享池,當系統中物件多的時候可以減少記憶體的開銷,通常與工廠模式一起使用。
一提到共享池,我們很容易聯想到Java裡面的JDBC連線池,想想每個連線的特點,我們不難總結出:適用於作共享的一些個物件,他們有一些共有的屬性,就拿資料庫連線池來說,url、driverClassName、username、password及dbname,這些屬性對於每個連線來說都是一樣的,所以就適合用享元模式來處理,建一個工廠類,將上述類似屬性作為內部資料,其它的作為外部資料,在方法呼叫時,當做引數傳進來,這樣就節省了空間,減少了例項的數量。
看下資料庫連線池的程式碼:
package com.model.structure;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;
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;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
通過連線池的管理,實現了資料庫連線的共享,不需要每一次都重新建立連線,節省了資料庫重新建立的開銷,提升了系統的效能!
注,本文參考了另外一位博主的文章,某些地方有結合自己的一些理解加以修改:
http://blog.csdn.net/zhangerqing/article/details/8194653