代理模式(静态代理)

初识代理模式
定义:为其他对象提供一种代理以控制对这个对象的访问
Proxy:
代理对象,通常具有如下功能:
1:实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象
2:保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象
3:可以控制对具体目标对象的访问,并可能负责创建和删除它
Subject:
目标接口,定义代理和具体目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象
RealSubject:
具体的目标对象,真正实现目标接口要求的功能。

/**
 *抽象的目标接口,定义具体的目标对象和代理公用的接口
 */
public interface Subject {
    //示意方法:一个抽象的请求方法
    public void request();
}
/**
 *代理对象
 */
public class Proxy implements Subject {
    //持有被代理的具体的目标对象
    private RealSubject realSubject = null;
    //构造方法:传入被代理的具体的目标对象
    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }
    public void request() {
        //在转调具体的目标对象前,可以执行一些功能处理

        //转调具体的目标对象的方法
        realSubject.request();

        //在转调具体的目标对象后,可以执行一些功能处理
    }
}
/**
 *具体的目标对象,是真正被代理的对象
 */
public class RealSubject implements Subject {
    public void request() {
        //执行具体的功能处理
    }
}

体会代理模式
访问多条数据
考虑这样一个实际应用:要一次性访问多条数据。
这个功能的背景是这样的:在一个HR(人力资源)应用项目中客户提出,当选择一个部门或是分公司的时候,要把这个部门或者分公司下的所有员工都显示处理,而且不要翻页,好方便他们进行业务处理。在显示全部员工的时候,只需要显示名称即可,但是也需要提供如下的功能:在必要的时候可以选择并查看某位员工的详细信息。
客户方是一个集团公司,有些部门或者分公司可能有好几百人,不让翻页,也就是要求一次性的获取这多条数据并展示出来。
该怎样实现呢?
不用模式的解决方案
不就是要获取某个部门或者某个分公司下的所有员工的信息吗?直接使用sql语句从数据库中查询就可以得到,示意性的sql大致如下:

String sql = “select * from 用户表,部门表 where 用户表.depId = 部门表.depId and 部门表.depId like ‘”+用户选择查看的depId+”%’”;

1:建表的语句如下:

CREATE TABLE TBL_DEP (
    DEPID VARCHAR2(20) PRIMARY KEY,
    NAME VARCHAR2(20)   
);
CREATE TABLE TBL_USER (
    USERID VARCHAR2(20) PRIMARY KEY,
    NAME VARCHAR2(20),
    DEPID VARCHAR2(20),
    SEX VARCHAR2(10),
    CONSTRAINT TBL_USER_FK FOREIGN KEY(DEPID)
    REFERENCES TBL_DEP(DEPID)
);

然后增加一些测试数据

不用模式的代码如下:

//描述用户数据的对象
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;
    }
}
//实现示例要求的功能
public class UserManager {
    public List<UserModel> getUserByDepId(String depId) {
        List<UserModel> list = new ArrayList<UserModel>();
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = this.getConnection();
            String sql = "select * from tbl_user u,tbl_dep d "
                +"where u.depId=d.depId and d.depId like ?";

            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, depId+"%");

            rs = pstmt.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"));

                list.add(um);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != rs) rs.close();
            if (pstmt != rs) pstmt.close();
            if (conn != rs) conn.close(); 
        }
    }
    //获取与数据库的连接
    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();
        List<UserModel> list = userManager.getUserByDepId("0101");
        System.out.println(list);
    }
}

存在的问题:
上面的实现看起来很简单,功能也正确,但是蕴含一个较大的问题,那就是:当一次性访问的数据条数过多,而且每条描述的数据量又很大的话,那会消耗较多的内存。
对于用户表,事实上是有很多字段的,不仅仅是示例的那么几个,再加上不使用翻页,一次性访问的数据就可能会有很多条。如果一次性需要访问的数据较多的话,内存开销会比较大。
而且从客户使用的角度来说,有很大的随机性,客户既可能访问每一条数据,也可能一条都不访问。也就是说,一次性访问很多条数据,消耗了大量内存,但是很可能是浪费掉了,客户根本就不会去访问那么多数据,对于每条数据,客户只需要看看姓名而已。
那么该怎么实现,才能即把多条用户数据的姓名显示出来,而又能节省内存空间,当然还要实现在客户想要看到更多数据的时候,能正确访问到数据呢?

使用模式的解决方案

//定义用户数据对象的接口
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 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;
    }
}
//代理对象,代理用户数据对象
public class Proxy implements UserModelApi {
    //持有被代理的具体的目标对象
    private UserModel realSubject = null;
    //构造方法,传入被代理的具体的目标对象
    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();
            //设置重新转载的标志为true
            this.loaded = true;
        }
        return realSubject.getSex();
    }

    //重新查询数据库以获取完整的用户数据
    private void reload() {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = this.getConnection();
            String sql = "select * from tbl_user where userId=?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, realSubject.getUserId());
            ResultSet rs = pstmt.executeQuery();

            if (rs.next()) {
                realSubject.setDepId(rs.getString("depId"));
                realSubject.setSex(rs.getString("sex"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != rs) rs.close();
            if (pstmt != rs) pstmt.close();
            if (conn != rs) conn.close(); 
        }
    }
    //获取与数据库的连接
    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 {
    public List<UserModelApi> getUserByDepId(String depId) {
        List<UserModelApi> list = new ArrayList<UserModelApi>();
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = this.getConnection();
            String sql = "select u.userid,u.name from tbl_user u "
                +"where u.depId like ?";

            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, depId+"%");

            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"));

                list.add(proxy);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != rs) rs.close();
            if (pstmt != rs) pstmt.close();
            if (conn != rs) conn.close(); 
        }
    }
    //获取与数据库的连接
    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();
        List<UserModelApi> list = userManager.getUserByDepId("0101");
        //如果只是显示用户名称,那么不需要重新查询数据库
        for (UserModelApi umApi : list) {
            System.out.println("用户姓名:"+umApi.getUserName());
        }
        //如果访问非用户编号和用户姓名外的属性,那就会重新查询数据库
        for (UserModelApi umApi : list) {
            System.out.println("用户年龄:"+umApi.getSex());
        }
    }
}

