装饰者模式

Decorator模式或者Wrapper模式允许修饰或者封装(在字面意义中,即修改行为)一个对象,即使你没有该对象的源代码或者该对象标识为final。

Decorator模式适用于无法继承该类(例如,对象的实现类使用final标识)或者无法创建该类的实例,但可以从另外的系统中可以取得该类的实现时。例如,Servlet容器方法。只有一种方法可以修改ServletRequest或者ServletResponse行为,即在另外的对象中封装该实例。唯一的限制是,修饰对象必须继承一个接口,然后实现接口以封装这些方法。

UML类图

上面类图说明了一个Component接口以及它的实现类ComponentImpl。Component接口定义了A的方法。为了修饰ComponentImpl的实例,需要创建一个Decorator类,并实现Component的接口,然后在子类中扩展Decorator的新行为。在类图中DecoratorA就是Decorator的一个子类。每个Decorator实例需要包含Component的一个实例。Decorator类代码如下(注意在构建函数中获取了Component的实例,这意味着创建Decorator对象只能传入Component的实例)

在Decorator类中,有修饰的方法就是可能在子类中需要修改行为的方法,在子类中不需要修饰的方法可以不需要实现。所有的方法,无论是否需要修饰,都叫作Component中的配对方法。Decorator是一个非常简单的类,便于提供每个方法的默认实现。修改行为在它的子类中。需要牢记一点,Decorator类及被修饰对象的类需要实现相同的接口。为了实现Decorator,可以在Decorator中封装修饰对象,并把Decorator作为Component的一个实现。任何Component的实现都可以在Decorator中注入。事实上,你可以把一个修饰的对象传入另一个修饰的对象,以实现双重的修饰。

Servlet API中的装饰者模式应用

Servlet API源自于4个实现类,它很少被使用,但是十分强大:ServletRequestWrapper、ServletResponseWrapper以及HttpServletRequestWrapper、HttpServletResponseWrapper。ServletRequestWrapper(或者其他3个Wrapper类)非常便于使用,因为它提供了每个方法的默认实现:即ServletRequest封闭的配置方法。通过继承ServletRequestWrapper,只需要实现你需要变更的方法就可以了。如果不用ServletRequestWrapper,则需要继承ServletRequest并实现ServletRequest中所有的方法。

下图所示为Decorator模式中ServletRequestWrapper的类图。Servlet容器在每次Servlet服务调用时创建ServletRequest、ContainerImpl。直接扩展ServletRequestWrapper就可以修饰ServletRequest了。

ServletRequestWrapper源码

