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


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

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

    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)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

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

  • 相关阅读:
    [SDOI2008]递归数列
    [SCOI2008]奖励关
    [SCOI2010]幸运数字
    [ZJOI2007]矩阵游戏
    [HAOI2006]旅行
    [ZJOI2008]泡泡堂
    [BZOJ1800][Ahoi2009]fly 飞行棋
    [POJ2288]Islands and Bridges
    [LUOGU] 3959 宝藏
    [BZOJ]1029: [JSOI2007]建筑抢修
  • 原文地址:https://www.cnblogs.com/DarrenChan/p/5440341.html
Copyright © 2020-2023  润新知