结构型模式:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
2.1、Adapter(适配器模式):
class C继承class A ,以达到A的代码重复利用;就是将class A 的方法适配到 接口B中去,这种设计模式也是比较常用的;
上面是C继承了A,默认得到了2个方法,这种称为类适配模式,
若C没有继承A而是在C中声明了A为属性变量a,那么在实例C时,interfaceB c=new C();c.a.method2();也实现了和上面一样的功能,这种称为对象适配模式;
因为接口的每个方法,子类都要覆写,这样就造成子类不需要的方法也要去覆写;最好就是在实现类和接口之间增加一个抽象类,这样实现类就可以自由选择自己需要覆写那些方法了,增加灵活性。
比如:
AbstractB implements interfaceB{A a;method1(){a.method1();};method2(){a.method2();};method3();method4()}
//采用对象适配模式,加载资源类A的方法;也可采用类适配模式直接继承A加载
class C extends AbstractB{method3()}
class D extends AbstractB{method4()}
interfaceB d=new D();
d.method1();
d.method4();
![](https://i-blog.csdnimg.cn/blog_migrate/e2a223d7263f3c1dc31ae7a4767a6473.jpeg)
这种称为接口适配模式;个人理解叫"抽象类适配模式"为实现类提供了可以选择性地实现抽象方法,增加编写程序的灵活性;
2.2、Decorator(装配器模式)
适配模式是将资源类的方法 加载到接口中去,以供实现类调用;
而装配类可以理解为:选择使用简装版的A和精装版的C(把A装饰了一下)
这样c.method()就在a.method的基础上增加了其他功能,采用对象适配模式加载方法;为什么不用C直接继承A,然后覆写method?那样的话没有体现复用代码的思想,也没体现装饰的含义,因为覆写就相当于重写做了个功能,装饰是在原功能之上添加点次要功能(比如一个毛坯房需要装饰,不是把拆了 重新建个别墅的意思);
![](https://i-blog.csdnimg.cn/blog_migrate/6adf9be4ce92ee483c16fdbae6ae601a.jpeg)
这样c.method()就在a.method的基础上增加了其他功能,采用对象适配模式加载方法;为什么不用C直接继承A,然后覆写method?那样的话没有体现复用代码的思想,也没体现装饰的含义,因为覆写就相当于重写做了个功能,装饰是在原功能之上添加点次要功能(比如一个毛坯房需要装饰,不是把拆了 重新建个别墅的意思);
2.3、Proxy(代理模式)
看过前面的类加载器,就知道父类加载器可以代理子类加载器去加载一个.class文件,直至祖先类成功加载为止;在现实生活中,一般你的父母可以代理你完成某些事项,又如你委托律师代理去打官司,又如你委托朋友代理缴纳社保(要提供两人的身份证),又如你要建房子,建筑公司代理你帮你拿到建房许可、房梁室内外设计、分包施工队、分包水电气装修、代办房产证等等。
![](https://i-blog.csdnimg.cn/blog_migrate/1fd55d1258c0fdee566adcf6afd1d23e.jpeg)
从结构上与装配模式相似,只是装配模式一般是没有划分模块功能;而代理模式是有分模块功能的,可以调用模块,利于代码的升级、维护;
2.4、Facade(外观模式)
![](https://i-blog.csdnimg.cn/blog_migrate/5fe27358a10b9b05b50d979b7bc1d036.jpeg)
ABCD互相没有依赖关系,实现解耦的目的;如果没有F封装这些方法,而是ABCD互持其它对象,比如B的方法发生了改变,ACD并不知道而继续加载使用,可能会出现程序错误;外观模式只需要通知F:B的某个方法改变了,F采取相应调整就可以了,而原来ACD都需要进行调整;
2.5、Bridge(桥接模式)
使用过数据库就会知道有个叫桥连接的概念:
Connection con=null;
Class.forName("com.mysql.jdbc.Driver");
con=DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "111122");
//dosomgthing
con.close();
Connection、Driver是java.sql包里定义的两个接口;DriverManager是java.sql包里定义的一个类;如何启动mysql驱动程序的呢?com.mysql.jdbc.Driver是mysql-connector-java.jar里定义的一个类;这里使用了反射机制,照说应该写成Class clazz=Class.forName("com.mysql.jdbc.Driver");
然后在创建实例,再调用实例的某个方法来启动数据库,比如这样:clazz.newInstace().method(); 这里看看com.mysql.jdbc.Driver的源码是怎么写的,好像现在下载不到源码,使用反编译器jd-gui查看:
public class Driver extends NonRegisteringDriver{
public Driver() throws SQLException{}
static {
try{
DriverManager.registerDriver(new Driver());
}
catch(SQLException E){
throw new RuntimeException("Can't register driver!");
}
}
}
在Driver的构造方法里声明了static关键字,也就是说在Class.forName的时候,1.构造方法没有执行,2.执行DriverManager.registerDriver方法,3.执行registerDriver为什么里面是new Driver()而不是this呢?
解决这个问题 看我的第2章、类与对象关于方法的执行顺序;因为直接执行了静态块,该对象还没创建一个堆内存空间,this指向的是个null,jvm会报空指针异常;这里查看到NonRegisteringDriver 实现了Driver接口:
再看看DriverManager.registerDriver()这个方法:
![](https://i-blog.csdnimg.cn/blog_migrate/5bde861d283b75b75370e92f1603b9af.jpeg)
再看看DriverManager.registerDriver()这个方法:
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver));
} else {
throw new NullPointerException();
}
执行上一步,就是为连接做好了准备,在DriverManager(驱动器管理者)这里注册登记了,以后就可以使用了; 需要更换数据库只需要更改发射机制提供的地址:
Class.forName("oracle.jdbc.driver.OracleDriver");
public class OracleDriver implements Driver{... ...}//同样里面也是有static{DriverManager.registerDriver()}方法
2.6、Composite(组件模式)
组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便;
2.7、Flyweight(享元模式)
享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。
当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象;又如
Java里面的JDBC连接池,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。
/*公有属性*/
private String url = "jdbc:mysql://localhost:3306/test";
private String username = "root";
private String password = "root";
private String driverClassName = "com.mysql.jdbc.Driver";
private Vector<Connection> pool;
public ConnectionPool(int poolSize) throw Exception{
pool = new Vector<Connection>(poolSize);
for (int i = 0; i < poolSize; i++) {
Class.forName(driverClassName);
conn = DriverManager.getConnection(url, username, password);
pool.add(conn);
}
}
通过设置Vector/List集合里的实列数量,来动态实现最大、最小、初始、闲置的数据库连接数量;
下章接着讲解设计模式的行为模式;