装饰(Decorator)模式又名包装(Wrapper)模式。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的另一个替代方案。
装饰模式以对客户透明的方式动态地给对象增加更多的责任。也就是说,客户端并不知道对象在装饰前和装饰后又什么区别。装饰模式可以在不使用创造更多的子类的情况下将对象的功能进行扩展。
装饰模式使用原来被装饰的一个子类的实例,把客户端的调用委派到被装饰类。装饰类的关键在于这种扩展是完全透明的。
Java中增强一个类的对象方法有三种:
1. 继承或者实现接口:A对象继承a对象类,然后重写fun1(),就是增强了这个方法,如果a是接口,就无法用继承增强。
2.装饰者模式:
(1)增强类与被增强类实现同一接口(继承同一父类)
(2)增强类中传入被增强类对象
(3)需要增强的重写,不需要的调用被增强对象的。
3.动态代理:
动态代理与装饰者模式比价相似,而且通过反射完成。
增强模式的类图如下:
涉及到的角色如下:
抽象构建(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
具体装饰(ConcreteDecorator)角色:负责给构件对象"贴上"附加的责任。(这个角色可以省略)
源码如下:
package cn.qlq.decorator; public interface Component { void option1(); void option2(); }
package cn.qlq.decorator; public class ConcreteComponent implements Component { @Override public void option2() { System.out.println("option2"); } @Override public void option1() { System.out.println("option2"); } }
package cn.qlq.decorator; public class Decorator implements Component { private Component component; public Decorator(Component component) { super(); this.component = component; } public Decorator() { } @Override public void option2() { component.option2(); } @Override public void option1() { System.out.println("增强处理前"); component.option1(); System.out.println("增强处理后"); } }
package cn.qlq.decorator; public class ConcreteDecorator extends Decorator { @Override public void option1() { super.option1(); } @Override public void option2() { super.option2(); } }
在上面的装饰类中有一个私有属性component属性,其数据类型是构件。这个装饰类实现Component接口。在装饰类中处理方法,需要增强的进行增强,不需要的委派给component。
客户端代码:
package cn.qlq.decorator; public class Client { public static void main(String[] args) { Component component = new ConcreteComponent(); Decorator decorator = new Decorator(component); decorator.option1(); } }
结果:
增强处理前
option2
增强处理后
适用场景:
(1)需要扩展一个类的功能,或者给类增加附加的责任。
(2)需要动态地给一个对象增加一些功能,这些功能可以再动态地撤销。
(3)需要增加增加由一些基功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。
优缺点:
优点:
(1)装饰者模式与继承都是扩展功能,但是装饰者模式更加灵活。
(2)通过使用不同的具体装饰类以及这些装饰了的组合可以创造出很多不同行为的组合。
缺点:
使用装饰模式会产生比使用继承关系更多的对象,使得差错变得困难,特别是这些对象看上去很像。
半透明的装饰模式
装饰模式和适配器模式都是"包裹模式",它们都是通过封装其他对象达到设计目的,但是区别很大。
理想的装饰模式在被装饰对象进行功能增强的同时,要求具体构件角色、装饰角色的接口与抽象构件角色的接口完全一致。而适配器模式则不然。
装饰模式有透明模式和半透明模式。两者的区别就在于装饰角色的接口与抽象构件的接口是否完全一致。透明的装饰模式也就是理想的装饰模式。如果接口不一致,也就是说装饰角色的接口比抽象构件角色接口宽的话,装饰角色实际上变成了一个适配器角色(对象适配模式),也可以被称为是半透明的装饰模式。如下图:
Java类库中应用装饰模式
在类库中,IO大量用到装饰模式。
例如:一个基于装饰者模式的编码过滤器
package web.filter; import java.io.IOException; import java.io.UnsupportedEncodingException; 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{ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //request.setCharacterEncoding("UTF-8"); //在传递request之前对request的getParameter方法进行增强 /* * 装饰者模式(包装) * * 1、增强类与被增强的类要实现统一接口 * 2、在增强类中传入被增强的类 * 3、需要增强的方法重写 不需要增强的方法调用被增强对象的 * */ //被增强的对象 HttpServletRequest req = (HttpServletRequest) request; //增强对象 EnhanceRequest enhanceRequest = new EnhanceRequest(req); chain.doFilter(enhanceRequest, response); } @Override public void destroy() { } @Override public void init(FilterConfig filterConfig) throws ServletException { } } class EnhanceRequest extends HttpServletRequestWrapper{ //这个类允许你只重写想增强的方法 private HttpServletRequest request; public EnhanceRequest(HttpServletRequest request) { super(request); this.request = request; } //对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; } }