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;

        }

    }

}

经过链接池的管理,实现了数据库链接的共享,不须要每一次都从新建立链接,节省了数据库从新建立的开销,提高了系统的性能!

相关文章
相关标签/搜索