设计模式 -- 代理模式(Proxy)

设计模式 -- 代理模式

--------------------------------------------------------------------------------------------------------------------

前景介绍:

好了,下面开始关于代码模式的一些内容:

  话说,泡菜君在公司也来实习了快3个月了,最近以前的项目经理突然离职了,公司就派了一个新的项目经理过来带这个组.

  新项目经理 : 樊曦 

  公司实习生 : 火爆泡菜 

  今天是周一,大家都是按着踩点的节奏来到公司,然后开电脑、泡茶 等等一些上班前奏工作, 但是今天办公室来了一位新的女同事, ~_~  ~_~  ~_~  这可不得了,大家都在私下议论纷纷, 不为别的,就因为来了一个美女..........,  泡菜君心里想: 我们这和尚部门也能来这么一大美女,看来以后大家是有福了,正在泡菜君还在意淫着和新来的女同事有点什么事情的时候,这时公司老总过来了,把大家都召集起来开个短会,先是按照惯例 表扬大家最近工作辛苦了、工作很努力,然后说因为前项目经理离职了 现在公司派来一位新的项目经理 希望大家能好好的配合她的工作,把工作顺利的开展下去。 新项目经理 先给大家做了个自我介绍:她叫樊曦,大家可以叫她樊姐,以后工作希望大家能支持和配合.    介绍完了后,  下面就是一阵一阵的掌声, 从掌声中就可以听出部门里面的屌丝对于突然来的这位美女经理是多么的渴望,多么的支持。  泡菜君心里想: 叫她樊姐, 还不如叫 "吸经理" 呢...  心里一阵坏笑....  ,会议很快就结束了,大家回到各种的位置上开始了今天的工作....

 

  中午吃过饭,樊姐(泡菜君心里的"吸经理")把泡菜君叫过去,用很温柔的话对他说:最近有一个公司的项目希望功能上做出一些调整。

    目前项目的情况: 公司人力资源系统再查看一个部门或者分公司时是只显示一部分的人员信息,需要通过翻页来获取其他人员的信息。

    客户希望的改进: 1.把部门或者分公司下的所有员工都显示出来,而且不要翻页,方便他们进行业务的出来。

             2.在显示全部员工的时候,只需要显示名称即可,但是也需要提供在必要的时候可以选择并查看某位员工的详细信息

    客户公司的情况: 客户方是一个集团公司,有些部门或者分公司可能有好几百人,不让翻页,也就是要求一次性地获取多条数据并且展示出来.

  在樊姐把项目介绍和客户希望改进的地方介绍完后,问泡菜君: "你觉得你能行吗?  ",  泡菜君一听这话便立即说: "放心,樊姐,我能行的,保证让你满意....."

 

  泡菜君回到自己的电脑前,心想:该怎么实现呢?

---------------------------------------------------------------------------------------------------------------------

   泡菜君按照惯例,还是先把目前的项目从SVN上面下载下来看看目前的实现情况:

  项目该模块涉及到两张表:部门表和员工表

   

  目前表中的数据有:

   部门表的数据:

  

  员工表的数据:

  

  目前的程序如下:

  (1)描述员工数据的对象

 
/**
 * 描述员工数据的对象
 * @author Administrator
 *
 */
