外观模式其实就是将细粒度的对象包装成粗粒度的对象。举个现实的例子,有四台电脑可以随便访问,可能客户a要访问第一台和第二台,客户b要访问第二台和第三台....这种情况下互相间的调用关系是和复杂的。外观模式就好比我通过一台路由器连接了四台电脑,这样客户统一通过路由器去访问需要访问的电脑。这样就大大加强了系统的易用性和可维护性。
外观模式还有一个作用就是可以保护底层对象的数据,tomcat在设计时就利用到了这一点。一个简单的servlet处理器uml图如下:
ServletProcess1的process方法用来处理servlet请求,该方法接收实现servletRequest和servletResponse接口的request和response,里面存放着请求信息和响应信息。process方法如下:
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import java.io.File;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class ServletProcessor1 {
public void process(Request request, Response response) {
String uri = request.getUri();//获取uri的方法
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null;
try {
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
// the forming of repository is taken from the createClassLoader method in
// org.apache.catalina.startup.ClassLoaderFactory
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
// the code for forming the URL is taken from the addRepository method in
// org.apache.catalina.loader.StandardClassLoader class.
System.out.println("repository:"+repository);
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
}
catch (ClassNotFoundException e) {
System.out.println(e.toString());
}
Servlet servlet = null;
try {
servlet = (Servlet) myClass.newInstance();
<span style="color:#ff0000;"> servlet.service((ServletRequest) request, (ServletResponse) response);</span>
}
catch (Exception e) {
System.out.println(e.toString());
}
catch (Throwable e) {
System.out.println(e.toString());
}
}
}
注意其中红色的代码部分,这里将request和response直接向上转型为servletRequest和servletResponse传给了servlet的services方法。
这里的servlet其实就是request所要请求的servlet方法,就是我们在web编程时自己新建的一个servlet方法。那么问题就来了,作为tomcat用户的web程序员在servlet的service方法中接收到了由request和response向上转型而来的servletRequest和servletResponse。web程序员就可以将servletRequest和servletResponse向下转型为request和response,这样web程序员就可以调用request和response中的一些处理方法,而这些方法自然是不应该被用户调用的。这里便可以通过外观模式保护request和response中的数据和方法。
外观类设计的uml如下:
RequestFacade类的定义如下:
import java.io.IOException;
import java.io.BufferedReader;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class RequestFacade implements ServletRequest {
private ServletRequest request = null;
public RequestFacade(Request request) {
this.request = request;//将servletrequest接口具体对应到request中
}
/* implementation of the ServletRequest*/
public Object getAttribute(String attribute) {
return request.getAttribute(attribute);
}
.........
}
外观类的作用就是将Request向上转型为ServletRequest,并用直接调用的方式实现接口。外观类实现了ServletRequest的所有方法,且通过将ServletRequset设置为private类型屏蔽掉了Request类中的一些数据和方法。通过外观类,我们只要在process方法中做一点修改就可以保证web程序员在实现service方法时无法访问Request的敏感数据和方法:
RequestFacade requestFacade = new RequestFacade(request);//在requestfacade中将request强制转化为servletrequest
ResponseFacade responseFacade = new ResponseFacade(response);
try {
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) requestFacade, (ServletResponse) responseFacade);
}
通过上面的方式,在service接收到的ServletRequest和ServletResponse只能向下转型为
RequestFacade和
ResponseFacade。而外观类已经屏蔽掉了Request和Response中的敏感方法和数据,从而实现了数据安全。