代理模式,另外一个名称叫委托模式,下面我从代理购票的例子来说说我的个人看法。
火车站购票代理门面店,被称为代理对象(A),个体对象是被代理对象(P),这个P过年回家需要买票,可以买火车票也可以是汽车票,也可以飞机票,P要做的事情就是回家,回家有多种方式,那么回家的方式是接口I,代理对象A也肯定实现了购买车票的方式,如果少了一样没有实现,估计这店很难继续开下去了,
好了,所有的条件都符合了,这时店面可以营业了,这时来了一个人,当然这个人就是实例化过的对象实例了,必须是person了,这个被代理对象的实例,肯定有身份证号,当然我们称没有身份证号的实例,不能叫person人,姑且这样确定.
package cn.coral.proxy;
/**
*/
public interface TicketBuy {
/**
* @Method: buyTrainTicket
* @Description: 购买火车票
* @param 参数
* @return void 返回类型
* @throws
*/
public void buyTrainTicket();
/**
* @Method: buyBusTicket
* @Description: 购买汽车票
* @param 参数
* @return void 返回类型
* @throws
*/
public void buyBusTicket();
/**
* @Method: buyAirTicket
* @Description: 购买飞机票
* @param 参数
* @return void 返回类型
* @throws
*/
public void buyAirTicket();
}
package cn.coral.proxy;
public class Agent implements TicketBuy{
private TicketBuy proxy;
/**
* @Method: Agent
* @Description: 只有拿到用户的身份证号,才能进行买票,当然还有cash哦,这里只要一个条件吧。所以购票点就只是代理,有台能上网的电脑而已
* @param @param userId 参数
* @return void 返回类型
* @throws
*/
public Agent(String userId){
this.proxy = new Person(userId);
}
@Override
public void buyAirTicket() {
System.out.println("飞机票要登记安检信息,查看身份证号码是否是通缉犯");
proxy.buyAirTicket();
}
@Override
public void buyBusTicket() {
proxy.buyBusTicket();
}
@Override
public void buyTrainTicket() {
proxy.buyTrainTicket();
System.out.println("火车票买完了要登记购票日志记录");
}
}
package cn.coral.proxy;
public class Person implements TicketBuy{
private String userId;//身份证号
public Person(String userId){
this.userId = userId;
}
@Override
public void buyAirTicket() {
System.out.println(userId+"购买了飞机票!\n");
}
@Override
public void buyBusTicket() {
System.out.println(userId+"购买了汽车票!\n");
}
@Override
public void buyTrainTicket() {
System.out.println(userId+"购买了火车票!\n");
}
}
那么这个实例,必须拿出身份证,交给代理对象A,A拿到身份证后,可以购买汽车火车票了.买完结束了.整个过程就是代理模式了,
package cn.coral.proxy;
public class Test {
public static void main(String args[]){
Agent agent = new Agent("110");
/**
* 仔细的读者发现了,这里是调用agent的方法,但是真实执行的person的方法,是不是中间绕了一道.
* 代理模式重要特点之一:agent肯定有个可以访问实体,比如如何初始化这个实体,代理知道,外部调
* 用者无需知道.在spring中,更恰当的称呼叫注入一个实体.
* 代理模式重要特点之二:agent和person同时都实现了购票的接口.
*/
agent.buyAirTicket();
agent.buyTrainTicket();
agent.buyBusTicket();
}
}
飞机票要登记安检信息,查看身份证号码是否是通缉犯
110购买了飞机票!
110购买了火车票!
火车票买完了要登记购票日志记录
110购买了汽车票!
分析:代理对象本身,并没有购买火车票,因为门面这个店,虽然有钱,它没有身份证,所以不具备购买汽车票火车票的条件,这个很好理解的,只有了身份证号,才能购买,因此这里的身份证号是关键因素(汽车票除外,现在还没有被实名制绑定),火车票和飞机票正常。
特点在于:代理类与被代理类实现的是同一个接口,因此代理类与被代理类的结构是相同的;
抽象角色:声明真实对象和代理对象的共同接口。(I)
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作(这里比如,发现身份证号码是个A级通缉犯,买票成功不说(必要时锁定该对象),反正是一件不好的事情,要报警的,或者要缴纳一些代理费用,购票有抽奖活动,这时P对象还是购票的功能,那么抽奖活动交给代理对象即可),相当于对真实对象进行封装。
例如:接口A有一个接口方法operator(),真实角色:RealA实现接口A,则必须实现接口方法operator()。客户端Client调用接口A的接方法operator()。现在新需求来了,需要修改RealA中的operator()的操作行为。怎么办呢?如果修改RealA就会影响原有系统的稳定性,还要重新测试。这是就需要代理类实现附加行为操作。创建代理ProxyA实现接口A,并将真实对象RealA注入进来。ProxyA实现接口方法operator(),另外还可以增加附加行为,然后调用真实对象的operator()。从而达到了“对修改关闭,对扩展开放”的原则.
从而达到了“对修改关闭,对扩展开放”,保证了系统的稳定性。我们看客户端Client调用仍是接口A的接口方法operator(),只不过实例变为了ProxyA类了而已。也就是说代理模式实现了ocp原则。
这里想多说两句:代理对象的智能引用,可以对一个身份证号,查看本年度累计多少次购票,指定的列车票如果卖完了,可以推荐其他的回家路线,在实际编码中,可能是添加一个记录日志功能,操作方法权限校验,有权限了,进行权限过滤等等.这是代理对象的智能指引场景应用,本实例不能包含所有的场景,但是比起网页图片延迟加载,代理卖毛巾的超市,专卖店,代理商的啥具体一点。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。这里就是P。
接下来我想说说实际应用中的场景:
1)单点登录,在现在很多的网站中,都嵌入了qq互联,微信互联等等登录方式。本身系统可能也有用户名登录,手机号登录,身份证号登录等方式,加上第三方认证登录,恐怕有小十种登录方式要处理。这个案例理解起来应该非常之简单了。吾辈在面试的时候,的确没有考虑到太多,导致这个问题的答案出现了偏差,这里汗一个。首先看行为抽象,都是登录,这是很关键的,都做的是同一件事情,因为都是登录所以有很多共性存在,比如记录登录日志,会员日访问量统计等等。但是登录方式有很多种,这里对应的就是接口实现了。对于自身的系统,可能得到的手机号,用户名,而对于qq互联得到认证后,如qq号,微信号等等,后面的事情就是该干嘛干嘛了。这点延伸一下,就是在设计的时候考虑到第三方认证,比如一个集团上了5个系统,都是一家供应商服务的,就是5个登录入口了,从设计层面上考虑一下单点登录的方案是上上策。
2)数据分表查询,有兴趣的可以看看这个http://zhengdl126.iteye.com/blog/443993,用户信息表非常庞大,以用户名做hash把所有用户打散到不同的表,如取md5('小强')的首英文字母(hash方法很多,这边简单以md5示例)进行横向分表后假设创建如下表名:
users_a、users_b、users_c、users_d、.....、users_otherusers_a 设置主健基数 10000000
users_b 设置主健基数 20000000
users_c 设置主健基数 30000000
select * from users_a where username='小张';
select * from users_b where ID=21001234;
以上这些办法都能在分表后大幅提升数据库查询性能。