设计模式——代理模式与装饰器模式

代理模式

解决的问题:在直接访问对象时带来很大的开销。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层

代理模式就相当于Windows 里面的快捷方式,它并不实现什么功能,而只是在中间加以控制;而装饰器模式为了增强功能。

Java中的典型示例:静态代理:hibernate中的session.load()方法;动态代理:SpringAOP

代码实现:

1> 创建一个接口:Image.java

public interface Image {
   void display();
}

2>创建两个接口的实现类

RealImage.java(假设该类实例化开销很大)

public class RealImage implements Image {

   private String fileName;

   public RealImage(String fileName){
      this.fileName = fileName;    
      loadFromDisk(fileName);    // 当实例化RealImage类的时候,会执行这个方法,打印输出语句
   }

   @Override
   public void display() {  // 重写父类的display方法
      System.out.println("Displaying " + fileName);
   }

   private void loadFromDisk(String fileName){
      System.out.println("Loading " + fileName);
   }
}

ProxyImage.java(代理类)

public class ProxyImage implements Image{

   private RealImage realImage;
   private String fileName;

   public ProxyImage(String fileName){    // 实例化时并不直接实例化RealImage,而是实例化这个类(这样就由类代理了RealImage的display方法)
      this.fileName = fileName;    
   }

   @Override
   public void display() {    // 当调用display方法时,实例化RealImage,然后再调用display方法
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.display();
   }
}

3>当被请求时,使用 ProxyImage 来获取 RealImage 类的对象。

public class ProxyPatternDemo {
    
   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg");    // 实例化代理类

      image.display();       调用方法
   }
}

静态代理与动态代理

静态代理:

静态代理就是我们上面写的内种方法,它的优缺点也很明显:

优点:

代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合)。

缺点:

1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。

即静态代理类只能为特定的接口服务。如想要为多个接口服务则需要建立很多个代理类

动态代理:

根据如上的介绍,你会发现每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类

所以我们就会想办法可以通过一个代理类完成全部的代理功能,那么我们就需要用动态代理

在上面的示例中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象

运行时动态的在内存中(那肯定和反射有关)创建代理对象

好处是:可以不用改动态代理的代码,自动生成要求对象的代理对象

在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持

@Test
public void test1(){
	//获得动态的代理对象----在运行时 在内存中动态的为Target创建一个虚拟的代理对象
	//objProxy是代理对象 根据参数确定到底是谁的代理对象
	TargetInterface objProxy = (TargetInterface) Proxy.newProxyInstance(
			Target.class.getClassLoader(), //与目标对象相同的类加载器
			new Class[]{TargetInterface.class}, 
			new InvocationHandler() {
				//invoke 代表的是执行代理对象的方法
				//method:代表目标对象的方法字节码对象
				//args:代表目标对象的响应的方法的参数
				public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
					System.out.println("目标方法前的逻辑");
					//通过反射执行目标对象的方法
					Object invoke = method.invoke(new Target(), args);
					System.out.println("目标方法后的逻辑");
					//retrun返回的值给代理对象
					return invoke;
				}
			}
		);
	
	objProxy.method1();
	String method2 = objProxy.method2();
	System.out.println(method2);
	
}

装饰器模式

向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

作用:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

装饰器模式相当于孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点:多层装饰比较复杂。

实现步骤:

1、增强类与被增强的类要实现统一接口

2、在增强类中传入被增强的类

3、需要增强的方法重写 不需要增强的方法调用被增强对象的

示例:解决request.getParameter()方法获取的中文乱码问题

public class EncodingFilter implements Filter{

	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		
		//被增强的对象
		HttpServletRequest req = (HttpServletRequest) request;
		//增强对象
		EnhanceRequest enhanceRequest = new EnhanceRequest(req);
		
		
		chain.doFilter(enhanceRequest, response);
		
	}


}

class EnhanceRequest extends HttpServletRequestWrapper{		// 1>与要增强的类(HttpServletRequest类)继承/实现同一个类/接口
	
	private HttpServletRequest request;

	public EnhanceRequest(HttpServletRequest request) {		// 2>传入要增强的类
		super(request);
		this.request = request;
	}
	
	//3>对要增强的方法(getParaameter)重写
	@Override
	public String getParameter(String name) {
		String parameter = request.getParameter(name);//乱码
		try {
			parameter = new String(parameter.getBytes("iso8859-1"),"UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return parameter;
	}
	
}

两者之间的区别***

装饰器模式关注于在一个对象上动态的添加方法(可以说他是继承的一个替代方案),然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

原文地址:https://www.cnblogs.com/x54256/p/8486706.html