代理模式(Proxy)

来源:互联网 发布:小米6相机在那清除数据 编辑:程序博客网 时间:2024/06/06 17:57

1. 模式定义

要求一次性地获取多条数据并展示出来
该怎么实现,才能既把多条用户数据的姓名显示出来,而又能节省内存空间?

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

代理模式引入一个Proxy对象来解决这个问题。刚开始只有用户编号和姓名的时候,不是一个完整的用户对象,而是一个代理对象。当需要访问完整的用户数据时,代理会从数据库中重新获取相应的数据。

2. UML图

这里写图片描述

Proxy:代理对象,实现与具体的目标对象一样的接口,这样可以使用代理来代替具体的目标对象。保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象。
Subject:目标接口,定义代理和具体目标对象的接口
RealSubject:具体的目标对象,真正实现目标接口要求的功能

代码:

/** * 描述用户数据的对象 */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 "userId="+userId+",name="+name+",depId="+depId+",sex="+sex+"\n";    }}/** * 定义用户数据对象的接口 */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);}/** * 代理对象,代理用户数据对象 */public class Proxy implements UserModelApi{    /**     * 持有被代理的具体的目标对象     */    private UserModel realSubject=null;    /**     * 构造方法,传入被代理的具体的目标对象     * @param realSubject 被代理的具体的目标对象     */    public Proxy(UserModel realSubject){        this.realSubject = realSubject;    }    /**     * 标示是否已经重新装载过数据了     */    private boolean loaded = false;    public String getUserId() {        return realSubject.getUserId();    }    public void setUserId(String userId) {        realSubject.setUserId(userId);    }    public String getName() {        return realSubject.getName();    }    public void setName(String name) {        realSubject.setName(name);    }    public void setDepId(String depId) {        realSubject.setDepId(depId);    }    public void setSex(String sex) {        realSubject.setSex(sex);    }    public String getDepId() {        //需要判断是否已经装载过了        if(!this.loaded){            //从数据库中重新装载            reload();            //设置重新装载的标志为true            this.loaded = true;        }        return realSubject.getDepId();    }       public String getSex() {        if(!this.loaded){            reload();            this.loaded = true;        }        return realSubject.getSex();    }    /**     * 重新查询数据库以获取完整的用户数据     */    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 err){            err.printStackTrace();        }finally{            try {                conn.close();            } catch (SQLException e) {                e.printStackTrace();            }        }    }    public String toString(){        return "userId="+getUserId()+",name="+getName()        +",depId="+getDepId()+",sex="+getSex()+"\n";    }    private Connection getConnection() throws Exception {        Class.forName("oracle.jdbc.driver.OracleDriver");        return DriverManager.getConnection(                "jdbc:oracle:thin:@localhost:1521:orcl", "test", "test");    }}/** * 实现示例要求的功能 */public class UserManager {      /**     * 根据部门编号来获取该部门下的所有人员     * @param depId 部门编号     * @return 该部门下的所有人员     */    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 数据库连接     */    private Connection getConnection() throws Exception {        Class.forName("oracle.jdbc.driver.OracleDriver");        return DriverManager.getConnection(                "jdbc:oracle:thin:@localhost:1521:orcl", "test", "test");    }}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());        }    }}

3. 研磨设计模式

1) 代理模式的功能:
代理模式是通过创建一个代理对象,用这个代理对象去代表实际真实的对象,客户端得到这个代理对象后,对客户端没有什么影响,就跟得到了真实对象一样来使用

2) 代理模式的本质是:控制对象访问
当客户端操作这个代理对象时,实际功能最终还是会由真实的对象完成。

3) 虚代理:根据需要创建一个开销很大的对象,该对象只有在需要的时候才被真正创建
保护代理:控制对原始对象的访问,多用于对象应该有不同的访问权限

//保护代理/** * 订单对象的接口定义 */public interface OrderApi {    /**     * 获取订单订购的产品名称     * @return 订单订购的产品名称     */    public String getProductName();    /**     * 设置订单订购的产品名称     * @param productName 订单订购的产品名称     * @param user 操作人员     */    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);}/** * 订单对象 */public class Order implements OrderApi{    /**     * 订单订购的产品名称     */    private String productName;    /**     * 订单订购的数量     */    private int orderNum;    /**     * 创建订单的人员     */    private String orderUser;    /**     * 构造方法,传入构建需要的数据     * @param productName 订单订购的产品名称     * @param orderNum 订单订购的数量     * @param orderUser 创建订单的人员     */    public Order(String productName,int orderNum,String orderUser){        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;    }}/** * 订单的代理对象 */public class OrderProxy implements OrderApi{    /**     * 持有被代理的具体的目标对象     */    private Order order=null;    /**     * 构造方法,传入被代理的具体的目标对象     * @param realSubject 被代理的具体的目标对象     */    public OrderProxy(Order realSubject){        this.order = realSubject;    }    public void setProductName(String productName,String user) {        //控制访问权限,只有创建订单的人员才能够修改        if(user!=null && user.equals(this.getOrderUser())){            order.setProductName(productName, user);        }else{            System.out.println("对不起"+user+",您无权修改订单中的产品名称。");        }    }    public void setOrderNum(int orderNum,String user) {        //控制访问权限,只有创建订单的人员才能够修改        if(user!=null && user.equals(this.getOrderUser())){            order.setOrderNum(orderNum, user);        }else{            System.out.println("对不起"+user+",您无权修改订单中的订购数量。");        }    }    public void setOrderUser(String orderUser,String user) {        //控制访问权限,只有创建订单的人员才能够修改        if(user!=null && user.equals(this.getOrderUser())){            order.setOrderUser(orderUser, user);        }else{            System.out.println("对不起"+user+",您无权修改订单中的订购人。");        }    }    public int getOrderNum() {        return this.order.getOrderNum();    }    public String getOrderUser() {        return this.order.getOrderUser();    }    public String getProductName() {        return this.order.getProductName();    }    public String toString(){        return "productName="+this.getProductName()+",orderNum="+this.getOrderNum()+",orderUser="+this.getOrderUser();    }}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);    }}

4) Java对代理模式提供了内建的支持,在java.lang.reflect包下面,提供了一个Proxy和InvocationHandler。
静态代理和动态代理:
静态代理实现的时候,在Subject接口上定义了很多的方法,代理类里面自然也要实现很多方法
而动态代理实现的时候,虽然Subject接口上定义了很多的方法,但是动态代理类只有一个invoke方法。

/** * 使用Java中的动态代理 */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;    }    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;    }}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("李四修改后订单记录没有变化:"+orderApi);        //张三修改就不会有问题        orderApi.setOrderNum(123, "张三");        //再次输出order        System.out.println("张三修改后,订单记录:"+orderApi);    }}
0 0
原创粉丝点击