您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關Java設計模式中模板方法模式的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
在程序開發中,經常會遇到這種情況:某個方法要實現的算法需要多個步驟,但其中有一些步驟是固定不變的,而另一些步驟則是不固定的。為了提高代碼的可擴展性和可維護性,模板方法模式在這種場景下就派上了用場。
譬如制作一節網課的步驟可以簡化為4個步驟:
1.制作PPT
2.錄制視頻
3.編寫筆記
4.提供課程資料
其中1、2、3的動作在所有課程中的固定不變的,步驟3可有可無,步驟4在每個課程都不同(有些課程需要提供源代碼,有些需要提供圖片文件等)
我們可以在父類中確定整個流程的循序,并實現固定不變的步驟,而把不固定的步驟留給子類實現。甚至可以通過一個鉤子方法,讓子類來決定流程中某個方法的執行與否
模板方法模式:定義一個操作中算法的框架,而將一些步驟延遲到子類中。模板方法模式使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。模板方法模式是一種基于繼承的代碼復用技術,它是一種類行為型模式。
AbstractClass(抽象類):在抽象類中定義了一系列基本操作(PrimitiveOperations),這些基本操作可以是具體的,也可以是抽象的,每一個基本操作對應算法的一個步驟,在其子類中可以重定義或實現這些步驟。同時,在抽象類中實現了一個模板方法(Template Method),用于定義一個算法的框架,模板方法不僅可以調用在抽象類中實現的基本方法,也可以調用在抽象類的子類中實現的基本方法,還可以調用其他對象中的方法。
ConcreteClass(具體子類):它是抽象類的子類,用于實現在父類中聲明的抽象基本操作以完成子類特定算法的步驟,也可以覆蓋在父類中已經實現的具體基本操作。
一個模板方法是定義在抽象類中的、把基本操作方法組合在一起形成一個總算法或一個總行為的方法。這個模板方法定義在抽象類中,并由子類不加以修改地完全繼承下來。模板方法是一個具體方法,它給出了一個頂層邏輯框架,而邏輯的組成步驟在抽象類中可以是具體方法,也可以是抽象方法。
基本方法是實現算法各個步驟的方法,是模板方法的組成部分。基本方法又可以分為三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。
抽象方法:一個抽象方法由抽象類聲明、由其具體子類實現。
具體方法:一個具體方法由一個抽象類或具體類聲明并實現,其子類可以進行覆蓋也可以直接繼承。
鉤子方法:可以與一些具體步驟 “掛鉤” ,以實現在不同條件下執行模板方法中的不同步驟
所以我們通過模板方法模式,在抽象類中把整個流程固定下來,其中1、2、3的實現在抽象類中完成,3的執行與否則由子類通過鉤子方法來控制,4則由子類來實現
抽象類定義如下:
//抽象課程類 public abstract class Course { protected final void makeCourse() { this.makePPT(); this.makeVideo(); if (needWriteArticle()) { this.writeArticle(); } this.packageCourse(); } //final表示子類繼承后,無法修改父類該方法 final void makePPT() { System.out.println("1. 制作PPT"); } final void makeVideo() { System.out.println("2. 制作視頻"); } final void writeArticle() { System.out.println("3. 編寫課程筆記"); } //鉤子方法 protected boolean needWriteArticle() { return false; } //抽象方法,留給子類去實現 abstract void packageCourse(); }
其中的 makeCourse
方法是模板方法,它定義了制作網課的基本流程,makePPT
、makeVideo
、writeArticle
這三個步驟在所有課程中都是固定的,所以用 final 關鍵字修飾;packageCourse
方法在所有課程中都可能不一樣,所以聲明為抽象方法,由子類自行實現;鉤子方法 needWriteArticle
返回一個 boolean
類型的值,控制是否編寫課程筆記
子類 JavaCourse
,實現了抽象方法 packageCourse
,重寫了鉤子方法 needWriteArticle
public class JavaCourse extends Course { @Override void packageCourse() { System.out.println("4. 提供Java課程源代碼"); } @Override protected boolean needWriteArticle() { return true; } }
子類 FECourse
,實現了抽象方法 packageCourse
,重寫了鉤子方法 needWriteArticle
,其中把鉤子方法的結果交給客戶端確定
public class JavaCourse extends Course { @Override void packageCourse() { System.out.println("4. 提供Java課程源代碼"); } @Override protected boolean needWriteArticle() { return true; } }
客戶端測試
public class TEST { public static void main(String[] args) { System.out.println("Java課程start---"); Course javaCourse = new JavaCourse(); javaCourse.makeCourse(); System.out.println("Java課程end---\n"); System.out.println("前端課程start---"); Course feCourse = new FECourse(false); feCourse.makeCourse(); System.out.println("前端課程end---"); } }
輸出結果
Java課程start---
1. 制作PPT
2. 制作視頻
3. 編寫筆記
4. 提供Java課程源代碼
Java課程end---前端課程start---
1. 制作PPT
2. 制作視頻
4.1 提供課程的前端代碼
4.2 提供課程的圖片等多媒體素材
前端課程end---
在父類中形式化地定義一個算法,而由它的子類來實現細節的處理,在子類實現詳細的處理算法時并不會改變算法中步驟的執行次序。
模板方法模式是一種代碼復用技術,它在類庫設計中尤為重要,它提取了類庫中的公共行為,將公共行為放在父類中,而通過其子類來實現不同的行為,它鼓勵我們恰當使用繼承來實現代碼復用
可實現一種反向控制結構,通過子類覆蓋父類的鉤子方法來決定某一特定步驟是否需要執行。
在模板方法模式中可以通過子類來覆蓋父類的基本方法,不同的子類可以提供基本方法的不同實現,更換和增加新的子類很方便,符合單一職責原則和開閉原則
需要為每一個基本方法的不同實現提供一個子類,如果父類中可變的基本方法太多,將會導致類的個數增加,系統更加龐大,設計也更加抽象,此時,可結合橋接模式來進行設計。
對一些復雜的算法進行分割,將其算法中固定不變的部分設計為模板方法和父類具體方法,而一些可以改變的細節由其子類來實現。即:一次性實現一個算法的不變部分,并將可變的行為留給子類來實現。
各子類中公共的行為應被提取出來并集中到一個公共父類中以避免代碼重復。
需要通過子類來決定父類算法中某個步驟是否執行,實現子類對父類的反向控制。
一般模板方法都會加上final關鍵字,防止子類重寫該方法
Servlet
(Server Applet)是Java Servlet的簡稱,用Java編寫的服務器端程序,主要功能在于交互式地瀏覽和修改數據,生成動態Web內容。在每一個 Servlet
都必須要實現 Servlet 接口,GenericServlet 是個通用的、不特定于任何協議的Servlet,它實現了 Servlet 接口,而 HttpServlet
繼承于 GenericServle
t,實現了 Servlet 接口,為 Servlet 接口提供了處理HTTP協議的通用實現,所以我們定義的 Servlet 只需要繼承 HttpServlet 即可。
HttpServlet
的簡要代碼如下所示
public abstract class HttpServlet extends GenericServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... } protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... } protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... } protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... } protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... } protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... } protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... } 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); } } // ...省略... }
在 HttpServlet
的 service
方法中,首先獲得到請求的方法名,然后根據方法名調用對應的 doXXX
方法,比如說請求方法為GET
,那么就去調用 doGet
方法;請求方法為POST
,那么就去調用 doPost
方法
HttpServlet
相當于定義了一套處理 HTTP
請求的模板;service
方法為模板方法,定義了處理HTTP請求的基本流程;doXXX 等方法為基本方法,根據請求方法做相應的處理,子類可重寫這些方法;HttpServletRequest
中的Method則起到鉤子方法的作用.
在開發javaWeb
應用時,自定義的Servlet
類一般都擴展 HttpServlet
類,譬如我們實現一個輸出 Hello World! 的 Servlet 如下
// 擴展 HttpServlet 類 public class HelloWorld extends HttpServlet { public void init() throws ServletException { // ... } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<h2>Hello World!</h2>"); } public void destroy() { // ... } }
該自定義的 Servlet 重寫了 doGet 方法,當客戶端發起 GET 請求時將得到
Spring IOC容器初始化時運用到的模板方法模式
1、首先定義一個接口ConfigurableApplicationContext,聲明模板方法refresh
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { /**聲明了一個模板方法*/ void refresh() throws BeansException, IllegalStateException; }
2、抽象類AbstractApplicationContext實現了接口,主要實現了模板方法refresh(這個方法很重要,是各種IOC容器初始化的入口)的邏輯
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean { /**模板方法的具體實現*/ public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); //注意這個方法是,里面調用了兩個抽象方法refreshBeanFactory、getBeanFactory // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { //注意這個方法是鉤子方法 // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); //注意這個方法是鉤子方法 // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
這里最主要有一個抽象方法obtainFreshBeanFactory、兩個鉤子方法postProcessBeanFactory和onRefresh,看看他們在類中的定義
兩個鉤子方法:
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { } protected void onRefresh() throws BeansException { // For subclasses: do nothing by default. }
再看看獲取Spring容器的抽象方法:
/**其實他內部只調用了兩個抽象方法**/ protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; } protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException; public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
具體要取那種BeanFactory容器的決定權交給了子類!
3、具體實現的子類,實現了抽象方法getBeanFactory的子類有: AbstractRefreshableApplicationContext
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext { @Override public final ConfigurableListableBeanFactory getBeanFactory() { synchronized (this.beanFactoryMonitor) { if (this.beanFactory == null) { throw new IllegalStateException("BeanFactory not initialized or already closed - " + "call 'refresh' before accessing beans via the ApplicationContext"); } //這里的this.beanFactory在另一個抽象方法refreshBeanFactory的設置的 return this.beanFactory; } } } public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { @Override public final ConfigurableListableBeanFactory getBeanFactory() { //同樣這里的this.beanFactory在另一個抽象方法中設置 return this.beanFactory; } }
其實這里的差別還不是很大,我們可以看看另一個抽象方法refreshBeanFactory的實現,兩個抽象方法的配合使用。
感謝各位的閱讀!關于“Java設計模式中模板方法模式的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。