1.代理
1.1定义
代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
代理模式的定义说明:
-
引入一个新的代理对象
-
代理对象在客户端对象和目标对象之间起到中介的作用
-
去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务
1.2角色
代理模式包含以下3个角色:
- Subject(抽象主题角色)
- Proxy(代理主题角色)
- RealSubject(真实主题角色)
1.3优点
- 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度
- 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性
1.4缺点
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢(例如保护代理)
- 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂(例如远程代理)
1.5适用环境
- 当客户端对象需要访问远程主机中的对象时可以使用远程代理
- 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理
- 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理
- 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理
- 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理
1.6教学例子
(1)分析
-
代购商品:顾客 ——》 代购网站——》 商品
-
软件开发:客户端 ——》代理对象 ——》 真实对象
(2)类图
(3)代码
Subject(抽象主题角色)
public abstract class Mobile {
protected String type;
protected double price;
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public abstract void sellPhone();
}
Proxy(代理主题角色)
public class ProxyMobile extends Mobile {
private Mobile mobile;
public void setMobile(Mobile m){
this.mobile = m;
}
@Override
public void sellPhone() {
//中间商加价
mobile.setPrice(mobile.getPrice()*1.1);
mobile.sellPhone();
}
//中间商提供额外服务
public void tiemo(){
System.out.println("为手机贴膜。");
}
}
RealSubject(真实主题角色)
public class IphoneMobile extends Mobile{
public IphoneMobile(){
super.type="IphoneXXXXX";
super.price = 5000.0;
}
public void sellPhone(){
System.out.println("手机型号是:"+this.type+",售价为:"+this.price);
}
}
public class HuaweiMobile extends Mobile {
public HuaweiMobile(){
super.type="huaweiP30";
super.price = 5500.0;
}
@Override
public void sellPhone() {
System.out.println("手机型号是:"+this.type+",售价为:"+this.price);
}
}
Client(客户端 )
public class Client {
public static void main(String[] args) {
IphoneMobile iphonexxx = new IphoneMobile();
iphonexxx.sellPhone();
ProxyMobile proxyMobile = new ProxyMobile();
proxyMobile.setMobile(iphonexxx);
proxyMobile.sellPhone();
proxyMobile.tiemo();
Mobile hwPhone= new HuaweiMobile();
proxyMobile.setMobile(hwPhone);
proxyMobile.sellPhone();
proxyMobile.tiemo();
}
}
1.7实操案例
(1)问题:
某应用软件中需要记录业务方法的调用日志,在不修改现有业务类的基础上为每个类提供一个日记记录代理类,在代理类张输出日志,例如在业务方法method()调用之前输出“方法method()被调用,调用时间为2020-06-09 10:10:10”,调用之后如果没有抛出异常则输出“方法method()调用成功“否者输出”方法method() 调用失败。请选择一种合适的设计模式,画出类图并模拟实现。
(2)类图:
(3)代码(静态代理):
Subject(抽象主题角色)
public abstract class Subject {
public abstract void use();
}
Proxy(代理主题角色)
public class Proxy extends Subject {
private Subject subject;
public void setMethod(Subject subject) {
this.subject = subject;
}
/**
* @author Zhao-Benshan
* 代理对象类
*/
@Override
public void use() {
// TODO Auto-generated method stub
journal("use()");
try {
this.subject.use();
} catch (Exception e) {
// TODO: handle exception
tryCatch(true);
return;
}
tryCatch(false);
}
public void journal(String dy){
System.out.println("调用"+dy+"调用时间为:2020/06/16 17:12");
}
public void tryCatch(boolean err){
if(err){
System.out.println("调用失败");
}else{
System.out.println("调用成功");
}
}
}
RealSubject(真实主题角色)
public class Method extends Subject {
/**
* @author Zhao-Benshan
* 被代理对象
*/
public void use(){
System.out.println("这是一个使用方法");
};
}
Client(客户端)
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
Proxy p = new Proxy();
Method m = new Method();
p.setMethod(m);
p.use();
}
}
(4)代码(动态代理)
Subject(抽象主题角色)
public interface Subject {
public void use();
}
Proxy(代理主题角色)
public class ProxyMethod implements InvocationHandler {
//被代理者
Class<?> cls =null;
//被代理的实例
Object obj = null;
//我要代理谁
public ProxyMethod(Object _obj){
this.obj = _obj;
}
public void use() {
// TODO Auto-generated method stub
journal("use()");
try {
} catch (Exception e) {
// TODO: handle exception
tryCatch(true);
return;
}
tryCatch(false);
}
public void journal(String dy){
System.out.println("调用"+dy+"调用时间为:2020/06/16 17:12");
}
public void tryCatch(boolean err){
if(err){
System.out.println("调用失败");
}else{
System.out.println("调用成功");
}
}
//调用被代理的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
journal("use()");
Object result = null;
try {
result = method.invoke(obj, args);
} catch (Exception e) {
// TODO: handle exception
tryCatch(true);
return result;
}
tryCatch(false);
return result;
}
}
RealSubject(真实主题角色)
public class Method implements Subject {
/**
* @author Zhao-Benshan
* 被代理方法
*/
public void use(){
System.out.println("这是一个使用方法");
};
}
Client(客户端)
public class Client {
public static void main(String[] args) {
// TODO Auto-generated methler);
Subject sub = new Method();
InvocationHandler handler = new ProxyMethod(sub);
ClassLoader cl = sub.getClass().getClassLoader();
Subject p = (Subject)Proxy.newProxyInstance(cl,new Class[]{Subject.class},handler);
p.use();
}
}