public class ServletRequestWrapper implements ServletRequest {
private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
private static final ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.LocalStrings");
private ServletRequest request; public ServletRequestWrapper(ServletRequest request) {
if (request == null) {
throw new IllegalArgumentException(lStrings.getString("wrapper.nullRequest"));
} else {
this.request = request;
}
} public ServletRequest getRequest() {
return this.request;
} public void setRequest(ServletRequest request) {
if (request == null) {
throw new IllegalArgumentException(lStrings.getString("wrapper.nullRequest"));
} else {
this.request = request;
}
} public Object getAttribute(String name) {
return this.request.getAttribute(name);
} public Enumeration<String> getAttributeNames() {
return this.request.getAttributeNames();
} public String getCharacterEncoding() {
return this.request.getCharacterEncoding();
} public void setCharacterEncoding(String enc) throws UnsupportedEncodingException {
this.request.setCharacterEncoding(enc);
} public int getContentLength() {
return this.request.getContentLength();
} public long getContentLengthLong() {
return this.request.getContentLengthLong();
} public String getContentType() {
return this.request.getContentType();
} public ServletInputStream getInputStream() throws IOException {
return this.request.getInputStream();
} public String getParameter(String name) {
return this.request.getParameter(name);
} public Map<String, String[]> getParameterMap() {
return this.request.getParameterMap();
} public Enumeration<String> getParameterNames() {
return this.request.getParameterNames();
} public String[] getParameterValues(String name) {
return this.request.getParameterValues(name);
} public String getProtocol() {
return this.request.getProtocol();
} public String getScheme() {
return this.request.getScheme();
} public String getServerName() {
return this.request.getServerName();
} public int getServerPort() {
return this.request.getServerPort();
} public BufferedReader getReader() throws IOException {
return this.request.getReader();
} public String getRemoteAddr() {
return this.request.getRemoteAddr();
} public String getRemoteHost() {
return this.request.getRemoteHost();
} public void setAttribute(String name, Object o) {
this.request.setAttribute(name, o);
} public void removeAttribute(String name) {
this.request.removeAttribute(name);
} public Locale getLocale() {
return this.request.getLocale();
} public Enumeration<Locale> getLocales() {
return this.request.getLocales();
} public boolean isSecure() {
return this.request.isSecure();
} public RequestDispatcher getRequestDispatcher(String path) {
return this.request.getRequestDispatcher(path);
} /** @deprecated */
@Deprecated
public String getRealPath(String path) {
return this.request.getRealPath(path);
} public int getRemotePort() {
return this.request.getRemotePort();
} public String getLocalName() {
return this.request.getLocalName();
} public String getLocalAddr() {
return this.request.getLocalAddr();
} public int getLocalPort() {
return this.request.getLocalPort();
} public ServletContext getServletContext() {
return this.request.getServletContext();
} public AsyncContext startAsync() throws IllegalStateException {
return this.request.startAsync();
} public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
return this.request.startAsync(servletRequest, servletResponse);
} public boolean isAsyncStarted() {
return this.request.isAsyncStarted();
} public boolean isAsyncSupported() {
return this.request.isAsyncSupported();
} public AsyncContext getAsyncContext() {
return this.request.getAsyncContext();
} public boolean isWrapperFor(ServletRequest wrapped) {
if (this.request == wrapped) {
return true;
} else {
return this.request instanceof ServletRequestWrapper ? ((ServletRequestWrapper)this.request).isWrapperFor(wrapped) : false;
}
} public boolean isWrapperFor(Class<?> wrappedType) {
if (wrappedType.isAssignableFrom(this.request.getClass())) {
return true;
} else {
return this.request instanceof ServletRequestWrapper ? ((ServletRequestWrapper)this.request).isWrapperFor(wrappedType) : false;
}
} public DispatcherType getDispatcherType() {
return this.request.getDispatcherType();
}
}

只需继承ServletRequestWrapper类,覆盖需要修改的方法,即可自定实现类。


内容源自《Servlet、JSP和Srping MVC学习指南》

