需求
小明一直暗恋着小花,想了很久,下定决心要向她表白,对她说出我爱你 ?
鉴于笔记整理需要,将原文的故事背景进行改编。
初步实现
-
妹子类:
public class Meizi { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
汉子类:
public class Hanzi { private Meizi meizi; public Hanzi(Meizi meizi) { this.meizi = meizi; } public void sayLoveToHer() { System.out.println("I love you, " + meizi.getName() + "!"); } }
-
客户端类:
public class Client { public static void main(String[] args) { Meizi xiaohua = new Meizi(); xiaohua.setName("小花"); Hanzi xiaoming = new Hanzi(xiaohua); xiaoming.sayLoveToHer(); } }
-
运行结果:
分析
大家都知道,现实情况小明很有可能有点害羞,不好意思直接对小花说我爱你。所以,小明想了个办法,决定让小红(小花的闺密)转述,简单来说,小红是个“僚机”。那么,这样的实现方式小红充当一个代理。
代理模式
-
定义:为其他对象提供一种代理以控制对这个对象的访问。通过引入一个新的对象,来实现对真实对象的操作或者将新的对象作为真实对象的一个替身。即代理对象。
-
结构示意图:
代理模式代码实现如下:
-
Subject类:定义了RealSubject和Proxy的公用接口,这样就可以在任何使用RealSubject的地方使用Proxy;
public abstract class Subject{ public abstract void request(); }
-
RealSubject类:定义了Proxy所代表的真实实体;
public class RealSubject extends Subject{ public void request(){ System.out.println("真实的请求"); } }
-
Proxy类:保存了一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这种代理就可以用来替代实体;
public class Proxy extends Subject{ private RealSubject realSubject; public void request(){ if(realSubject != null) realSubject = new RealSubject(); realSubject.request(); } }
-
客户端代码:
public class Client { public static void main(String[] args) { Proxy proxy = new Proxy(); proxy.request(); } }
再次实现
了解代理模式的实现过程,我们很容易写出运用代理模式的案例实现。小红作为代理对象,代替小明去表白,所以表白是俩者共同的功能接口,小明和小红都要去实现这个功能。
-
定义接口:表白
public interface IExpress { void sayLoveToHer(); }
-
汉子实现接口;
public class Hanzi implements IExpress{ private Meizi meizi; public Hanzi(Meizi meizi) { this.meizi = meizi; } @Override public void sayLoveToHer() { System.out.println("I love you, " + meizi.getName() + "!"); } }
-
代理类实现接口;
public class Proxy implements IExpress { private Hanzi hanzi; public Proxy(Hanzi hanzi) { this.hanzi = hanzi; } @Override public void sayLoveToHer() { System.out.println("昨天,有个汉子和我说:"); hanzi.sayLoveToHer(); } }
-
客户端。
public class Client { public static void main(String[] args) { Meizi xiaohua = new Meizi(); xiaohua.setName("小花"); Proxy xiaohong = new Proxy(new Hanzi(xiaohua)); xiaohong.sayLoveToHer(); } }
-
运行结果
再次分析
可能已经发现了,代理对象小红如实的转达了小明的意思,那么如果小明和小红关系很好,可能小红会在小花面前多美言几句,这其实就是代理模式可以增强原本类的功能。例如,在JavaEE中,我们会通过Spring来管理Hibernate的事务,我们并没有去写开启事务、关闭事务的语句,但其实这一切都在背后帮我们做了,我们仅仅需要在Spring的配置文件中去配置好Hibernate的事务管理,其实这背后就是代理模式。
深入代理模式
上面所实现的是一般的静态代理模式,下面介绍一下JDK动态代理:
动态代理和上面静态代理实现区别就是少一个代理类Proxy的实现,而在客户端动态实现功能。
因此,可以看出静态代理在如下情况显得捉襟见肘:
- 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
- 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
JDK动态代理实现
因为创建出来的这个代理类,一定是接口的子类,所以JDK动态代理一定要有接口,并且真实的业务类要实现该接口。
-
客户端代码:
public class Client { public static void main(String[] args) { Meizi xiaohua = new Meizi(); xiaohua.setName("小花"); IExpress xiaoming = new Hanzi(xiaohua); IExpress xiaohong = (IExpress) Proxy.newProxyInstance(xiaoming.getClass().getClassLoader(), xiaoming.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("sayLoveToHer")){ System.out.println("昨天,小明和我说:"); Object obj = method.invoke(xiaoming, args); System.out.println("我觉得很不错,答应他吧!"); return obj; } return method.invoke(proxy, args); } }); xiaohong.sayLoveToHer(); } }
-
运行结果:
最后
动态代理还有Cglib动态代理,在Spring中经常用到。Spring这个框架运用到的设计模式确实非常多,这里推荐前几日掘金上的一篇文章,关于代理模式:JAVA中的静态代理、动态代理以及CGLIB动态代理