public class UserModel {
    //员工编号
    private String userId;
    //员工姓名
    private String name;
    //部门编号
    private String depId;
    //性别
    private String sex;
    public String getUserId() {
        return userId;
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDepId() {
        return depId;
    }
    public void setDepId(String depId) {
        this.depId = depId;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    @Override
    public String toString() {
        return "UserModel [userId=" + userId + ", name=" + name + ", depId="
                + depId + ", sex=" + sex + "]";
    }
}
 

 

  (2)获取员工数据的方法

 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;

public class UserManager {
    /**
     * 功能 : 根据部门编号来获取该部门下的所有人员
     * @param depId 部门编号
     * @return 该部门下的所有人员
     * @throws Exception 
     */
    public Collection<UserModel> getUserByDepId(String depId) throws Exception{
        Collection<UserModel> col = new ArrayList<UserModel>();
        Connection conn = null;
        try{
            conn = this.getConnection();
            String sql = "select * from tbl_user u,tbl_dep d where u.depId = d.depId and  d.depId like ?";
            PreparedStatement psmt = conn.prepareStatement(sql);
            psmt.setString(1, depId + "%");
            ResultSet rs = psmt.executeQuery();
            
            while(rs.next()){
                UserModel um = new UserModel();
                um.setUserId(rs.getString("userId"));
                um.setName(rs.getString("name"));
                um.setDepId(rs.getString("depId"));
                um.setSex(rs.getString("sex"));
                col.add(um);
            }
            rs.close();
            psmt.close();
        }finally{
            conn.close();
        }
        return col;
    }
    
    /**
     * 功能 : 获取与数据库的链接
     * @return 数据库链接
     * @throws Exception
     */
    private Connection getConnection() throws Exception{
        Class.forName("com.mysql.jdbc.Driver"); 
        return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/yl","root","123456");
    }
}
 

 

   测试类:

 
import java.util.Collection;

public class Client {
    public static void main(String[] args) throws Exception {
        UserManager userManager = new UserManager();
        Collection<UserModel> col = userManager.getUserByDepId("0101");
        System.out.println(col);
    }
}
 

 运行结果:

[UserModel [userId=user0001, name=张三1, depId=010101, sex=男], 
UserModel [userId=user0002, name=张三2, depId=010101, sex=男],
UserModel [userId=user0003, name=张三3, depId=010102, sex=男]]

  泡菜君看了目前代码的实现后,分析出存在的问题:目前已经的实现看起来很简单,功能也正确,但是蕴含一个较大的问题。那就是,当一次性访问的数据条数过多,而且每条描述的数据量又很大的话,将会消耗较多的内存。(PS:上面只是演示的数据表,对于真正开发中的用户表,事实上是有很多字段的,不仅仅是示例的几个,再加上不使用翻页,一次性访问的数据就可能会有很多条,但是从客户使用的角度来说,有很大的随机性,客户有可能访问每一条数据,也有可能一条都不访问,也就是说,一次性访问很多条数据会消耗大量的内存,但是很有可能是浪费掉了,客户根本就不会去访问那么多数据,对于每条数据,客户只需要看看姓名而已.)

 泡菜君去到 "吸经理" 办公室 把他从目前项目代码中分享出来的情况给她介绍了一下,但是目前还没有想到解决的办法,这时 樊姐用很销魂的声音说:"刚才我也看了看这个项目目前的情况,需不要我给你点帮助来解决这个问题呀!" , 泡菜君听到樊姐这话 全身都酥软了,连说:"我要你,我要你 ... 的帮助...", 樊姐白了一眼泡菜君 说:"这个问题可以用代理模式来解决",又问道:"对于代理模式你知道多少?" , 泡菜君这时完全沉浸在樊姐销魂的声音中,哪儿还记得什么代理模式, 便回答说:"代理模式啊, 这个 .... 不是特别的了解.", 樊姐又温柔的说道:"好吧,那我就先给你讲讲代码模式的基础知识,然后你回去在想想这个问题的解决办法", 然后,樊姐便起身拿起彩笔在会议讨论板上给泡菜君讲代理模式的基础知识.   (泡菜君看到樊姐起身, ~_~   他此时眼中的樊姐, 小西服、白衬衣、包臂裙、白丝袜、高跟鞋.... 身材是这样的完美  加上 声音又是如何的销魂,  泡菜君哪里Hold住这场面....  心里不知道意淫了什么东西 ...)

 不一会的功夫,樊姐就在讨论版上把 代理模式的定义、应用代理模式解决问题的思路、代理模式的结构都讲了一遍,

  代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.

  应用代理模式来解决问题的思路: 由于客户访问多条用户数据的时候,基本上只需要看到用户的姓名,因此可以考虑刚开始从数据库查询返回的用户数据就只有用户的编号和用户姓名,当客户想要查看某个用户详细数据的时候,再次根据用户编号到数据库中获取完整的用户数据。这样一来,就可以在满足客户功能的前提下,大大减少对内存的消耗,只是每次需要重新查询一下数据库,算是一个以实际换空间的策略。

   代理模式的结构和说明

  

  (1)Proxy : 代理对象,通常具有如下功能 :

              a.实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象

              b.保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象

              c.可以控制对具体目标对象的访问,并可以负责创建和删除它

  (2)Subject : 目标接口,定义代理和具体目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象

  (3)RealSubject : 具体的目标对象,真正实现目标接口要求的功能

    代理模式示例代码:

  (1)目标接口的定义

 
/**
 * 抽象的目标接口,定义具体的目标对象和代理对象共用的接口
 * @author Administrator
 */
public interface Subject {

    public void request();
}
 

  (2)具体目标对象的实现

 
/**
 * 具体的目标对象,是真正被代理的对象
 * @author Administrator
 */
public class RealSubject implements Subject {

    @Override
    public void request() {
        //执行具体的功能处理
    }
}
 

  (3)代理对象的实现

 
/**
 * 代理对象
 * @author Administrator
 */
public class Proxy implements Subject {

    //持有被代理的具体的目标对象
    private RealSubject realSubject = null;
    
    /**
     * 构造方法,传入被代理的具体的目标对象
     * @param realSubject 被代理的具体的目标对象
     */
    public Proxy(RealSubject realSubject){
        this.realSubject = realSubject;
    }
    
    @Override
    public void request() {
        //在转调具体的目标对象前,可以执行的一些功能处理
        
        //转调具体的目标对象的方法
        realSubject.request();
        
        //在转调具体的目标对象后,可以执行的一些功能处理
    }
}
 

   泡菜君听了樊姐对代码模式基础知识的介绍后,回到自己的电脑前想着 怎么样使用代理模式把目前项目的代码进行改造呢?

  泡菜君分享了半天还是没有点结果,因为此时他脑袋里全是 樊姐 刚才的身影 太迷人了... , 但是项目的需求还是得解决,这时泡菜君去卫生间用冷水把脸洗了洗,让自己清醒一下,然后开始分享问题:

  (1)要使用代理模式来重新示例,首先就需要为用户对象定义一个接口,然后实现相应的用户对象的代理,这样在使用用户对象的地方,使用这个代理对象就可以了.

  (2)这个代理对象刚开始时只需要加载 用户编号和姓名即可,再客户需要访问其他数据时再从数据库中去加载其它数据.

  分享完问题后,画了一个简单的整体结构图:

  

  (1)定义用户数据对象接口

 
/**
 * 定义用户数据对象的接口
 * @author Administrator
 *
 */
public interface UserModelApi {
    public String getUserId();
    public void setUserId(String userId);
    public String getName(); 
    public void setName(String name); 
    public String getDepId(); 
    public void setDepId(String depId); 
    public String getSex(); 
    public void setSex(String sex); 
}
 

(2)用户数据对象接口实现

 
/**
 * 描述员工数据的对象
 * @author Administrator
 *
 */
public class UserModel implements UserModelApi{
    //员工编号
    private String userId;
    //员工姓名
    private String name;
    //部门编号
    private String depId;
    //性别
    private String sex;
    public String getUserId() {
        return userId;
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDepId() {
        return depId;
    }
    public void setDepId(String depId) {
        this.depId = depId;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    @Override
    public String toString() {
        return "UserModel [userId=" + userId + ", name=" + name + ", depId="
                + depId + ", sex=" + sex + "]";
    }
}
 

(3)代理对象

 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 代理对象,代理用户数据对象
 * @author Administrator
 *
 */
public class Proxy implements UserModelApi {
    //持有被代理的具体的目标对象
    private UserModel realSubject = null;
    
    //标示是否已经重新装载过数据了
    private boolean loaded = false;
    /**
     * 构造方法,传入被代理的具体目标对象
     * @param realSubject 被代理的具体的目标对象
     */
    public Proxy(UserModel realSubject){
        this.realSubject = realSubject;
    }
    
    @Override
    public String getUserId() {
        return realSubject.getUserId();
    }

    @Override
    public void setUserId(String userId) {
        realSubject.setUserId(userId);
    }

    @Override
    public String getName() {
        return realSubject.getName();
    }

    @Override
    public void setName(String name) {
        realSubject.setName(name);
    }

    @Override
    public String getDepId() {
        //需要判断是已经装载过了
        if(!this.loaded){
            //从数据库中重新装载
            reload();
            
            //设置重新装载的标志位true
            this.loaded = true;
        }
        return realSubject.getDepId();
    }

    @Override
    public void setDepId(String depId) {
        realSubject.setDepId(depId);
    }

    @Override
    public String getSex() {
        if(!this.loaded){
            reload();
            this.loaded = true;
        }
        return realSubject.getSex();
    }

    @Override
    public void setSex(String sex) {
        realSubject.setSex(sex);
    }

    /**
     * 重新查询数据库以获取完整的用户数据
     */
    private void reload(){
        System.out.println("重新查询数据库获取完整的用户数据,userId == " + realSubject.getUserId());
        Connection conn = null;
        try{
            conn = this.getConnection();
            String sql = "select * from tbl_user where userId = ?";
            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, realSubject.getUserId());
            ResultSet rs = pstmt.executeQuery();
            if(rs.next()){
                //只需要重新获取除了userId和name歪的数据
                realSubject.setDepId(rs.getString("depId"));
                realSubject.setSex(rs.getString("sex"));
            }
            rs.close();
            pstmt.close();
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                conn.close();
            }catch(SQLException e){
                e.printStackTrace();
            }
        }
    }
    
    public String toString(){
        return "userId = " + getUserId() + ",name = " + getName() + ",depId = " + getDepId() + ",sex = " + getSex();
    }
    
    /**
     * 功能 : 获取与数据库的链接
     * @return 数据库链接
     * @throws Exception
     */
    private Connection getConnection() throws Exception{
        Class.forName("com.mysql.jdbc.Driver"); 
        return DriverManager.getConnection("jdbc:mysql://192.168.1.128:3306/yl","root","123456");
    }
}
 

(4)UserManager

 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;

public class UserManager {
    /**
     * 根据部门编号来获取该部门下所有人员
     * @param depId
     * @return
     * @throws Exception 
     */
    public Collection<UserModelApi> getUserByDepId(String depId) throws Exception{
        Collection<UserModelApi> col = new ArrayList<UserModelApi>();
        Connection conn = null;
        
        try{
            conn = this.getConnection();
            //只需要查询userId和name两个值就可以了
            String sql = "select u.userId,u.name from tbl_user u,tbl_dep d where u.depId = d.depId and d.depId like ?";
            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, depId + "%");
            
            ResultSet rs = pstmt.executeQuery();
            while(rs.next()){
                //这里是创建的代理对象,而不是直接创建UserModel的对象
                Proxy proxy = new Proxy(new UserModel());
                
                //只是设置userId和name两个值就可以了
                proxy.setUserId(rs.getString("userId"));
                proxy.setName(rs.getString("name"));
                col.add(proxy);
            }
            rs.close();
            pstmt.close();
        }finally{
            conn.close();
        }
        return col;
    }
    
    /**
     * 功能 : 获取与数据库的链接
     * @return 数据库链接
     * @throws Exception
     */
    private Connection getConnection() throws Exception{
        Class.forName("com.mysql.jdbc.Driver"); 
        return DriverManager.getConnection("jdbc:mysql://192.168.1.128:3306/yl","root","123456");
    }
}
 

(5)客户端

 
import java.util.Collection;

public class Client {
    public static void main(String[] args) throws Exception {
        UserManager userManager = new UserManager();
        Collection<UserModelApi> col = userManager.getUserByDepId("0101");
        
        //如果只是显示用户名称、则不需要重新查询数据库
        for(UserModelApi umApi : col){
            System.out.println("用户编号:" + umApi.getUserId() + ",用户姓名: " + umApi.getName());
        }
        
        //如果访问非用户编号和用户姓名外的属性,那就会重新查询数据库
        for(UserModelApi umApi : col){
            System.out.println("用户编号:" + umApi.getUserId() + ",用户姓名: " + umApi.getName()+",所属部门:" + umApi.getDepId());
        }
    }
}
 

