装饰者模式以对客户端透明的方式动态的为对象增加责任。此模式提供了一个比继承更为灵活的替代方案来扩展对象的功能,避免了继承方法产生的类激增问题,而且更方便更改对象的责任。
我们经常要为某一些个别的对象增加一些新的职责,并不是全部的类。例如我们系统留言反馈板块中可能需要过滤用户输入留言中的一些词汇(例如政治敏感词汇、色情词汇等)、还可能对用户输入留言进行一些修饰(例如对用户输入的URL自动加上超链接、对用户输入的UBB代码进行转换的)、还可能将用户输入的内容定时发送的网管的邮箱中等等。如果使用类继承的方式进行设计,我们可能要设计一个接口
BodyContentFilterIntf,然后在由BodyContentFilterIntf派生出SensitiveWordContentFilter、HtmlContentFilter、SendEmailContentFilter等类。但是如果还要要求同时能过滤敏感词汇并能进行修饰、或者过滤敏感词汇之后把用户输入的留言发送到网管邮箱等等,这样就要增加SensitiveWordHtmlContentFilter、SensitiveWordSendEmaillContentFilter等类,这种方式导致了子类瀑发式的产生。
一个灵活的方法是将过滤器嵌入另一个过滤器中,由这个过滤器来负责调用被嵌入过滤器的方法并执行自己的过滤器方法。我们称这个嵌入的过滤器为装饰(Decorator)。这个装饰与过滤器接口一致。装饰将请求向前转到到另一个过滤器,并且可能能转发前后执行一些额外的动作(如修饰、发送邮件),透明性使你可以递归的嵌套多个装饰,从面可以添加任意多的功能。
其实java中的过滤器模式应用非常多,典型的就是IO的Stream操作。在IO处理中,Java将数据抽象为流(Stream)。在IO库中,最基本的是InputStream和OutputStream两个分别处理输出和输入的对象,但是在InputStream和OutputStream中之提供了最简单的流处理方法,只能读入/写出字符,没有缓冲处理,无法处理文件,等等。
LineNumberInputStream、BufferInputStream、StringBufferInputStream等提供各种不同服务的类只要组合起来就可以实现很多功能,如下:
FilterInputStream myStream=new LineNumberInputStream
( new BufferInputStream( new StringBufferInputStream( myStringBuffer)));
多个的Decorator被层叠在一起,最后得到一个功能强大的流。既能够被缓冲,又能够得到行数,这就是Decorator的威力!
下面是我们的类静态图
我们定义一个接口BodyContentFilterIntf 来定义所有过滤器要实现的方法:
public interface BodyContentFilterIntf {
public String filtContent(String aContent) throws ContentFilterException;
}
这个接口中只有一个方法filtContent,将要过滤的留言传给aContent参数,filtContent对aContent进行一些处理(如装饰URL、UBB等),然后将处理后的字符串做为返回值返回;如果留言没有通过过滤(如含有敏感词汇等),只要抛出自定义ContentFilterException异常即可。
下面是一个可能的一个过滤器(保证输入的字数多于50):
public class LengthContentFilter
implements BodyContentFilterIntf {
private BodyContentFilterIntf bodyContentFilterIntf = null;
public HtmlContentFilter(BodyContentFilterIntf aFilter)
{
bodyContentFilterIntf = aFilter;
}
public String filtContent(String aContent) throws ContentFilterException {
String l_Content = aContent;
If (bodyContentFilterIntf!=null)
_Content = bodyContentFilterIntf .filtContent(l_Content);
if (aContent.length()<=50)
throw new ContentFilterException (“输入的字数不能少于50!”);
return aContext;
}
}
这是另一个过滤器(伪码,用来实现向网管邮箱发送邮件) public class SendEmailContentFilter
implements BodyContentFilterIntf {
private BodyContentFilterIntf bodyContentFilterIntf = null;
public SendEmailContentFilter(BodyContentFilterIntf aFilter)
{
bodyContentFilterIntf = aFilter;
}
public String filtContent(String aContent) throws ContentFilterException {
String l_Content = aContent;
if (bodyContentFilterIntf!=null)
l_Content = bodyContentFilterIntf .filtContent(l_Content);
SendEmail(“webmaster@SnailWeb.com”,l_Content)
return aContext;
}
}
当然还有SensitiveWordContextFilter(过滤敏感词汇),HtmlContentFilter(修饰用户输入留言中的超级链接)等。
有了这些过滤器,我们就可以很方便的为留言版添加各种复合的过滤器。例如我们想对输入的留言进行超链接修饰和过滤敏感词汇,那么我们只要如下调用即可:
try {
l_Content = new HtmlContentFilter(new SensitiveWordContextFilter(null)).
filtContent(bodyContext);
}
catch (ContentFilterException ex) {
BBSCommon.showMsgInResponse(response, ex.getMessage());
return;
}
我们甚至可以动态的添加不同的过滤器,例如对于会员我们要对输入的留言进行超链接修饰并且将他的留言发送到网管邮箱,而对于非会员我们则要过滤他输入的敏感词汇并且保证输入的字数不少于50,我们只要如下调用即可:
try {
BodyContentFilterIntf bodyContentFilterIntf = null;
bodyContentFilterIntf = new HtmlContentFilter(null);
if(IsMember==true)
bodyContentFilterIntf = new sendEmailContentFilter(bodyContentFilterIntf);
else
bodyContentFilterIntf = new SensitiveWordContextFilter(bodyContentFilterIntf);
l_Content = bodyContentFilterIntf.filtContent(bodyContext);
}
catch (ContentFilterException ex) {
BBSCommon.showMsgInResponse(response, ex.getMessage());
return;
}