2.3 行为型模式
行为型模式一共有11种,分别是模板方法模式(Template Method),策略模式(Strategy),命令模式(Command),中介者模式(Mediator),观察者模式(Observer),迭代器模式(Iterator),访问者模式(Visiter),责任链模式(Chain of Responsibility),备忘录模式(Memento),状态模式(State)和解释器模式(Interpreter)。
2.3.1 模板方法模式
模板方法模式的名称就能说明这个模式的特点。模板方法,说明这个方法是一个模板,是一个通用的方法。所有的调用者都需要调用这个方法,而且这个方法里面很多步骤都是公用的,固定的。当然,既然被多个调用者调用,说明肯定有某个或几个步骤实现是不一样的。因此这些不一样的实现步骤就可以定义为抽象方法,让调用者自己去实现。
模板方法的描述是:定义一个操作中算法的骨架,而将一些步骤延迟到子类中,使子类可以重定义算法的某些特定步骤而不改变该算法的结构。
模板方法模式是行为型模式中最简单的模式之一。
2.3.2 策略模式
策略模式其实很简单,就是根据不同的条件调用不同的接口实现。不用策略模式的话,用if-else就也可以实现,如if a then doSomethingWithA; if b then doSomthingWithB; if c…。使用策略模式后就是抽象出一个策略接口,定义一个接口方法doSomething。然后由不同的策略实现者来实现接口的doSomething方法。根据不同的条件获取到不同的策略实例进行调用。
策略模式的描述是:定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。策略模式使算法可独立于使用它的客户而变化。
2.3.3 观察者模式
观察者模式描述:观察者模式定义了一种一对多的关系,让多个观察者监听某一主题对象,当这个主题对象的状态变化时,会通知所有观察者对象,以便观察者对象进行某种应对。
观察者模式一共有四中角色,分别是抽象主题,具体主题,抽象观察者,具体观察者。抽象主题提供注册、注销和通知观察者的方法 (具体的例子可以看看参考资料1)。
2.3.4 责任链模式
责任链模式的描述:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到链上的某一个对象处理此请求。
Servelet中的过滤器Filter就使用了责任链模式。在一个应用中通常有多个过滤器,每个过滤器的作用不同,如字符编码过滤器,权限控制过滤器等等。一个请求过来,根据过滤器的先后顺序分别对该请求进行处理,前面的过滤器处理完成之后转发给后面的过滤器进行处理,一直到所有的过滤器处理完成。这些过滤器就形成了一个链,请求沿着这条链流转。
下面来看一个例子。
假设某论坛需要对用户留言进行处理,过滤掉其中的表情符号和敏感字符,如留言“JAVA是最好的语言,不接受反驳?(这里是笑脸表情符,无法显示)”,其中JAVA和笑脸符号需要过滤。可以设计两个过滤器,分别用于过滤敏感字符和表情符号。不使用责任链的情况下的实现方式:
public interface Filter {
String doFilter(String str);
}
public class SensitiveFilter implements Filter{
@Override
public String doFilter(String str) {
String sensitive = "JAVA";
if (str.contains(sensitive)) {
str = str.replace(sensitive, "**");
}
System.out.println("敏感词过滤后:" + str);
return str;
}
}
public class EmojiFilter implements Filter {
@Override
public String doFilter(String str) {
str = str.replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", "*");
System.out.println("过滤掉表情符号后:" + str);
return str;
}
}
调用:
public class Client {
public static void main(String[] args) {
String str = "JAVA是最好的语言,不接受反驳\uD83D\uDE00";
Filter[] filters = {new SensitiveFilter(), new EmojiFilter()};
for (Filter filter : filters) {
str = filter.doFilter(str);
}
}
}
输出:
敏感词过滤后:**是最好的语言,不接受反驳?
过滤掉表情符号后:**是最好的语言,不接受反驳*
上述例子中主要有两个角色,过滤器接口和具体过滤器。在客户端中控制过滤器的调用,不存在链的概念,所以不是责任链模式。如果使用责任链模式应该怎么实现呢?显然应该增加一个角色:过滤器链FilterChain,如下所示:
public class FilterChain implements Filter {
public List<Filter> filters = new ArrayList<>();
public FilterChain addFilter(Filter filter) {
filters.add(filter);
return this;
}
public String doFilter(String str) {
for (Filter filter : filters) {
str = filter.doFilter(str);
}
return str;
}
}
客户端调用:
public class Client {
public static void main(String[] args) {
String str = "JAVA是最好的语言,不接受反驳\uD83D\uDE00";
FilterChain filterChain = new FilterChain().addFilter(new SensitiveFilter()).addFilter(new EmojiFilter());
filterChain.doFilter(str);
}
}
经改造后,过滤器由filterChain进行调用,控制权交给filterChain了,这是一个简单的责任链。这个责任链中实际上是有一个控制中心的,即filterChain,控制中心决定处理对象的调用。
责任链在过滤器中的应用
实际servlet过滤器中的责任链比上面的例子要复杂一点。上述例子中,有一个控制中心,请求由控制中心进行分发,交给每一个处理器进行处理,处理完成之后结束。其结构如下:
在过滤器中,请求是由过滤器进行转发的。对上文中的例子进行改造:
public interface Filter {
void doFilter(String request, String response, FilterChain filterChain);
}
public class SensitiveFilter implements Filter{
@Override
public void doFilter(String request, String response, FilterChain filterChain) {
String sensitive = "JAVA";
if (request.contains(sensitive)) {
request = request.replace(sensitive, "**");
}
System.out.println("敏感词过滤后:" + request);
filterChain.doFilter(request, response, filterChain);
}
}
public class EmojiFilter implements Filter {
@Override
public void doFilter(String str, String response, FilterChain filterChain) {
str = str.replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", "*");
System.out.println("过滤掉表情符号后:" + str);
filterChain.doFilter(str, response, filterChain);
}
}
public class FilterChain implements Filter {
public List<Filter> filters = new ArrayList<>();
int index = 0;
public FilterChain addFilter(Filter filter) {
filters.add(filter);
return this;
}
@Override
public void doFilter(String str, String response, FilterChain filterChain) {
if (index == filters.size()) {
return;
}
Filter filter = filters.get(index);
index++;
filter.doFilter(str, response, filterChain);
}
}
调用和输出:
public class Client {
public static void main(String[] args) {
String str = "JAVA是最好的语言,不接受反驳\uD83D\uDE00";
String response = null;
FilterChain filterChain = new FilterChain().addFilter(new SensitiveFilter()).addFilter(new EmojiFilter());
filterChain.doFilter(str, response, filterChain);
}
}
敏感词过滤后:**是最好的语言,不接受反驳?
过滤掉表情符号后:**是最好的语言,不接受反驳*
过滤器链中,当前过滤器处理完request后调用filterChain的doFilter方法,该方法会继续找到下一个过滤器进行调用。下一个过滤器处理完request之后再次调用filterChain的doFilter方法,继续分发给下一个过滤器。其调用结构如下:
在这个调用过程中filterChain实际上只是负责找到下一个过滤器,将请求传给该过滤器处理。进一步简化为:
这样从左到右是处理request的,从右到左是处理response的。