 (6)运行结果

 
用户编号:user0001,用户姓名: 张三1
用户编号:user0002,用户姓名: 张三2
用户编号:user0003,用户姓名: 张三3
重新查询数据库获取完整的用户数据,userId == user0001
用户编号:user0001,用户姓名: 张三1,所属部门:010101
重新查询数据库获取完整的用户数据,userId == user0002
用户编号:user0002,用户姓名: 张三2,所属部门:010101
重新查询数据库获取完整的用户数据,userId == user0003
用户编号:user0003,用户姓名: 张三3,所属部门:010102
 

 泡菜君按照樊姐讲的代理模式 依葫芦画瓢 把目前的程序进行了改进,但是对于代理模式还是不是特别熟悉,所以他在网上查询更多代理模式的资料

 

模式讲解

  代理模式的功能 : 通过创建一个代理对象,用这个代理对象去代表真实的对象,客户端使用这个代理对象来操作真实的对象(当客户端操作这个代理对象的时候,实际上功能最终还是会由真实的对象来完成,只不过是通过代理操作的,也就是客户端操作代理,代理操作真正的对象)

  代理模式的调用顺序图:

  

  代理模式的分类:

    (1)虚代理: 根据需要来创建开销很大的对象,该对象只有在需要的时候才会被真正创建.

