Android面试题: 动态代理和代理模式
android面试题: 动态代理和代理模式。
1. 代理模式(proxy pattern)
代理模式根据不用的职责和应用场景有很多种形式,例如远程代理,虚拟代理,保护代理,防火墙代理、智能引用代理、缓存代理、同步代理、复杂隐藏代理、写入复制代理等。
但是它们都有一个共同的特点,为其它对象提供一种代理以控制对这个对象的访问。
1.1 和装饰者模式的区别
一般情况下,我们容易将装饰者模式和代理模式搞混。
- 装饰者模式:是在不使用继承,不改变原有对象的情况下增加或扩展对象行为,但是并不会禁用你对象的某个行为。
- 代理模式:而代理模式是控制这个对象的访问。
例如我们看看下面这个虚拟代理的例子:
当一个对象的创建开销很大,我们想要在真正使用到的时候才真正创建这个对象的时候,就可以用到虚拟代理。
interface image { public void displayimage(); }
class realimage implements image { private string filename; public realimage(string filename) { this.filename = filename; loadimagefromdisk(); } private void loadimagefromdisk() { system.out.println("loading " + filename); } public void displayimage() { system.out.println("displaying " + filename); } }
class proxyimage implements image { private string filename; private image image; public proxyimage(string filename) { this.filename = filename; } public void displayimage() { if(image == null) image = new realimage(filename); image.displayimage(); } }
class proxyexample { public static void main(string[] args) { image image1 = new proxyimage("hires_10mb_photo1"); image image2 = new proxyimage("hires_10mb_photo2"); image1.displayimage(); // loading necessary image2.displayimage(); // loading necessary } }
例如proxyimage,我们隐藏了realimage中的loadimagefromdisk()方法,创建proxyimage对象并不会立刻创建realimage对象,实现了对象的懒加载。
这和装饰者模式是有些出入的,它们的职责和目的不一样。装饰模式主要是强调对对象的功能扩展,而代理模式虽然也可以扩展对象的功能,但更偏向于对象的访问限制。
2. 动态代理
假设有这么一个person类。
public interface person { string getname(); void setname(string name); string getgender(); void setgender(string gender); int getage(); void setage(int age); }
public class personimpl implements person { private string name; private string gender; private int age; @override public string getname() { return name; } @override public void setname(string name) { this.name = name; } @override public string getgender() { return gender; } @override public void setgender(string gender) { this.gender = gender; } @override public int getage() { return age; } @override public void setage(int age) { this.age = age; } }
如果我们想记录这个person对象的信息修改次数怎么办呢?
我们是不是可以弄一个装饰类扩展一下。
public class decorperson implements person { private personimpl person; private int modifiedtimes; public decorperson(personimpl person) { this.person = person; } @override public string getname() { return person.getname(); } @override public void setname(string name) { modifiedtimes++; person.setname(name); } @override public string getgender() { return person.getgender(); } @override public void setgender(string gender) { modifiedtimes++; person.setgender(gender); } @override public int getage() { return person.getage(); } @override public void setage(int age) { modifiedtimes++; person.setage(age); } public int getmodifiedtimes() { return modifiedtimes; } }
如果person类拥有非常非常多setxxx()方法,为每一个setter方法都增加代码会不会非常繁琐。或者当我们再给person类添加一个job属性时,这个decorperson是不是也要跟着修改呢。
所以java就提供了一个代理类给我们使用java.lang.reflect.proxy,可以通过proxy.newproxyinstance(classloader loader, class[] interfaces, invocationhandler h)这个方法创建代理对象。
首先我们需要实现一个invocationhandler对象。
public class personinvocationhandler implements invocationhandler { private person person; private int modifiedtimes; public personinvocationhandler(person person) { this.person = person; } public int getmodifiedtimes() { return modifiedtimes; } @override public object invoke(object proxy, method method, object[] args) throws throwable { if (method.getname().startswith("set")) { modifiedtimes++; } return method.invoke(person, args); } }
紧接着我们通过proxy类创建person的代理对象。
public class main { public static void main(string[] args) { // write your code here personinvocationhandler handler = new personinvocationhandler(new personimpl()); person person = (person) proxy.newproxyinstance( person.class.getclassloader(), new class[]{person.class}, handler); person.setage(17); person.setname("aitsuki"); person.setgender("male"); person.setage(18) system.out.println(handler.getmodifiedtimes()); // 最终打印结果为 4 } }
当然,如果我们需要隐藏person的来历,我们可以提供一个工厂用来创建person,让用户感觉不到这其实是一个代理对象。
public class personfactory { public static person getperson() { personinvocationhandler handler = new personinvocationhandler(new personimpl()); return (person) proxy.newproxyinstance( person.class.getclassloader(), new class[]{person.class}, handler); } }
上面演示了使用proxy类实现动态代理的方式。那么现在就来说说动态代理的概念:
动态代理,严格意义上来说指的并不是一种代理模式,而是一种反射的运用技术,它可以在程序运行时通过反射创建一个目标接口的代理对象,是实现代理模式的一种途径。
如果你非要说动态代理是一种设计模式,它反而更像是装饰者模式或监听器模式,因为它是监听目标对象的方法调用,实现对某个方法的扩展。从而提高程序的扩展性和灵活性,以及是java实现aop(面向切面)的一个重要技术。
那么相对于动态代理就出现了“静态代理”这个词了,指的就需要在级别中维护一个代理类,当然,“静态代理”也并不是指一种代理模式了。
aspect-oriented-programming">3. 面向切面编程(aop–aspect oriented programming)
既然说到了动态代理,那么再简单说说面向切面编程。
面向切面编程可以看作是对面向对象编程的扩展,“切面”,我们可以理解为“关注点”。
例如上面的person对象,它的主要关注点就是对象的操作,比如修改名字,性别等。而它的横切关注点则是日志记录,比如记录对象的修改次数。
但是如果直接在person的实现类中直接写入日志操作的相关代码,那么日志系统就和person产生了耦合,需要将日志记录这部分逻辑给单独的提取出来,而java中,动态代理则是面向切面编程的一个重要的实现手段。
上一篇: 一般的结婚戒指多少钱
下一篇: 闹离婚分居后挽回办法有哪些?