上篇代码设计中存在一个设计上的问题:
ServletProcessor类
Class myClass = loader.loadClass(servletName);
Servlet servlet= (Servlet) myClass.newInstance();
//使用servlet调用service方法,servlet处理完成
servlet.service(request, response);
调用servlet类service方法时,直接将处理类创建request对象跟response对象,传给HelloServlet类的service方法,乍一看好像没啥问题,仔细想想问题就出来:
//重写service方法,响应请求
public void service(ServletRequest req, ServletResponse resp)
throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.println("hello, servlet....");
}
首先,MyTomcat定位是java web服务器,部署在上面的项目,必须按照约定来操作。如果某位心机boy在service方法中对req,跟resp对象进行向下强转,转成MyTomcat定义的Request对象跟Response对象,那么,Request类中的parseRequest parseUri 方法跟Response上面其他方法,就暴露到servlet上了。很明显, 这种设计不合理,服务器不应当对servlet开放这些底层实现的代码。问题来了,那该如何解决?答案就是本篇的主题-外观设计模式(门面设计模式)。
概念(引自百度百科)
门面模式,是指提供一个统一的接口去访问多个子系统的多个不同的接口,它为子系统中的一组接口提供一个统一的高层接口。使得子系统更容易使用。
大白话:你尽管调用接口方法就能实现功能了,具体怎么实现你甭管。
生活例子
举个例子,
图一:你跟你女票子去撸串,假设你去的是一个自助烤摊,所有操作都得亲力亲为,削签,串肉,烤火的,忙得不亦乐乎,最终烤焦了吃碳,吃力不讨好。
图二:第二你学精了,直接去烧烤店,你跟女票子只需负责点餐即可。所有操作烤店老板帮忙搞掂,你不需要知道老板是怎么削签,串肉,烤火的,你只需负责吃就行咯。
分析:上面的削签,串肉,烤火可以认为是子系统(子系统角色)能提供的功能,烤店老板扮演中间者(门面角色),而你跟女票子就是客户端(客户角色)。你的需求满足,不需要知道削签,串肉,烤火怎么操作,只需要让烤店完成即可。
门面模式(外观模式):门面角色遮蔽了子系统角色的具体功能实现,并按照客户需求对各个子系统功能进行整合,客户完成需求时,调用门面角色提供的外露接口(方法)即可。
代码列表
public class SystemA {
public void doSomething() {
System.out.println("systemA....");
}
}
public class SystemB {
public void doSomething() {
System.out.println("systemB....");
}
}
public class SystemC {
public void doSomething() {
System.out.println("systemB....");
}
}
public class Client {
private SystemA a = new SystemA();
private SystemB b = new SystemB();
private SystemC c = new SystemC();
public void show() {
this.a.doSomething();
this.b.doSomething();
this.c.doSomething();
}
}
public class Client2 {
//持有门面对象
private Facade facade = new Facade();
public void show() {
facade.toShow();
}
}
对比Client跟Client2,使用门面模式Client2更加简洁。
了解门面模式后,回到开篇的问题,
Request implements ServletRequest
Response implements ServletResponse
ServletProcessor类中让servlet调用service方法,将实现类request对象/response对象向上转型,当做参数传入在HelloServlet的service方法, 如果有心还是可以反向操作public void service(ServletRequest req, ServletResponse resp), 对req跟resp向下转型成Request / Response 对象, 就这点,在service方法中,已经暴露的request跟response类的实现操作,所以根据门面模式的特征,我们可以抽象出Request跟Response类的门面角色, 对他们具体实现进行遮蔽,仅仅对service暴露完成功能的必须方法即可。
public class RequestFacade implements ServletRequest{
private ServletRequest request;
public RequestFacade(Request request) {
this.request = request;
}
//其他方法也同样的道理
public Object getAttribute(String name) {
return this.request.getAttribute(name);
}
.....
}
public class ResponseFacade implements ServletResponse{
private ServletResponse response;
public ResponseFacade(Response response) {
this.response = response;
}
//其他方法也一样
public PrintWriter getWriter() throws IOException {
return response.getWriter();
}
}
Class myClass = loader.loadClass(servletName);
Servlet servlet= (Servlet) myClass.newInstance();
//使用servlet调用service方法,servlet处理完成
RequestFacade requestFacade = new RequestFacade(request);
ResponseFacade responseFacade = new ResponseFacade(response);
servlet.service(requestFacade, responseFacade);
对应的ServletProcessor类改进service方法传入的门面对象。
分析:
1:RequestFacade ResponseFacade 为什么要实现ServletRequest接口/ServletResponse接口
原因:门面实现对应接口是因为需要知道要将什么方法暴露个servlet,开发允许的方法,遮蔽了世界Reqeust其他是方法实现细节。
到这,本篇结束。