    (2)保护代理: 控制对原始对象的访问,如果有需要,可以给不同的用户提供不同的访问权限,以控制他们对原始对象的访问.

 

  虚代理的示例,前面的列子就是一个虚代理的实现,起初每个代理对象只有用户编号和姓名数据,直到需要的时候,才会把整个用户的数据加载到内存中来.也就是说,要根据需要来装载整个UserModel的数据,虽然用户数据对象前面已经创建好了的,但是只有用户编号和用户姓名的数据,可以看成是一个"虚"的对象,直到通过代理把所有的数据都设置好,才算是一个完整的用户数据对象.

  保护代理, 保护代理是一种控制对原始对象访问的代理,多用于对象应该有不同的访问权限的时候,保护代理会检查调用者是否具有请求所必须的访问权限,如果没有相应的权限,那么就不会调用目标对象,从而实现对目标对象的保护.

 对于保护代理还是用一个示例来说明:现在有一个订单系统,要求是: 一旦订单被创建,只有订单的创建人才可以修改订单中的数据,其他人则不能修改,相当于现在如果有了一个订单对象实例,那么就需要控制外部对它的访问,满足条件的可以访问,不满足条件的就不能访问.

 (1)订单对象的接口定义

 
public interface OrderApi {
    /**
     * 功能 : 获取订购的产品的名称
     * @return 订单订购的产品名称
     */
    public String getProductName();
    
