Java經典設計模式(2):七大結構型模式

整體來講設計模式分爲三大類:建立型模式、結構型模式和行爲型模式。java

博主的上一篇文章已經提到過建立型模式,此外該文章還有設計模式概況和設計模式的六大原則。設計模式的六大原則是設計模式的核心思想,詳情請看博主的另一篇文章: Java經典設計模式之五大建立模式(附實例和詳解)mysql

接下來咱們看看結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。其中適配器模式主要分爲三類:類的適配器模式、對象的適配器模式、接口的適配器模式。其中的對象的適配器模式是各類結構型模式的起源。spring

1、適配器模式

適配器模式主要分爲三類:類的適配器模式、對象的適配器模式、接口的適配器模式。sql

適配器模式將某個類的接口轉換成客戶端指望的另外一個接口表示,目的是消除因爲接口不匹配所形成的類的兼容性問題。有點抽象,咱們來看看詳細的內容。數據庫

1.一、類的適配器模式設計模式

類的適配器模式核心思想就是:有一個Source類,擁有一個方法,待適配,目標接口是Targetable,經過Adapter類,將Source的功能擴展到Targetable裏。數據結構

1app

2ide

3性能

4

5

6

7

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

9

package com.model.structure;

 

public interface Targetable {

    /* 與原類中的方法相同 */

    public void method1();

 

    /* 新類的方法 */

    public void method2();

}

1

2

3

4

5

6

7

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

9

package com.model.structure;

 

public class AdapterTest {

    public static void main(String[] args) {

        Targetable target = new Adapter();

        target.method1();

        target.method2();

    }

}

AdapterTest的運行結果:

這裏寫圖片描述

1.二、對象的適配器模式

對象的適配器模式的基本思路和類的適配器模式相同,只是將Adapter類做修改爲Wrapper,此次不繼承Source類,而是持有Source類的實例,以達到解決兼容性的問題。

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 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

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、裝飾模式

裝飾模式:在沒必要改變原類文件和使用繼承的狀況下,動態地擴展一個對象的功能。它是經過建立一個包裝對象,也就是裝飾來包裹真實的對象。

裝飾模式的特色:

(1) 裝飾對象和真實對象有相同的接口。這樣客戶端對象就能以和真實對象相同的方式和裝飾對象交互。
(2) 裝飾對象包含一個真實對象的引用(reference)
(3) 裝飾對象接受全部來自客戶端的請求。它把這些請求轉發給真實的對象。
(4) 裝飾對象能夠在轉發這些請求之前或之後增長一些附加功能。這樣就確保了在運行時,不用修改給定對象的結構就能夠在外部增長附加的功能。在面向對象的設計中,一般是經過繼承來實現對給定類的功能擴展。繼承不能作到這一點,繼承的功能是靜態的,不能動態增刪。

具體看看代碼實例

1

2

3

4

5

package com.model.structure;

 

public interface Sourceable {

    public void method();

}

1

2

3

4

5

6

7

8

9

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

11

12

13

14

15

16

17

18

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

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

before decorator!

the original method!

after decorator!

3、代理模式

代理模式就是多一個代理類出來,替原對象進行一些操做。代理類就像中介,它比咱們掌握着更多的信息。

具體看看代碼實例。

1

2

3

4

5

package com.model.structure;

 

public interface Sourceable {

    public void method();

}

1

2

3

4

5

6

7

8

9

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

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

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

package com.model.structure;

 

public class ProxyTest {

 

    public static void main(String[] args) {

        Sourceable source = new Proxy();

        source.method();

    }

}

運行結果:

1

2

3

before proxy!

the original method!

after proxy!

4、外觀模式

外觀模式是爲了解決類與類之間的依賴關係的,像spring同樣,能夠將類和類之間的關係配置到配置文件中,而外觀模式就是將他們的關係放在一個Facade類中,下降了類類之間的耦合度,該模式中沒有涉及到接口。

咱們以一個計算機的啓動過程爲例,看看以下的代碼:

1

2

3

4

5

6

7

8

9

10

11

12

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

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

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

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!

5、橋接模式

在軟件系統中,某些類型因爲自身的邏輯,它具備兩個或多個維度的變化,那麼如何應對這種「多維度的變化」?如何利用面嚮對象的技術來使得該類型可以輕鬆的沿着多個方向進行變化,而又不引入額外的複雜度?這就要使用Bridge模式。

在提出橋樑模式的時候指出,橋樑模式的用意是」將抽象化(Abstraction)與實現化(Implementation)脫耦,使得兩者能夠獨立地變化」。這句話有三個關鍵詞,也就是抽象化、實現化和脫耦。

