更好的理解装饰设计模式和代理设计模式

更好的理解装饰设计模式和代理设计模式

最近在一个小项目中,出现装饰模式和代理模式。有时感觉它们很像。。好好整理了下,发现还是。。。


对装饰者和被装饰者都实现同一个接口,对代理模式来说,代理类和真实处理的类,都实现同一个接口,此外,不论我们使用的是哪一个接口,都可以很容易地在真实的对象的方法前面或者后面加上自定义的方法。然而,实际上装饰器模式关注在一个对旬上动态的添加方法,然而代理模式关注于控制对象的访问。通俗的说,就是代理类可以的它的客户隐藏一个对象的具体信息,因此,我们用代理模式的时候,我们常常 一个代表类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,通常我们做的是将原始对象作为一个参数传给装饰者的构造器。


下面通过这个过滤器看下装饰类是如何用的?

import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;


import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;


public class EncodingFilter implements Filter {




	private String encode=null;


	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		//拿到配置文件中的编码值
		this.encode=filterConfig.getServletContext().getInitParameter("encode");
	}
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		response.setContentType("text/html;charset="+encode);
		chain.doFilter(new MyHttpServletRequest((HttpServletRequest) request), response);
	}
	//装饰类,用来改变不某些不想要的方法,装饰类,装饰request对象,改造和获取请求参数相关的三个方法,改造成获取有乱码的值,解决乱码后返回
	// 这样一来,包装过后的request,虽然内部还是乱码,但是通过方法获取时,是解决过后没有乱码的值
	class MyHttpServletRequest extends HttpServletRequestWrapper{


		private HttpServletRequest request=null;
		private boolean EncodehasNode=true;
		//构造方法
		public MyHttpServletRequest(HttpServletRequest request) {
			super(request);
			this.request=request;
		}
		
		//复写三个方法
		 *//**
	     * Returns a java.util.Map of the parameters of this request. Request
	     * parameters are extra information sent with the request. For HTTP
	     * servlets, parameters are contained in the query string or posted form
	     * data.
	     * 
	     * @return an immutable java.util.Map containing parameter names as keys and
	     *         parameter values as map values. The keys in the parameter map are
	     *         of type String. The values in the parameter map are of type
	     *         String array.
	     *//*
		//这三个方法是ServeltRequest接口身上的抽象方法
		@Override
		public Map<String, String[]> getParameterMap() {
			


			try {
				if(request.getMethod().equals("POST")){
					//--POST提交,一行代码解决乱码,返回
					request.setCharacterEncoding(encode);								
					//return getParameterMap();
					return request.getParameterMap();
				}else if(request.getMethod().equals("GET")){
					//--GET提交获取有乱码的Map,手动编解码解决返回
					Map<String, String[]> map=request.getParameterMap();
					if(EncodehasNode){
						for(Entry<String, String[]> entry: map.entrySet() ){
							String [] vs=entry.getValue();
							for(int i=0;i<vs.length;i++){
								//解码
								//底层解码原理:
						
						/***static char[] decode(String charsetName, byte[] ba, int off, int len)
							        throws UnsupportedEncodingException
							    {
							        StringDecoder sd = deref(decoder);
							        String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
							        if ((sd == null) || !(csn.equals(sd.requestedCharsetName())
							                              || csn.equals(sd.charsetName()))) {
							            sd = null;
							            try {
							                Charset cs = lookupCharset(csn);
							                if (cs != null)
							                    sd = new StringDecoder(cs, csn);
							            } catch (IllegalCharsetNameException x) {}
							            if (sd == null)
							                throw new UnsupportedEncodingException(csn);
							            set(decoder, sd);
							        }
							        return sd.decode(ba, off, len);
							    } 
						 ****/
								vs[i]=new String(vs[i].getBytes("iso8859-1"),encode);
								
							}
						}
						//执行完第一次后,就把它置为false,因为第一次就都编译好了
						EncodehasNode=false;
					}
					return map;
				}else{
					
					//其他格式,就返回
					return getParameterMap();
				}
				
				
				
			} catch (Exception e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
			
			
		}
		
		@Override
		public String getParameter(String name) {
			//调用getParameterValues得到第0位
			String vs[]=getParameterValues(name);
			//判断是名字是不是为空
			return vs ==null ? null:vs[0];
		}
		
		@Override
		public String[] getParameterValues(String name) {
			
			return  getParameterMap().get(name);
		}
		
		
	}
	
	
	@Override
	public void destroy() {
		
	}
	
}