    /**
     * 功能 : 设置订单订购的产品名称
     * @param productName 订单订购的产品名称
     */
    public void setProductName(String productName);
    
    /**
     * 功能 : 获取订单订购的数量
     * @return 订单订购的数量
     */
    public int getOrderNum();
    
    /**
     * 设置订单订购的数量
     * @param orderNum 订单订购的数量
     * @param user 操作人员
     */
    public void setOrderNum(int orderNum,String user);
    
    /**
     * 获取创建订单的人员
     * @return 创建订单的人员
     */
    public String getOrderUser();
    
    /**
     * 设置创建订单的人员
     * @param orderUser 创建订单的人员
     * @param user 操作人员
     */
    public void setOrderUser(String orderUser,String user);
}
 

 (2)订单对象

 
public class Order implements OrderApi{
    //订单订购的产品名称
    private String productName;
    //订单订购的数量
    private int orderNum;
    //创建订单的人员
    private String orderUser;
    
    public Order(String productName, int orderNum, String orderUser) {
        super();
        this.productName = productName;
        this.orderNum = orderNum;
        this.orderUser = orderUser;
    }
    
    public String getProductName() {
        return productName;
    }
    public void setProductName(String productName) {
        this.productName = productName;
    }
    public int getOrderNum() {
        return orderNum;
    }
    public void setOrderNum(int orderNum,String user) {
        this.orderNum = orderNum;
    }
    public String getOrderUser() {
        return orderUser;
    }
    public void setOrderUser(String orderUser,String user) {
        this.orderUser = orderUser;
    }
}
 

 (3)订单对象的代理

 
/**
 * 订单的代理对象
 * @author Administrator
 *
 */
public class OrderProxy implements OrderApi {

    //持有被代理的具体的目标对象
    private Order order = null;
    
