𝑰’𝒎 𝒉𝒉𝒈, 𝑰 𝒂𝒎 𝒂 𝒈𝒓𝒂𝒅𝒖𝒂𝒕𝒆 𝒔𝒕𝒖𝒅𝒆𝒏𝒕 𝒇𝒓𝒐𝒎 𝑵𝒂𝒏𝒋𝒊𝒏𝒈, 𝑪𝒉𝒊𝒏𝒂.
- 🏫 𝑺𝒉𝒄𝒐𝒐𝒍: 𝑯𝒐𝒉𝒂𝒊 𝑼𝒏𝒊𝒗𝒆𝒓𝒔𝒊𝒕𝒚
- 🌱 𝑳𝒆𝒂𝒓𝒏𝒊𝒏𝒈: 𝑰’𝒎 𝒄𝒖𝒓𝒓𝒆𝒏𝒕𝒍𝒚 𝒍𝒆𝒂𝒓𝒏𝒊𝒏𝒈 𝒅𝒆𝒔𝒊𝒈𝒏 𝒑𝒂𝒕𝒕𝒆𝒓𝒏, 𝑳𝒆𝒆𝒕𝒄𝒐𝒅𝒆, 𝒅𝒊𝒔𝒕𝒓𝒊𝒃𝒖𝒕𝒆𝒅 𝒔𝒚𝒔𝒕𝒆𝒎, 𝒎𝒊𝒅𝒅𝒍𝒆𝒘𝒂𝒓𝒆 𝒂𝒏𝒅 𝒔𝒐 𝒐𝒏.
- 💓 𝑯𝒐𝒘 𝒕𝒐 𝒓𝒆𝒂𝒄𝒉 𝒎𝒆:𝑽𝑿
- 📚 𝑴𝒚 𝒃𝒍𝒐𝒈: 𝒉𝒕𝒕𝒑𝒔://𝒉𝒉𝒈𝒚𝒚𝒅𝒔.𝒃𝒍𝒐𝒈.𝒄𝒔𝒅𝒏.𝒏𝒆𝒕/
- 💼 𝑷𝒓𝒐𝒇𝒆𝒔𝒔𝒊𝒐𝒏𝒂𝒍 𝒔𝒌𝒊𝒍𝒍𝒔:𝒎𝒚 𝒅𝒓𝒆𝒂𝒎
1:模版方法
1-1:定义
☘️大白话说,就是父类中有一个方法调用抽象方法step1,抽象方法step2… ,来完成某一个逻辑。这种方法就是模版方法,step1,step2是抽象方法。也就是说,模版方法定义这些抽象方法执行顺序,把你安排起来,并且子类别不允许覆写模板方法。具体方法怎么实现是子类自己实现的。形象的来说,我定义了一共99八十一难,每一难怎么过,你自己看着办。
1-2:适用场景
有一个设计模式,我们就应该想一下为什么要有模版方法模式?
- 把子类公共部分的代码抽取出来。
- 定规矩,流程怎么走,我说了算,你不能改,都得按照我这个流程走。
1-3:简单demo
- GoOnlineHelper
package com.company.design.template; public abstract class GoOnlineHelper { abstract void login(); abstract void scan(); abstract void logout(); protected final void surfTheInternet() { this.login(); this.scan(); this.logout(); } }
- chromeHelper
package com.company.design.template; public class ChromeHelper extends GoOnlineHelper { @Override void login() { System.out.println("chrome help you login"); } @Override void scan() { System.out.println("chrome help you scan"); } @Override void logout() { System.out.println("chrome help you logout"); } }
- firefoxHelper
package com.company.design.template; public class FireFoxHelper extends GoOnlineHelper { @Override void login() { System.out.println("firefox help you login"); } @Override void scan() { System.out.println("firefox help you scan"); } @Override void logout() { System.out.println("firefox help you logout"); } }
☘️GoOnlineHelper 抽象类定义了3个动作以及模版surfTheInternet规定了3者的顺序,从而实现类只需要去实现流程中的细节部分,流程已经被父抽象类定义好了。那么,怎么去理解子类可以重新定义某些特定的步骤呢?比如chrome不想logout。可以加一个标识,来标识是否想要logout。于是乎,有了下面的代码:
- GoOnlineHelper
package com.company.design.template; public abstract class GoOnlineHelper { boolean logoutFlag; GoOnlineHelper(boolean logoutFlag) { this.logoutFlag = logoutFlag; } boolean logoutOrNot() { return this.logoutFlag; } abstract void login(); abstract void scan(); abstract void logout(); protected final void surfTheInternet() { this.login(); this.scan(); if (logoutOrNot()) { this.logout(); } } public boolean isLogoutFlag() { return logoutFlag; } public void setLogoutFlag(boolean logoutFlag) { this.logoutFlag = logoutFlag; } }
- chromeHelper
package com.company.design.template; public class ChromeHelper extends GoOnlineHelper { public ChromeHelper(boolean logoutFlag) { super(logoutFlag); } @Override void login() { System.out.println("chrome help you login"); } @Override void scan() { System.out.println("chrome help you scan"); } @Override void logout() { System.out.println("chrome help you logout"); } }
- firefoxHelper
package com.company.design.template; public class FireFoxHelper extends GoOnlineHelper { public FireFoxHelper(boolean logoutFlag) { super(logoutFlag); } @Override void login() { System.out.println("firefox help you login"); } @Override void scan() { System.out.println("firefox help you scan"); } @Override void logout() { System.out.println("firefox help you logout"); } }
- client
package com.company.design.template; public class Client { public static void main(String[] args) { GoOnlineHelper chromeHelper = new ChromeHelper(true); GoOnlineHelper firefoxHelper = new FireFoxHelper(false); chromeHelper.surfTheInternet(); firefoxHelper.surfTheInternet(); } } ----output---- chrome help you login chrome help you scan chrome help you logout firefox help you login firefox help you scan
☘️可以发现,通过子类传递的标识,就可以修改部分的流程逻辑,这里Firefox 就少了一回登录,跳过了一次流程。
1-4:模版方法的缺陷
- 模版方法模式的调用顺序和流程都是被模版方法限制的,这就很好的体现了“模版”二字,你只能按照这个流程走。
- 违背了里式替换原则,因为父类中帮助了子类做了一个default实现。
- 因为算法流程都在模版方法里面,如果这个算法的流程越来越长,那么维护就会越来越难了。
1-5:源码中模版方法-HttpServlet
-
HttpServlet
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 = lStrings.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); } }
它这个就是一个模版方法,我们看一眼doGet和doPost
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String protocol = req.getProtocol(); String msg = lStrings.getString("http.method_get_not_supported"); if (protocol.endsWith("1.1")) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg); } else { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); } } protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String protocol = req.getProtocol(); String msg = lStrings.getString("http.method_post_not_supported"); if (protocol.endsWith("1.1")) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg); } else { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); } }
☘️会发现虽然这里有默认实现,但是都是一样的,也就是说这里是等着你去覆盖,这就是为啥我们之前写servlet的时候,extend HttpServlet之后,要覆盖doGet和doPost方法的原因了,这里是一个模版方法模式,我们覆盖具体的方法,执行流程在HttpServlet中已经帮我们定好了。