关于装饰模式和动态代理模式

装饰模式和动态代理模式乍一看差不多,都是动态的增加行为,其实有各自的区别。

一、首先我们看一下装饰设计模式,其基本思想如下:

1、编写一个类,实现与被装饰类相同的接口。目的使他们有相同的行为

2、定义一个实例变量,引用被装饰对象。目的和原来的老对象进行交接

3、定义构造方法,把被装饰对象注入进来。

4、对于不需要改写的方法,调用被装饰对象的。

5、对于要改写的方法,改写即可。

废话不多说,举一个例子,模拟实现一个数据库连接池,在这里,我想重写close方法,以实现调用close方法之后不是关闭连接,而是归还连接。

首先,继承java.sql.Connection接口,写一个类MyConnection:

 1 package pool;
 2 
 3 import java.sql.Array;
 4 import java.sql.Blob;
 5 import java.sql.CallableStatement;
 6 import java.sql.Clob;
 7 import java.sql.Connection;
 8 import java.sql.DatabaseMetaData;
 9 import java.sql.NClob;
10 import java.sql.PreparedStatement;
11 import java.sql.SQLClientInfoException;
12 import java.sql.SQLException;
13 import java.sql.SQLWarning;
14 import java.sql.SQLXML;
15 import java.sql.Savepoint;
16 import java.sql.Statement;
17 import java.sql.Struct;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Properties;
21 import java.util.concurrent.Executor;
22 
23 //1、编写一个类,实现与被装饰类(com.mysql.jdbc.Connection)相同的接口。目的使他们有相同的行为
24 //2、定义一个实例变量,引用被装饰对象。目的和原来的老对象进行交接
25 //3、定义构造方法,把被装饰对象注入进来。
26 //4、对于不需要改写的方法,调用被装饰对象的。
27 //5、对于要改写的方法,改写即可。
28 
29 public class MyConnection implements Connection {
30     
31     private Connection conn;
32     private List<Connection> pool;
33     
34     public MyConnection(Connection conn, List<Connection> pool){
35         this.conn = conn;
36         this.pool = pool;
37     }
38 
39     //把conn还回池中
40     @Override
41     public void close() throws SQLException {
42         pool.add(conn);
43     }
44     
45     //静态代理
46     @Override
47     public <T> T unwrap(Class<T> iface) throws SQLException {
48         return conn.unwrap(iface);
49     }
50 
51     @Override
52     public boolean isWrapperFor(Class<?> iface) throws SQLException {
53         return conn.isWrapperFor(iface);
54     }

除了重写的close方法进行改写,其余的都调用被装饰对象的,即conn.

接下来,继承标准的数据源,写一个MyDataSource,这里只演示重写getConnection()方法。

package pool;

import java.awt.Menu;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

import javax.sql.DataSource;

import day02.JDBCUtil;

//继承标准的数据源
public class MyDataSource implements DataSource {
    
    //存放链接对象的池
    private static List<Connection> pool = 
            Collections.synchronizedList(new ArrayList<Connection>());
    //最开始初始化一些链接到池中
    static{
        for (int i = 0; i < 10; i++) {
            Connection conn = JDBCUtil.getConnection();
            pool.add(conn);
        }
    }
    
    @Override
    public Connection getConnection() throws SQLException {
        if(pool.size() > 0){
            Connection connection =  pool.remove(0);
            //使用装饰模式
            MyConnection myConnection = new MyConnection(connection, pool);
            return myConnection;

        }else{
            throw new RuntimeException("服务器忙");
        }
    }

 可以通过以下代码进行验证:

 1 @Test
 2     public void test1() throws SQLException{
 3         MyDataSource ds = new MyDataSource();
 4         //保存
 5         Connection conn = ds.getConnection();
 6         System.out.println(conn.getClass().getName()); //输出MyConnection
 7         //-----------
 8         conn.close();//需要重新写一下close方法,不让它关闭而是归还连接
 9         
10         //删除
11         Connection conn1 = ds.getConnection();
12         //-----------
13         conn1.close();
14     }

二、对于动态代理模式,是AOP技术实现的关键,实现它有两种方式:

基于接口的动态代理:

前提:被代理对象的类,实现了至少一个接口

基于子类的动态代理:

借助第三方-CGLIB。

这里我们采用基于接口的动态代理进行演示刚才的例子:

不用写MyConnection了,只需要写MyDataSource:

package pool;

import java.awt.Menu;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

import javax.sql.DataSource;

import day02.JDBCUtil;

//继承标准的数据源
public class MyDataSource implements DataSource {
    
    //存放链接对象的池
    private static List<Connection> pool = 
            Collections.synchronizedList(new ArrayList<Connection>());
    //最开始初始化一些链接到池中
    static{
        for (int i = 0; i < 10; i++) {
            Connection conn = JDBCUtil.getConnection();
            pool.add(conn);
        }
    }
    
    @Override
    public Connection getConnection() throws SQLException {
        if(pool.size() > 0){
            final Connection connection =  pool.remove(0);//使用动态代理模式
            Connection proxyConnection = (Connection) Proxy.newProxyInstance(
                    connection.getClass().getClassLoader(), 
                    new Class[]{Connection.class},
                    new InvocationHandler() {
                        
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args)
                                throws Throwable {
                            if("close".equals(method.getName())){
                                //用户调用close方法,返回池中
                                pool.add(connection);
                            }else{
                                //其他方法,调用原来对象的对应方法
                                method.invoke(connection, args);
                            }
                            return null;
                        }
                    });
            return proxyConnection;
        }else{
            throw new RuntimeException("服务器忙");
        }
    }
    

然后进行验证:

@Test
    public void test1() throws SQLException{
        MyDataSource ds = new MyDataSource();
        //保存
        Connection conn = ds.getConnection();
        System.out.println(conn.getClass().getName());//com.sun.proxy.$Proxy4
        //-----------
        conn.close();//需要重新写一下close方法,不让它关闭而是归还连接
        
        //删除
        Connection conn1 = ds.getConnection();
        //-----------
        conn1.close();
    }

对装饰器模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个 接口。对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。

然而,实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。    

原文地址:https://www.cnblogs.com/DarrenChan/p/5440341.html