装饰者模式
什么是装饰者模式
- 作用:对一个类的功能进行增强,对现有类中的方法进行重写,增强原有的方法功能,以多次增强。采用继承的方式对现有的类进行功能增强
概念:
如果要对一个类的功能进行增强,又不能修改原来类的源代码,可以使用装饰者模式。
- 特点:采用继承的方式,重写父类原有的方法,在重写的子类方法中调用父类的方法,并且对父类的方法进行增强。
使用场景:
- 一切需要对类中的方法进行增强场景,而且又不能修改原来的代码。
装饰者模式中的角色:
- 抽象角色:是一个接口或抽象类,定义真实角色具有哪些功能。
- 真实角色:实现了抽象角色,并且实现了接口中所有的方法。
- 装饰角色:用于对真实角色的功能进行增强的类,继承于真实角色,并且重写真实角色中需要增强的方法,并且对功能进行增强。在装饰角色中通过构造方法传入真实角色。
装饰类的开发步骤:
- 装饰类继承于真实类,重写真实类需要增强的方法。
- 装饰类通过构造方法传入抽象对象到装饰类中。
- 在被重写的方法中,先调用真实类中的同名方法,并且对方法的功能进行增强。
示范
##### 抽象角色:是一个接口或抽象类,定义真实角色具有哪些功能。
public interface Star {
/**
* 唱歌
*/
void sing();
/**
* 跳舞
*/
void dance();
}
真实角色:实现了抽象角色,并且实现了接口中所有的方法。
public class LingMeiMei implements Star {
@Override
public void sing() {
System.out.println("林妹妹唱歌:萌萌站起来");
}
@Override
public void dance() {
System.out.println("林妹妹跳舞");
}
}
装饰类继承于真实类
public class DecorationOne extends LingMeiMei {
private Star star;
this.star = star;
}
//重写真实类需要增强的方法
@Override
public void sing() {
//调用它现有的功能
star.sing();
//调用前进行功能的增强
System.out.println("鼓掌,献花");
}
}
增强装饰类继承于真实对象
public class DecorationTwo extends LingMeiMei {
private Star star;
//构造方法传入抽象对象
public DecorationTwo(Star star) {
this.star = star;
}
//重写增强的方法
@Override
public void sing() {
//调用真实对象的方法
star.sing();
System.out.println("给出场费");
}
}
main 使用者
public class Fans {
public static void main(String[] args) {
//1. 实例化真实对象
Star s1 = new LingMeiMei();
//2. 创建装饰对象,传入了要增强的真实对象
Star s2 = new DecorationOne(s1);
//再次增强
Star s3 = new DecorationTwo(s2);
//3. 调用方法
/*
//调用的顺序不同,效果也不同
Star s2 = new DecorationTwo(s1);
Star s3 = new DecorationOne(s2);
*/
s3.sing();
//s1.dance();
}
}
装饰者模式与代理模式的区别:
开发模式 | 区别 |
---|---|
装饰者模式 | 注重的是对原有的功能的增强 |
代理模式 | 注重的是对原有功能的拦截,甚至完全替换原有的功能 |
案例 通过过滤器解决POST和GET的编码问题
- GET方式解码回顾
String 乱码 = request.getParameter("name")
new String("乱码".getBytes("iso-8859-1"),"utf-8")
思路
通过装饰类模式来给GET解决编码问题
1. POST方法很容易实现,GET方法因为需要得到每一项的参数进行编码,而且一开始并不知道有哪些参数。对request中getParameter()方法进行增强,让它直接对GET方法进行解码,到了Servlet中得到的汉字就不是乱码。
- 要继承的代理类是哪一个呢?查API文档可知,HttpServletRequest接口的实现类是HttpServletRequestWrapper
- 重写request.getParameter()方法,对这个方法进行装饰,在方法中实现编码的功能。
实现步骤:
装饰类:
1. 写一个类装饰角色MyRequestWrapper继承于真实角色HttpServletRequestWrapper,对它进行增强
1. 在类中声明一个成员变量HttpServletRequest,这是抽象角色
1. 通过构造方法转入HttpServletRequest的对象
1. 重写request.getParameter()方法,对汉字进行编码。
- 调用原来request.getParameter() 得到未编码前的字符串
- 判断如果是GET方法,而且值不为null,则进行编码。new String(“乱码”.getBytes(“iso-8859-1”),”utf-8”)
- 返回增强以后的字符串
过滤器:
1. 在过滤器中首先给POST方法编码
1. 创建装饰类对象MyRequestWrapper
1. 在过滤器中放行,注意:这里的过滤器放行的是装饰类的对象,而不应该是原来的request对象
这样就既能解决POST编码问题,也解决了GET编码问题
/**
* 过滤所有Servlet中使用POST方法提交的汉字的编码
*
* @author NewBoy
*
*/
public class EncodingFilter implements Filter {
// 成员变量,通过配置属性参数设置的编码值(UTF-8)
private String charset;
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 设置汉字的编码, charset相当于utf-8,解决了POST的编码问题
request.setCharacterEncoding(charset);
//直接实例化增强的装饰类,用于解决GET的编码问题
MyRequest myRequest = new MyRequest((HttpServletRequest) request);
// 放行,传递给下一个过滤链或web资源,放行的是增强以后的对象
chain.doFilter(myRequest, response);
}
// 过滤的编码参数,通过filterConfig得到
@Override
public void init(FilterConfig config) throws ServletException {
charset = config.getInitParameter("charset");
System.out.println(charset);
}
/*
* 抽象角色:接口HttpServletRequest 真实角色:HttpServletRequestWrapper
* 实现了HttpServletRequest接口 装饰角色:我们的类 class MyRequest extends
* HttpServletRequestWrapper重写getParameter()方法增强
*/
private class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
// 通过构造方法传入对象
public MyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
// 重写getParameter方法进行功能的增强
@Override
public String getParameter(String name) {
// 1. 得到原来的值
String value = request.getParameter(name);
// 2. 判断是否是GET方法,只有GET方法进行解码
// 解码
if (value == null) {
return null;
}
// 不为空
if ("GET".equals(request.getMethod())) {
// 解码
try {
return new String(value.getBytes("iso-8859-1"), charset);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return value;
}
}
}