Servlet中的装饰者模式的更多相关文章

  1. Netty中的装饰者模式

    装饰者的应用 所谓装饰者,说白了,目的就是对现有的对象进行增强,装饰者设计模式最大的优点就是,它在扩展类原有功能的基础上还避免的类爆炸的情况 Netty中的装饰者模式的应用 ByteBuf是netty ...

  2. 设计模式(三):“花瓶+鲜花”中的装饰者模式(Decorator Pattern)

    在前两篇博客中详细的介绍了"策略模式"和“观察者模式”,今天我们就通过花瓶与鲜花的例子来类比一下“装饰模式”(Decorator Pattern).在“装饰模式”中很好的提现了开放 ...

  3. Java中InputStream装饰器模式的大家族

    本文写在po主初学JAVA时,在学习inputStream摸不着头脑,受Java IO-InputStream家族 -装饰者模式一文启发,所以在理清思路时写下本文.因为初学,如有错误,望指正. 因为和 ...

  4. RecyclerView中装饰者模式应用

    近段时间一直在加班,在赶一个项目,现在项目接近尾声,那么需要对过去一段时间工作内容进行复盘,总结下比较好的解决方案,积累一些经验,我认为的学习方式,是「理论-实践-总结-分享」,这一种很好的沉淀方式. ...

  5. .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(三)

    阅读目录: 7.HtmlHelper.HtmlHelper<T>中的ViewModel的类型推断 8.控制ViewModel中的某个属性的呈现(使用PartialView部分视图细粒度控制 ...

  6. javascript设计模式学习之十五——装饰者模式

    一.装饰者模式定义 装饰者模式可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象.这种为对象动态添加职责的方式就称为装饰者模式.装饰者对象和它所装饰的对象拥有一致的接口,对于用 ...

  7. 【设计模式 - 9】之装饰者模式(Decorator)

    1      模式简介 装饰者模式允许向一个现有的对象添加新的功能,同时又不改变其结构. 装饰者模式的思路是用"调料"对象将原始对象进行层层包裹,同时其属性.动作层层传递,达到最终 ...

  8. java I/O之装饰者模式

    装饰者: Decorator模式(别名Wrapper):动态将职责附加到对象上,若要扩展功能,装饰者提供了比继承更具弹性的代替方案. 装饰者模式意图: 动态的给一个对象添加额外的职责.Decorato ...

  9. Java设计模式之装饰者模式

    要实现装饰者模式,注意一下几点内容: 1.装饰者类要实现真实类同样的接口 2.装饰者类内有一个真实对象的引用(可以通过装饰者类的构造器传入) 3.装饰类对象在主类中接受请求,将请求发送给真实的对象(相 ...

随机推荐

  1. NAT基本原理及应用

    参考链接 https://blog.csdn.net/u013597671/article/details/74275852

  2. 如何使Camtasia的抠像效果更真实自然

    相信大家都知道录像编辑软件Camtasia的"移除颜色"可以抠像换背景,详细操作可以看小编的另一篇教程 <如何用Camtasia为微课换上一个好看的背景>.接下来,小编 ...

  3. appium 启动参数配置

    启动配置参数,可以参照官网: http://appium.io/docs/en/writing-running-appium/caps/#general-capabilities from appiu ...

  4. python中正则表达式

    正则表达式是一种通用的字符串匹配技术,是不会因为编程语言不一样而发生变化的如果想查找对应规则的字符串,就可以用正则表达式python中要使用正则表达式需使用re模块,它是正则表达式在python中的封 ...

  5. 关于Java里方法重载

    覆盖和重载很重要,并且比较容易混淆,所以面试中常见.基础回答:覆盖(Override),又叫重写,是指子类对父类方法的一种重写,方法名.参数列表必须相同,返回值小于父类,只能比父类抛出更少的异常,访问 ...

  6. SpringSecurity之整合JWT

    SpringSecurity之整合JWT 目录 SpringSecurity之整合JWT 1. 写在前面的话 2. JWT依赖以及工具类的编写 3. JWT过滤器 4. 登录成功结果处理器 5. Sp ...

  7. Educational Codeforces Round 96 (Rated for Div. 2) E. String Reversal 题解(思维+逆序对)

    题目链接 题目大意 给你一个长度为n的字符串,可以交换相邻两个元素,使得这个字符串翻转,求最少多少种次数改变 题目思路 如果要求数组排序所需要的冒泡次数,那其实就是逆序对 这个也差不多,但是如果是相同 ...

  8. IDEA连接码云

    IDEA连接码云: 1.安装Gitee插件 2.下载git.exe https://git-scm.com/download/win 3.安装git 如果是Win10专业版,可能会出错,GitBash ...

  9. 为k8s预留系统资源

    为k8s预留系统资源 Kubernetes 的节点可以按照 Capacity 调度.默认情况下 pod 能够使用节点全部可用容量. 这是个问题,因为节点自己通常运行了不少驱动 OS 和 Kuberne ...

  10. kafka入门之broker-副本与ISR设计

    kafka把分区的所有副本均匀地分配到所有broker上,并从这些副本中挑选一个作为leader副本对外提供服务,而其他副本被称为follower副本,只能被动地向leader副本请求数据,从而保持与 ...