此方法典型的以时间换空间的原理

Hibernate的延迟加载实现机制是动态代理

理解代理模式:
认识代理模式
1:代理模式的功能
代理模式是通过创建一个代理对象,用这个代理对象去代表真实的对象,客户端得到这个代理对象过后,对客户端没有什么影响,就跟得到了真实对象一样来使用。
当客户端操作这个代理对象时,实际上功能最终还是会由真实的对象来完成,只不过是通过代理操作的,也就是客户端操作代理,代理操作真正的对象。
正是因为有代理夹在客户端和被代理的真实对象中间,相当于一个中转,那么在中转的时候就有很多花招可以玩,比如:判断一下权限,如果没有足够的权限那就不给你中转了,等等。
2:代理的分类
(1)虚代理:根据需要来创建开销很大额对象,该对象只有在需要的时候才会被真正创建
(2)远程代理:用来在不同的地址空间上代表同一个对象,这个不同的地址空间可以是在本机,也可以在其他机器上,在java里面最典型的就是RMI技术
(3)copy-on-write代理:在客户端操作的时候,只有对象确实改变了,才会真的拷贝(或克隆)一个目标对象,算是虚代理的一个分支
(4)保护代理:控制对原始对象额访问,如果有需要,可以给不同的用户提供不同的访问权限,以控制他们对原始对象的访问
(5)Cache代理:为那些昂贵的操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果
(6)防火墙代理:保护对象不被恶意用户访问和操作
(7)同步代理:使多个用户能够同时访问目标对象而没有冲突
(8)智能指引:在访问对象是执行一些附加操作,比如:对指向实际对象的引用计数、第一次引用一个持久对象时,将它装入内存等

在这些代理类型中,最常见的是:虚代理、保护代理、远程代理和智能指引这几种。我们主要来学习虚代理和保护代理,这是实际开发中使用频率最高的。
3:具体目标和代理的关系
从代理模式的结构图来看,好像是有一个具体目标类就有一个代理类,其实不是这样的。如果代理类能完全通过接口来操作它所代理的目标对象,那么代理对象就不需要知道具体的目标对象,这样就无须为每一个具体目标类都创建一个代理类了(接口的好处 )。但是,如果代理类必须要实例化它代理的目标对象,那么代理类就必须知道具体被代理的对象,这种情况下,一个具体目标类通常会有一个代理类。这种情况多出现在虚代理的实现里面。

保护代理:
保护代理是一种控制对原始对象访问的代理,多用于对象应该有不同的访问权限的时候。保护代理会检查调用者是否具有请求所必须的访问权限,如果没有相应的权限,那么就不会调用目标对象,从而实现对目标对象的保护。
1:示例需求
现在有一个订单系统,要求是:一旦订单被创建,只有订单的创建人才可以修改订单中的数据,其他人不能修改。
相当于现在如果有了一个订单对象实例,那么就需要控制外部对它的访问,满足条件的可以访问,而不满足条件的就不能访问了。

public interface IOrder {
    public String getProductName();
    public void setProductName(String productName, String user);
    public int getOrderNum();
    public void setOrderNum(int orderNum, String user);
    public String getOrderUser();
    public void setOrderUser(String orderUser, String user);
}
public class Order implements IOrder {
    //订单订购的商品名称
    private String productName;
    //订单订购的商品数量
    private int orderNum;
    //订单创建人
    private String 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 ProxyOrder implements IOrder {
    //持有被代理的对象的引用
    private Order realObject;

    public ProxyOrder(Order realObject) {
        this.realObject = realObject;
    }

    public int getOrderNum() {
        return realObject.getOrderNum();
    }

    public String getOrderUser() {
        return realObject.getOrderUser();
    }

    public String getProductName() {
        return realObject.getProductName();
    }

    public void setOrderNum(int orderNum, String user) {
        if (realObject.getOrderUser().equals(user)) {
            realObject.setOrderNum(orderNum, user);
        } else {
            System.out.println("对不起," + user + ",您无法修改订单!");
        }
    }

    public void setOrderUser(String orderUser, String user) {
        if (realObject.getOrderUser().equals(user)) {
            realObject.setOrderUser(orderUser, user);
        } else {
            System.out.println("对不起," + user + ",您无法修改订单!");
        }
    }

    public void setProductName(String productName, String user) {
        if (realObject.getOrderUser().equals(user)) {
            realObject.setProductName(productName, user);
        } else {
            System.out.println("对不起," + user + ",您无法修改订单!");
        }
    }

}
public class Client {
    public static void main(String[] args) {
        /*//张三先登录系统创建了一个订单
        Order order = new Order("鸡蛋", 10, "张三");

        //李四登录系统,修改张三创建的那个订单
        order.setOrderNum(20, "李四");*/

        //张三先登录系统创建了一个订单
        IOrder proxyOrder = new ProxyOrder(new Order("egg", 10, "张三"));

        //李四登录系统,修改张三创建的那个订单
        proxyOrder.setOrderNum(20, "李四");  //出错!
    }
}

java中的代理
1:java的静态代理
通常把前面自己实现的代理模式,称为java的静态代理。这种实现方式有一个较大的缺点,就是如歌Subject接口发生变化,那么代理类和具体的目标实现都要变化,不是很灵活

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值