装饰者模式的特点为:
1、装饰者和被装饰者对象有相同的超类型。
2、你可以用一个或多个装饰者包装一个对象
3、那么我们知道装饰和被装饰者有相同的超类型,所以在任何须要原始对象(被包装)的对象场合,可以用装饰过的对象代替它。
4、装饰者可以在所委托被装饰者的行为之前/或之后,加上自己行为,以达到特定的目的。
5.对象可以在任何时候被装饰



代理模式:为另一个对象提供一个替身或占位符以控制对这个对象的访问。


代理模式的变种相当多。如动态代理,虚拟代理,保护代理,远程代理。。

我们的EJB技术就是用到了代理模式。

接口:

<span style="font-family:KaiTi_GB2312;font-size:18px;"><strong>package bean;


import javax.ejb.Remote;


@Remote
public interface LoginRemote {
	public boolean login(String id, String pwd);
}</strong></span>
实例类:
<span style="font-family:KaiTi_GB2312;font-size:18px;"><strong>import java.util.Iterator;
import javax.annotation.Resource;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.sql.DataSource;
import model.User;

@Stateless
@Remote(LoginRemote.class)
public class Login implements LoginRemote {


	@PersistenceContext
	
	protected EntityManager em;
	
	public Login() {
	}


	@Resource(mappedName = "java:jboss/myDataSource")
	DataSource myDb;


	@Override
	public boolean login(String id, String pwd) {
		
		Query query = em.createQuery("select u from User u");
	
		Iterator iter = query.getResultList().iterator();
		
		System.out.println(iter.hasNext());
		while(iter.hasNext()) {
			User user = (User)iter.next();
			if(id.equals(user.getId()) && pwd.equals(user.getPwd())) {
				return true;	
			}
		}
		return false;
	}


}</strong></span>

测试类,实际上也就是客户端,通过调用接口,来达到实现的目的。。
<span style="font-family:KaiTi_GB2312;font-size:18px;"><strong>import bean.Login;


public class Test {
	public static void main(String args) {
		new Login().login("haha", "haha");
	}
}</strong></span>

客户端通过调用
远程接口或本地接口。。来达到实现接口的方法。。这种被称为远程代理。就如同我们开时开3389远程
端口一样。通过远程桌面进行服务器进行访部操作。远程桌面就是起到了一个代理的作用。

远程代理
远程代理可以为作为另个JVM上对象的本地代表,调用代理的方法,会被代理利用网络转发到远程执行,并且结果会
通过网络返回给代理。。再由代理将结果转给客户。


虚拟代理
虚拟代理作为开销大的对象的代表。虚拟代理经常直到我们真正需要一个对象的时候才创建它。当对象在创建前和创建中时由虚拟代理来扮演对象的替身,对象创建后,代理就会将请求直接委托给对象。。


核心对比:代理很像装饰者,很容易让大家以为是用一个对象把另一个包起来。然后委托一个实现方法。如动态代理是给IncovationHandler接口。通过回调,在invoke方法中进行修改你方法达到你想要的。我在这里主要理解的是。。它们虽然你,但是目的不同,因为装饰者为对象境加行为,而代理是控制对象的访问。你可能想到的是:显示在”加载中“消息,难道不是在增加行为,从一定程度上说,这的确可以算是,但是,更重要的是,代理是控制对某一个你要想改造的方法的访问,可以参考(我的另一篇博文(通个一个工具类更深理解动态代理),如何控制访问呢?代理将用户从改造的方法解耦了,就是一种两全其美的方法,用到这个方法时,通过代理是一种情况,不用到代理时,照样能很好的实现它原来的作用。。
我们给用户使用的是代理,而不是直正的对象。


通常来说。工厂也能做到这一点,只要你提供原料在工厂中实例化,返加主题,就变了。不同的原料
返回的主题也不同。代理同样也能达到这种效果。用户不知道也不在乎他使用的是代理还是真实的对象。

写到这里,突然又想起一个家伙。适配器。。因为不想一下代理和适配器都是挡在其他对象的前面,并负责请求转以给它们适配器会改变对象适配器的接口。而代理是实现相同的接口。打个比方,同样买到香港的电脑,我们知道香港的插孔和大陆的插孔是不一样的。可以通过代理去买。也可以直接在海淘然后发货给你。代理会按照你的要求(接口),买成大陆通用的版本,而香港卖家可能并不知道,你的具体需求,因为全世界这么多买家,他可能只会给你一套通用的插口。可以转换各个国家具体的插孔。这样转换头就是一个适配器。。


你可能感兴趣的:(装饰者模式,代理模式)