    /**
     * 构造方法,传入被代理的具体的目标对象
     * @param order 被代理的具体的目标对象
     */
    public OrderProxy(Order order){
        this.order = order;
    }
    
    @Override
    public String getProductName() {
        return order.getProductName();
    }

    @Override
    public void setProductName(String productName,String user) {
        //控制访问权限,只有创建订单的人员才能够修改
        if(user != null && user.equals(this.getOrderUser())){
            order.setProductName(productName, user);
        }else{
            System.out.println("对不起 "+ user +",您无权修改订单中的产品名称!");
        }
    }

    @Override
    public int getOrderNum() {
        return order.getOrderNum();
    }
    
    @Override
    public void setOrderNum(int orderNum, String user) {
        //控制访问权限,只有创建订单的人员才能够修改
        if(user != null && user.equals(this.getOrderUser())){
            order.setOrderNum(orderNum, user);
        }else{
            System.out.println("对不起 "+ user +",您无权修改订单中的订购数量!");
        }
    }

    @Override
    public String getOrderUser() {
        return order.getOrderUser();
    }

    @Override
    public void setOrderUser(String orderUser, String user) {
        //控制访问权限,只有创建订单的人员才能够修改
        if(user != null && user.equals(this.getOrderUser())){
            order.setOrderUser(orderUser, user);
        }else{
            System.out.println("对不起 "+ user +",您无权修改订单中的订购人!");
        }
    }

    @Override
    public String toString() {
        return "Order [productName=" + this.getProductName() + ", orderNum=" + this.getOrderNum()
                + ", orderUser=" + this.getOrderUser() + "]";
    }
}
 

 (4)测试代码

 
public class Client {
    public static void main(String[] args) {
        //张三先登录系统创建了一个订单
        OrderApi order = new OrderProxy(new Order("设计模式",100,"张三"));
        
        //李四想要来修改
        order.setOrderNum(123, "李四");
        
        //输出Order
        System.out.println("李四修改后的订单记录没有变化: " + order);
        
        //张三修改就不会有问题
        order.setOrderNum(123, "张三");
        
        //再次输出order
        System.out.println("张三修改后,订单记录:" + order);
    }
}
 

 输出结果:

对不起 李四,您无权修改订单中的订购数量!
李四修改后的订单记录没有变化: Order [productName=设计模式, orderNum=100, orderUser=张三]
张三修改后,订单记录:Order [productName=设计模式, orderNum=123, orderUser=张三]

   

   Java中的代理

    Java对代理模式提供了内建的支持,在java.lang.reflect包下面,提供了一个Proxy的类和一个InvocationHandler接口。

  通常把前面我们自己实现的代理模式成为Java的静态代理。这种实现方式由一个较大的缺点,就是如果Subject接口发生改变,那么代理类和具体的目标实现都要变化,不是很灵活。 

  使用Java内建的对代理模式支持的功能来实现的代理成为Java的动态代理。动态代理和静态代理明显的变化是:静态代理实现的时候,在Subject接口上定义很多的方法,代理类里面自然也要实现很多方法;而动态代理实现的时候,虽然Subject接口上定义了很多方法,但是动态代理类始终只有一个invoke方法。这样,当Subject接口发生变化的时候,动态代理的接口就不需要跟着变化了.

  Java的动态代理目前只能代理接口,基本的实现是依靠Java的反射机制和动态生成class的技术,来动态生成被代理的接口的实现对象.(如果要实现类的代理,可以使用cglib)

  下面还是使用示例来说明Java的动态代理:

  (1)订单接口定义

 
public interface OrderApi {
    /**
     * 功能 : 获取订购的产品的名称
     * @return 订单订购的产品名称
     */
    public String getProductName();
    
    /**
     * 功能 : 设置订单订购的产品名称
     * @param productName 订单订购的产品名称
     */
    public void setProductName(String productName,String user);
    
    /**
     * 功能 : 获取订单订购的数量
     * @return 订单订购的数量
     */
    public int getOrderNum();
    
    /**
     * 设置订单订购的数量
     * @param orderNum 订单订购的数量
     * @param user 操作人员
     */
    public void setOrderNum(int orderNum,String user);
    