抽象化:存在於多個實體中的共同的概念性聯繫,就是抽象化。做爲一個過程,抽象化就是忽略一些信息,從而把不一樣的實體當作一樣的實體對待。
實現化:抽象化給出的具體實現,就是實現化。
脫耦:所謂耦合,就是兩個實體的行爲的某種強關聯。而將它們的強關聯去掉,就是耦合的解脫,或稱脫耦。在這裏,脫耦是指將抽象化和實現化之間的耦合解脫開,或者說是將它們之間的強關聯改換成弱關聯。

下面咱們來看看代碼實例:

1

2

3

4

5

package com.model.structure;

 

public interface Driver { 

    public void connect(); 

}

1

2

3

4

5

6

7

8

9

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

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

10

11

12

13

14

15

16

17

18

19

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

package com.model.structure;

 

public class MyDriverManager extends DriverManager {

 

    public void connect() {

        super.connect();

    }

 

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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

connect mysql done!

connect db2 done!

若是看完代碼實例還不是很理解,咱們想一想以下兩個維度擴展:(1)假設我想加一個OracleDriver,這是一個維度,很好理解,很少解釋。(2)假設咱們想在鏈接先後固定輸出點什麼,咱們只須要加一個MyDriverManager2,代碼以下:

1

2

3

4

5

6

7

8

9

10

11

package com.model.structure;

 

public class MyDriverManager2 extends DriverManager {

 

    public void connect() {

        System.out.println("before connect");

        super.connect();

        System.out.println("after connect");

    }

 

}

再將Client代碼中的MyDriverManager 改爲 MyDriverManager2 ,執行結果以下:

1

2

3

4

5

6

before connect

connect mysql done!

after connect

before connect

connect db2 done!

after connect

6、組合模式

組合模式,將對象組合成樹形結構以表示「部分-總體」的層次結構,組合模式使得用戶對單個對象和組合對象的使用具備一致性。掌握組合模式的重點是要理解清楚 「部分/總體」 還有 」單個對象「 與 「組合對象」 的含義。

組合模式讓你能夠優化處理遞歸或分級數據結構。

《設計模式》:將對象組合成樹形結構以表示「部分總體」的層次結構。組合模式使得用戶對單個對象和組合對象的使用具備一致性。

涉及角色:

Component:是組合中的對象聲明接口,在適當的狀況下,實現全部類共有接口的默認行爲。聲明一個接口用於訪問和管理Component子部件。

Leaf:在組合中表示葉子結點對象,葉子結點沒有子結點。

Composite:定義有枝節點行爲,用來存儲子部件,在Component接口中實現與子部件有關操做,如增長(add)和刪除(remove)等。

好比現實中公司內各部門的層級關係,請看代碼:

Component:是組合中的對象聲明接口,在適當的狀況下,實現全部類共有接口的默認行爲。聲明一個接口用於訪問和管理Component子部件。

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

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);

 

}

Composite:定義有枝節點行爲,用來存儲子部件,在Component接口中實現與子部件有關操做,如增長(add)和刪除(remove)等。

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

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);

    }

}

Leaf:在組合中表示葉子結點對象,葉子結點沒有子結點。

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 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) {

    }

}

Client:

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

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

北京總公司

--總公司人力資源部

--總公司財務部

--山東分公司

----山東分公司人力資源部

----山東分公司帳務部

----濟南辦事處

------濟南辦事處財務部

------濟南辦事處人力資源部

----棗莊辦事處

------棗莊辦事處財務部

------棗莊辦事處人力資源部

--上海華東分公司

----上海華東分公司人力資源部

----上海華東分公司帳務部

----杭州辦事處

------杭州辦事處財務部

------杭州辦事處人力資源部

----南京辦事處

------南京辦事處財務部

------南京辦事處人力資源部

7、享元模式

享元模式的主要目的是實現對象的共享,即共享池,當系統中對象多的時候能夠減小內存的開銷,一般與工廠模式一塊兒使用。

一提到共享池,咱們很容易聯想到Java裏面的JDBC鏈接池,想一想每一個鏈接的特色,咱們不難總結出:適用於做共享的一些個對象,他們有一些共有的屬性,就拿數據庫鏈接池來講,url、driverClassName、username、password及dbname,這些屬性對於每一個鏈接來講都是同樣的,因此就適合用享元模式來處理,建一個工廠類,將上述相似屬性做爲內部數據,其它的做爲外部數據,在方法調用時,當作參數傳進來,這樣就節省了空間,減小了實例的數量。

看下數據庫鏈接池的代碼:

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

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;

        }

    }

}

經過鏈接池的管理,實現了數據庫鏈接的共享,不須要每一次都從新建立鏈接,節省了數據庫從新建立的開銷,提高了系統的性能!

相關文章
相關標籤/搜索