设计模式-代理模式

代理模式:

      为其他对象提供一种代理以控制对这个对象的访问,说白了就是,在一些情况下客户不想或者不能直接引用一个对象,而代理对象可以在客户和目标对象之间起到中介作用,去掉客户不能看到的内容和服务或者增添客户需要的额外服务。


为什么使用:

     1.授权机制,不同级别的用户对同一对象拥有不同的访问权利,如Jive论坛系统中,就使用Proxy进行授权机制控制,访问论坛有两种人:注册用户和游客(未注册用户),Jive中就通过类似ForumProxy这样的代理来控制这两种用户对论坛的访问权限.

      2.某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动.
         举例两个具体情况:
         (1)如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待 大图片处理完成,这时需要做个图片Proxy来代替真正的图片.
         (2)如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象.

      总之原则是,对于开销很大的对象,只有在使用它时才创建,这个原则可以为我们节省很多宝贵的Java内存. 所以,有些人认为Java耗费资源内存,我以为这和程序编制思路也有一定的关系.


什么时候使用:

     在对已有的方法进行使用的时候出现需要对原有方法进行改进或者修改,这时候有两种改进选择:修改原有方法来适应现在的使用方式,或者使用一个“第三者”方法来调用原有的方法并且对方法产生的结果进行一定的控制。第一种方法是明显违背了“对扩展开放、对修改关闭”(开闭原则),而且在原来方法中作修改可能使得原来类的功能变得模糊和多元化(就像现在企业多元化一样),而使用第二种方式可以将功能划分的更加清晰,有助于后面的维护。所以在一定程度上第二种方式是一个比较好的选择!


生活例子:

     我们去科技市场为自己的机器添加点奢侈的配件,很多DIYer都喜欢去找代理商,因为在代理商那里拿到的东西不仅质量有保证,而且价格和售后服务上都会好很多。客户通过代理商得到了自己想要的东西,而且还享受到了代理商额外的服务;而生产厂商通过代理商将自己的产品推广出去,而且可以将一些销售服务的任务交给代理商来完成(当然代理商要和厂商来共同分担风险,分配利润),这样自己就可以花更多的心思在产品的设计和生产上了。


分类:

      代理模式分为8种,这里将几种常见的、重要的列举如下:

  1. 远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。比如:你可以将一个在世界某个角落一台机器通过代理假象成你局域网中的一部分。

  2.虚拟(Virtual)代理:根据需要将一个资源消耗很大或者比较复杂的对象延迟的真正需要时才创建。比如:如果一个很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,这个大图片可能就影响了文档的阅读,这时需要做个图片Proxy来代替真正的图片。

  3. 保护(Protect or Access)代理:控制对一个对象的访问权限。比如:在论坛中,不同的身份登陆,拥有的权限是不同的,使用代理模式可以控制权限(当然,使用别的方式也可以实现)。

  4. 智能引用(Smart Reference)代理:提供比对目标对象额外的服务。比如:纪录访问的流量(这是个再简单不过的例子),提供一些友情提示等等。

  代理模式是一种比较有用的模式,从几个类的“小结构”到庞大系统的“大结构”都可以看到它的影子。


结构

      代理模式中的“代理商”要想实现代理任务,就必须和被代理的“厂商”使用共同的接口(你可以想象为产品)。所以自然而然你会想到在java中使用一个抽象类或者接口(推荐)来实现这个共同的接口。于是代理模式就有三个角色组成了:

  1.抽象主题角色:声明了真实主题和代理主题的共同接口。

  2.代理主题角色:内部包含对真实主题的引用,并且提供和真实主题角色相同的接口。

  3.真实主题角色:定义真实的对象。

  使用类图来表示下三者间的关系如下:

深入浅出基于Java的代理设计模式

      当然,图上所示的是代理模式中的一个具体情况。而代理模式可以非常灵活的使用其他方式来实现,这样就与图上所示有很大的区别。


静态代理模式:

一个简单的例子:


/**
 * 电脑批发商。
 */
public interface Computer {
	public void buy(String name);
}

/**
 * 联想电脑公司。
 */
public class Lenovo implements Computer {
	@Override
	public void buy(String name) {
		System.out.println(name + "联想公司产品");
	}
}

/**
 * 三星电脑公司。
 */
public class SamSung implements Computer {
	@Override
	public void buy(String name) {
		System.out.println(name + "三星公司产品");
	}
}

/**
 * 电脑销售代理商。
 */
public class ComputerProxy implements Computer {
	
	private Computer computer;
	
	public ComputerProxy(Computer computer) {
		this.computer = computer;
	}
	