    /**
     * 获取创建订单的人员
     * @return 创建订单的人员
     */
    public String getOrderUser();
    
    /**
     * 设置创建订单的人员
     * @param orderUser 创建订单的人员
     * @param user 操作人员
     */
    public void setOrderUser(String orderUser,String user);
}
 

 

  (2)订单对象的实现

 
public class Order implements OrderApi{
    //订单订购的产品名称
    private String productName;
    //订单订购的数量
    private int orderNum;
    //创建订单的人员
    private String orderUser;
    
    public Order(String productName, int orderNum, String orderUser) {
        super();
        this.productName = productName;
        this.orderNum = orderNum;
        this.orderUser = orderUser;
    }
    
    public String getProductName() {
        return productName;
    }
    public void setProductName(String productName,String user) {
        this.productName = productName;
    }
    public int getOrderNum() {
        return orderNum;
    }
    public void setOrderNum(int orderNum,String user) {
        this.orderNum = orderNum;
    }
    public String getOrderUser() {
        return orderUser;
    }
    public void setOrderUser(String orderUser,String user) {
        this.orderUser = orderUser;
    }

    @Override
    public String toString() {
        return "Order [productName=" + productName + ", orderNum=" + orderNum
                + ", orderUser=" + orderUser + "]";
    }
}
 

  (3)代理类的实现

 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理
 * @author Administrator
 *
 */
public class DynamicProxy implements InvocationHandler {

    //持有被代理的对象
    private OrderApi order = null;
    
    /**
     * 获取绑定好代理和具体目标对象的目标对象的接口
     * @param order    具体的订单对象,相当于具体的目标对象
     * @return 绑定好代理和具体目标对象的目标对象的接口
     */
    public OrderApi getProxyInterface(Order order){
        //设置被代理的对象,好方便invoke里面的操作
        this.order = order;
        
        //把真正的订单对象和动态代理关联起来
        OrderApi orderApi = (OrderApi)Proxy.newProxyInstance(
                order.getClass().getClassLoader(),
                order.getClass().getInterfaces(),
                this);
        return orderApi;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //如果是调用setter方法就需要检查权限
        if(method.getName().startsWith("set")){
            //如果不是创建人,那就不能修改
            if(order.getOrderUser() != null && order.getOrderUser().equals(args[1])){
                //可以操作
                return method.invoke(order, args);
            }else{
                System.out.println("对不起," + args[1]+",您无权修改本订单中的数据");
            }
        }else{
            //不是调用的setter方法就继续执行
            return method.invoke(order, args); 
        }
        return null;
    }
}
 

 (4)测试类

 
public class Client {
    public static void main(String[] args) {
        //张三先登录系统创建了一个订单
        Order order = new Order("设计模式",100,"张三");
        
        //创建一个动态代理
        DynamicProxy dynamicProxy = new DynamicProxy();
        //然后把订单和动态代理关联起来
        OrderApi orderApi = dynamicProxy.getProxyInterface(order);
        
        //以下就需要使用被代理过的接口来操作了
        //李四想要来修改
        orderApi.setOrderNum(123, "李四");
        
        //输出Order
        System.out.println("李四修改后的订单记录没有变化: " + order);
        
        //张三修改就不会有问题
        orderApi.setOrderNum(123, "张三");
        
        //再次输出order
        System.out.println("张三修改后,订单记录:" + order);
    }
}
 

运行结果:

对不起,李四,您无权修改本订单中的数据
李四修改后的订单记录没有变化: Order [productName=设计模式, orderNum=100, orderUser=张三]
张三修改后,订单记录:Order [productName=设计模式, orderNum=123, orderUser=张三]

  对代理模式的思考

    代理模式的本质: 控制对象的访问。

----------------------------------------------------------------------------------------------------------------------------------

  参考:《研磨设计模式》一书 

  后话:  因个人水平有限,如有不足之处,希望大家给我指出,以求共同学习,共同进步。   2016-06-06 21:26   

 

 

 

 

 

 

 

 

 

 

 

 

  

 

分类: 设计模式

posted on 2016-06-07 10:15  Sun‘刺眼的博客  阅读(329)  评论(0编辑  收藏  举报

导航