模板方法设计模式
一.认识模板方法设计模式
1.模式定义
定义一个操作算法中的框架,而将这些步骤延迟加载到子类中。它的本质就是固定算法框架。
2.解决何种问题
让父类控制子类方法的调用顺序。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
3.模式好处
开发人员在开发时,只需要考虑方法的实现,不需要考虑方法在何种情况下被调用,实现了代码的复用。
4.模式适用场景
- 一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。
- 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
- 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。
二.模式结构与实例讲解
1.模式结构
如模板方法模式结构图所知,有两个类:
- AbstractClass(抽象类):在抽象类中定义了一系列的操作PrimitiveOperation,每个操作可以是具体的,也可以是抽象的,每个操作对应一个算法的步骤,在子类中可以重新定义或实现这些步骤。TemplateMethod()这个方法用于定义一个算法结构,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。
- ConcreteClass(具体子类):用于实现在父类中声明的抽象基本操作,也可以覆盖在父类中已经实现的具体基本操作。
2.实例讲解
创建一个抽象模板结构(AbstractClass)好父亲
public abstract class AbstractClass {
// 模板方法用来控制子类的顺序 要想有人生必须按老爸的人生顺序来
// 声明 final 不让子类覆盖这个方法,防止改变人生顺序
public final void 人生() {
学习();
工作();
爱情();
}
// 家里穷更得用工学习
public void 学习() {
System.out.println("每天晚上趴在邻居窗上学习");
}
// 工作必须稳定
public void 工作() {
System.out.println("从一而终");
}
// 恋爱自由 让儿子自由恋去
public abstract void 爱情();
}
创建一个具体模板(ConcreteClass)好儿子
public class ConcreteClass extends AbstractClass {
// 儿子不认可父亲的学习方法 考高分影响同学关系
@Override
public void 学习() {
System.out.println("60分万岁...");
}
// 父亲给我爱情自由 一定要好好谈恋爱
@Override
public void 爱情() {
System.out.println("肤白貌美大长腿...");
}
}
调用他们的人生
public class Test {
public static void main(String[] args) {
ConcreteClass cc = new ConcreteClass();
cc.人生();
}
}
输出结果
60分万岁...
从一而终
肤白貌美大长腿...
3.模式在Servlet中的应用
3.1自己实现
浏览器向服务端发送一个请求,常用请求方式有两种,get请求和post请求,这两种请求方式会导致请求参数在请求协议包(Http包)中的位置是不一样的,那么请求协议包中不同的内容到达服务端之后会由不同的对象进行处理,如请求头的内容由tomcat负责,请求体中的内容由request负责,所以此时,开发人员在拿到service()方法后考虑到它可以接受所有请求方式,因此会针对不同的请求方式封装不同的请求方法。
创建一个OneServlet继承GenericServlet,实现service()方法,需要重写里面的doPost和doGet方法。
public class OneServlet extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
// 1.从协议包[请求行]中来读取浏览器发送的请求方式
HttpServletRequest request = (HttpServletRequest)req;
String method = request.getMethod(); // POST or GET
if ("GET".equals(method)) {
doGet(req, res);
} else if ("POST".equals(method)) {
doPost(req, res);
}
}
// 处理浏览器发送的post请求
public void doPost(ServletRequest arg0, ServletResponse arg1){
// 这里面是doPost封装好的方法
System.out.println("doPost is run....");
}
// 处理浏览器发送的get请求
public void doGet(ServletRequest arg0, ServletResponse arg1){
// 这里面是doPost封装好的方法
System.out.println("doGet is run....");
}
}
现在开发人员面临的是,即需要做方法的实现,又需要考虑service()方法在何时调用。在实际开发过程中service()方法里面是一段重复性的代码,所有的servlet类实现中都需要写这么一段重复性的代码,这样重复的开发既增加工作量,又显得代码臃肿,降低了系统耦合度。模板方法设计模式就是来解决这个问题的。下面看一下怎么解决。
创建MyHttpServlet类(就是模板方法设计模式中的父类),继承GenericServlet类。
public class MyHttpServlet extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
// 控制子类中的doGet和doPost方法
// 1.从协议包[请求行]来读取浏览器发送的请求方式
HttpServletRequest request = (HttpServletRequest) req;
String method = request.getMethod(); // POST or GET
if ("GET".equals(method)) {
doGet(req, res); // this.doGet()
} else if("POST".equals(method)) {
doPost(req, res);
}
}
public void doPost(ServletRequest arg0, ServletResponse arg1) {}
public void doGet(ServletRequest arg0, ServletResponse arg1) {}
}
创建TwoServlet类,此时此刻开发人员不用去考虑何时调用doGet方法。当调用TwoServlet类的时候,tomcat一定是调用它的service()方法。
public class TwoServlet extends MyHttpServlet {
// 选择是接受doGet方法还是doPost方法
@Override
public void doGet(ServletRequest arg0, ServletResponse arg1) {
System.out.println("TwoServlet doGet is run...");
}
}
当调用TwoServlet类的时候,tomcat一定是调用它的service()方法,如果浏览器发送的请求方式为GET,那么则会执行TwoServlet中的doGet()方法。
3.2看HttpServlet源码
HttpServlet也继承了GenericServlet,跟踪找到Service()方法,发现有两个service()方法。
// 这个方法是从它的父类GenericServlet继承过来的
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest && res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
// 分别对请求对象和响应对象做了类型强转
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response); // 调用的是自己声明的service方法,重载
}
进入到自己声明的service()方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod(); // 读取请求方式
if (method.equals(METHOD_GET)) { // 根据请求方式调用对应方法
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
String errMsg = Strings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
发现service()方法没有使用final,这是因为如果使用final修饰,就彻底断绝了我们下游开发人员的开发,这样是降低了系统的灵活度。
设计模式是问题解决思想,没有固定的命令搭配 。如果我们自己可以有这样一些解决办法,那就是好的设计模式。