	@Override
	public void buy(String name) {
		System.out.println("before method invoking");
		computer.buy(name);
		System.out.println("after method invoking");
	}
}

/**
 * 买电脑的客户,一个买三星电脑,一个买联想电脑。 
 */
public class BuyComputer {
	public static void main(String[] args) {
		Computer proxy1 = new ComputerProxy(new SamSung());
		proxy1.buy("我想买一台三星电脑");
		Computer proxy2 = new ComputerProxy(new Lenovo());
		proxy2.buy("我想买一台联想电脑");
	}
}



运行结果:

before method invoking
我想买一台三星电脑三星公司产品
after method invoking
before method invoking
我想买一台联想电脑联想公司产品
after method invoking

 

在上例中,Lenovo.java和SamSung.java是真正实现buy方法的类,ComputerProxy为代理类,Computer为他们共同的基类。

ComputerProxy里面并没有创建新的方法,它根据传入对象的类型(Lenovo或者SamSung)来决定到底是用Lenovo里面的buy方法,还是SamSung里面的buy方法,它充当的是Lenovo和SamSung的前台,也由此对应了代理模式的定义:Fronting for another object

 

动态代理模式:

         前面一个文章里的代码很简单(只是让大家了解什么是代理),实现的是静态代理,做为电脑代理商的ComputerProxy,在电脑行业 为电脑生产商(三星,联想)和客户提供服务,提供各种方便。
        郁闷的是,如果我现在增加一个行业,比如下面要讲到的Car汽车行业,那么,我们只能增加一个代理了,也就是说我们要再写一个CarProxy代码,我们现在假设我们有很多个行业,那么,无疑我们的工作量开始大了,有没有什么办法让我们的代理商实现跨行业代理呢?
        答案是:可以。这就是我们这里讲的动态代理产生存在的意义了。

 

如果你要增加一个汽车行业,则会增加以下代码:

 

/**
 * 汽车批发商。
 */
public interface Car {
	public void buy(String name);
}

/**
 * 劳斯莱斯批发商。
 */
public class RollsRoyce implements Car {
	public void buy(String name) {
		System.out.println(name + "劳斯莱斯公司产品");
	}
}

/**
 * 汽车销售代理商。
 */
public class CarProxy implements Car {
	
	private Car car;
	
	public CarProxy(Car car) {
		this.car = car;
	}
	
	@Override
	public void buy(String name) {
		System.out.println("before method invoking");
		car.buy(name);
		System.out.println("after method invoking");
	}
}



如果有很多行业的话,代理类(上面例子中的CarProxy)也会很多,工作量变大的同时,也带来了可维护性的问题。如果用动态代理的话,可以很方便的解决这个问题。

下面我们在上面静态代理模式例子的基础上,增加一个汽车行业。

完整的代码如下:


/**
 * 电脑批发商。
 */
public interface Computer {
	public void buy(String name);
}

/**
 * 联想电脑公司。
 */
public class Lenovo implements Computer {
	@Override
	public void buy(String name) {
		System.out.println(name + "联想公司产品");
	}
}

/**
 * 三星电脑公司。
 */
public class SamSung implements Computer {
	@Override
	public void buy(String name) {
		System.out.println(name + "三星公司产品");
	}
}

/**
 * 汽车批发商。
 */
public interface Car {
	public void buy(String name);
}

/**
 * 劳斯莱斯批发商。
 */
public class RollsRoyce implements Car {
	public void buy(String name) {
		System.out.println(name + "劳斯莱斯公司产品");
	}
}

public class AllProxy implements InvocationHandler {
	
	private Object object;
	
	public Object bind(Object object) {
		this.object = object;
		
		return Proxy.newProxyInstance(object.getClass().getClassLoader(), 
				object.getClass().getInterfaces(), this);
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("before method invoking");
		Object result = method.invoke(object, args);
		System.out.println("after method invoking");
		return result;
	}
}

public class BuyAll {
	public static void main(String[] args) {
		AllProxy allProxy = new AllProxy();
		Computer proxy1 = (Computer)allProxy.bind(new SamSung());
		proxy1.buy("SamSung");
		
		Computer proxy2 = (Computer)allProxy.bind(new Lenovo());
		proxy2.buy("Lenovo");
		
		Car proxy3 = (Car)allProxy.bind(new RollsRoyce());
		proxy3.buy("RollsRoyce");
	}
}

运行结果:

before method invoking
SamSung三星公司产品
after method invoking
before method invoking
Lenovo联想公司产品
after method invoking
before method invoking
RollsRoyce劳斯莱斯公司产品
after method invoking

 

动态代理模式中,只需要一个代理类(AllProxy)就完成了所有的代理工作,减少代码。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值