装饰模式
1 对象增强的手段
* 继承
被增强的对象固定的
增强的内容也是固定的
使用继承会使类增多
1 class 咖啡类 {} 2 3 4 class 有糖咖啡 extends 咖啡类 {} 5 6 7 class 加奶咖啡 extends 咖啡类 {} 8 9 10 class 加糖加奶 extends 加奶咖啡 ,有糖咖啡{}
* 动态代理(AOP)
被增强的对象可以切换
增强的内容也可以切换:事务处理 日志 ... ...
参看:Java 动态代理
* 装饰者模式
被增强的对象是可以切换的
增强的内容是固定的
2 Decorator 模式
动态给一个对象添加一些额外的职责,就像在墙上刷油漆.
使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活.
也就是说:动态地给对象添加一些额外的功能。
它的工作原理是:创建一个始于Decorator对象(负责新功能的对象)终止于原对象的一个对象的“链”。
装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
装饰模式以对客户端透明的方式动态的给一个对象附加上更多的责任。换言之客户端并不会觉的对象在装饰前和装饰后有什么区别。
装饰模式可以在不创造更多的子类的模式下,将对象的功能加以扩展。
Source类是被装饰类,Decorator类是一个装饰类,可以为Source类动态的添加一些功能,代码如下:
1 public interface Sourceable { 2 public void method() 3 }; 4 5 public class Source implements Sourceable { 6 7 @Override 8 public void method() { 9 System.out.println("the original method!"); 10 } 11 } 12 13 public class Decorator implements Sourceable { 14 15 private Sourceable source; 16 17 public Decorator(Sourceable source){ 18 super(); 19 this.source = source; 20 } 21 22 @Override 23 public void method() { 24 System.out.println("before decorator!"); 25 source.method(); 26 System.out.println("after decorator!"); 27 } 28 } 29 30 public class DecoratorTest { 31 public static void main(String[] args) { 32 Sourceable source = new Source(); 33 Sourceable obj = new Decorator(source); 34 obj.method(); 35 } 36 } 37 38 输出: 39 before decorator! 40 the original method! 41 after decorator!
窍门:是你还有你,一切拜托你
is a
has a
use a
1.包装类要和被包装的对象实现同样的接口
2.包装类要持有一个被包装对象
3.包装类在实现接口的时候,对于不需要包装的方法调用被包装对象的方法来实现,对于需要包装的方法,我们自己实现
1 class MyConnection implements Connection {//是你 2 3 private Connection con;// 还有你 底层对象,被增强对象 4 5 public MyConnection(Connection con){//通过构造器传递底层对象! 6 this.con = con; 7 } 8 9 // 一切拜托你 不需要去改动的方法直接使用目标对象的方法 10 public Statement createStatement() { 11 return con.createStatement(); 12 } 13 14 // 增强点 15 public void close(){ 16 //把当前连接归还给池! 17 } 18 } 19 20 Connection con = 通过四大参数创建连接对象!是由mysql提供的! 21 Connection con1 = new MyConnection(con); 22 23 con1.createStatement() 24 con.createStatement(); 25 26 con1.close(); 27 con.close();
装饰模式与类继承的区别:
1) 装饰模式是一种动态行为,对已经存在类进行随意组合,
而类的继承是一种静态的行为,一个类定义成什么样的,该类的对象便具有什么样的功能,无法动态的改变。
2) 装饰模式扩展的是对象的功能,不需要增加类的数量,而类继承扩展是类的功能,
在继承的关系中,如果我们想增加一个对象的功能,我们只能通过继承关系,在子类中增加两个方法。
3) 装饰模式是在不改变原类文件和使用继承的情况下,动态的扩展一个对象的功能,它是通过创建一个包装对象,也就是装饰来包裹真是的对象。
应用:
示例1 : IO
回顾BufferedInputStream(包装类),FileInputStream读取数据的时候是一个字节一个字节的读取,效率很低,确实实现了读取文件。
需要对 FileInputStream 进行包装, 提高读取效率,BufferedInputStream就是这样的包装类, 内置了缓冲区
BufferedInputStream称为包装类 FileInputStream称为被包装对象
FileInputSteram:它是节点流!就是和一个资源绑定在一起的!文件!
BufferedInputStream:它是装饰流!创建我是一定要给我一个底层对象,然后我不管你给我的是什么流,我都会给它添加缓冲区!
new BufferedInputStream(任意的InputStream)
1 public class BufferedInputStream extends FilterInputStream { //是你 继承了FilterInputStream 2 3 protected volatile byte buf[]; 4 5 public BufferedInputStream(InputStream in) { 6 this(in, defaultBufferSize); 7 } 8 9 public BufferedInputStream(InputStream in, int size) { 10 super(in); //有你 在父类FilterInputStream已经存在里一个(见下面的FilterInputStream类) 11 if (size <= 0) { 12 throw new IllegalArgumentException("Buffer size <= 0"); 13 } 14 buf = new byte[size]; 15 } 16 17 private InputStream getInIfOpen() throws IOException {//获取需要加强的对象 18 InputStream input = in; 19 if (input == null) 20 throw new IOException("Stream closed"); 21 return input; 22 } 23 24 //方法增强 25 public synchronized int read() throws IOException { 26 if (pos >= count) { 27 fill(); 28 if (pos >= count) 29 return -1; 30 } 31 return getBufIfOpen()[pos++] & 0xff; 32 } 33 34 //方法增强 35 public synchronized int read(byte b[], int off, int len)throws IOException { 36 getBufIfOpen(); // Check for closed stream 37 if ((off | len | (off + len) | (b.length - (off + len))) < 0) { 38 throw new IndexOutOfBoundsException(); 39 } else if (len == 0) { 40 return 0; 41 } 42 43 int n = 0; 44 for (;;) { 45 int nread = read1(b, off + n, len - n); 46 if (nread <= 0) 47 return (n == 0) ? nread : n; 48 n += nread; 49 if (n >= len) 50 return n; 51 // if not closed but no bytes available, return 52 InputStream input = in; 53 if (input != null && input.available() <= 0) 54 return n; 55 } 56 } 57 。。。 。。。 58 } 59 60 61 public class FilterInputStream extends InputStream { 62 63 protected volatile InputStream in; 64 65 protected FilterInputStream(InputStream in) { 66 this.in = in; 67 } 68 69 }
案例2 HttpRequest
- 装饰 HttpServletRequest 对象
需求:在 HttpServletRequest 对象到达 Servlet 之前把用户输入的多余空格都去掉
情景:因为 HttpServletRequest 对象里的请求参数都实际包含在 java.util.Map 对象里,
而Map是不允许修改的,所以包含在 HttpServletRequest 对象里的请求参数不能被修改
解决方案:采取 Decorator(装饰器)模式
- HttpServletRequestWrapper 类(默认适配器模式)
Servlet API 中提供了一个 HttpServletRequestWrapper 类来包装原始的 request 对象,
HttpServletRequestWrapper 类实现了 HttpServletRequest 接口中的所有方法,
这些方法的内部实现都是仅仅调用了一下所包装的的 request 对象的对应方法
相类似 Servlet API 也提供了一个 HttpServletResponseWrapper 类来包装原始的 response 对象
1 package com.yiwei.utils.web.filter; 2 import java.io.UnsupportedEncodingException; 3 import java.util.Map; 4 import javax.servlet.http.HttpServletRequest; 5 import javax.servlet.http.HttpServletRequestWrapper; 6 7 /** 8 * 对GET请求参数加以处理! 9 * 装饰request 10 */ 11 public class GetRequest extends HttpServletRequestWrapper { 12 13 private HttpServletRequest request; 14 private String charset; 15 16 public GetRequest(HttpServletRequest request, String charset) { 17 super(request); 18 this.request = request; 19 this.charset = charset; 20 } 21 22 @Override 23 public String getParameter(String name) { 24 // 获取参数 25 String value = request.getParameter(name); 26 if(value == null) return null;//如果为null,直接返回null 27 try { 28 // 对参数进行编码处理后返回 29 return new String(value.getBytes("ISO-8859-1"), charset); 30 } catch (UnsupportedEncodingException e) { 31 throw new RuntimeException(e); 32 } 33 } 34 35 @SuppressWarnings({ "unchecked", "rawtypes" }) 36 @Override 37 public Map getParameterMap() { 38 Map<String,String[]> map = request.getParameterMap(); 39 if(map == null) return map; 40 // 遍历map,对每个值进行编码处理 41 for(String key : map.keySet()) { 42 String[] values = map.get(key); 43 for(int i = 0; i < values.length; i++) { 44 try { 45 values[i] = new String(values[i].getBytes("ISO-8859-1"), charset); 46 } catch (UnsupportedEncodingException e) { 47 throw new RuntimeException(e); 48 } 49 } 50 } 51 // 处理后返回 52 return map; 53 } 54 55 @Override 56 public String[] getParameterValues(String name) { 57 String[] values = super.getParameterValues(name); 58 for(int i = 0; i < values.length; i++) { 59 try { 60 values[i] = new String(values[i].getBytes("ISO-8859-1"), charset); 61 } catch (UnsupportedEncodingException e) { 62 throw new RuntimeException(e); 63 } 64 } 65 return values; 66 } 67 } 68 69 public class EncodingFilter implements Filter { 70 private String charset = "UTF-8"; 71 @Override 72 public void destroy() {} 73 74 @Override 75 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 76 throws IOException, ServletException { 77 HttpServletRequest req = (HttpServletRequest) request; 78 if(req.getMethod().equalsIgnoreCase("GET")) { 79 if(!(req instanceof GetRequest)) { 80 req = new GetRequest(req, charset);//处理get请求编码 81 } 82 } else { 83 req.setCharacterEncoding(charset);//处理post请求编码 84 } 85 chain.doFilter(req, response); 86 } 87 88 @Override 89 public void init(FilterConfig fConfig) throws ServletException { 90 String charset = fConfig.getInitParameter("charset"); 91 if(charset != null && !charset.isEmpty()) { 92 this.charset = charset; 93 } 94 } 95 }