設計模式17—設計模式之模板方法模式(Template Method)(行為型)

來源:互聯網
上載者:User
1.情境類比

使用軟體類比登入控制,普通使用者和工作人員使用者,工作人員的密碼在資料庫中是加密的。
步驟大致如下:
前台提交,後台擷取登入資訊,同資料庫中的登陸資訊進行比較,只不過工作人員是加密的,普通使用者是不加密的。
如果匹配,跳到下一介面,如果不匹配,那麼返回登入介面,並且顯示錯誤資訊。
代碼如下
普通使用者:

package demo15.templatemethod.example1;/** * 描述使用者資訊的資料模型 */public class UserModel {private String uuid,userId,pwd,name;public String getUuid() {return uuid;}public void setUuid(String uuid) {this.uuid = uuid;}public String getUserId() {return userId;}public void setUserId(String userId) {this.userId = userId;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}public String getName() {return name;}public void setName(String name) {this.name = name;}}*********************************************************************package demo15.templatemethod.example1;/** * 描述登入人員登入時填寫的資訊的資料模型 */public class LoginModel {private String userId,pwd;public String getUserId() {return userId;}public void setUserId(String userId) {this.userId = userId;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}}*********************************************************************package demo15.templatemethod.example1;/** * 普通使用者登入控制的邏輯處理 */public class NormalLogin {/** * 判斷登入資料是否正確,也就是是否能登入成功 * @param lm 封裝登入資料的Model * @return true表示登入成功,false表示登入失敗 */public boolean login(LoginModel lm) {//1:從資料庫擷取登入人員的資訊, 就是根據使用者編號去擷取人員的資料UserModel um = this.findUserByUserId(lm.getUserId());//2:判斷從前台傳遞過來的登入資料,和資料庫中已有的資料是否匹配//先判斷使用者是否存在,如果um為null,說明使用者肯定不存在//但是不為null,使用者不一定存在,因為資料層可能返回new UserModel();//因此還需要做進一步的判斷if (um != null) {//如果使用者存在,檢查使用者編號和密碼是否匹配if (um.getUserId().equals(lm.getUserId())&& um.getPwd().equals(lm.getPwd())) {return true;}}return false;}/** * 根據使用者編號擷取使用者的詳細資料 * @param userId 使用者編號 * @return 對應的使用者的詳細資料 */private UserModel findUserByUserId(String userId) {// 這裡省略具體的處理,僅做示意,返回一個有預設資料的對象UserModel um = new UserModel();um.setUserId(userId);um.setName("test");um.setPwd("test");um.setUuid("User0001");return um;}}
工作人員
package demo15.templatemethod.example2;/** * 描述登入人員登入時填寫的資訊的資料模型 */public class LoginModel{private String workerId,pwd;public String getWorkerId() {return workerId;}public void setWorkerId(String workerId) {this.workerId = workerId;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}}**********************************************************************package demo15.templatemethod.example2;/** * 工作人員登入控制的邏輯處理 */public class WorkerLogin {/** * 判斷登入資料是否正確,也就是是否能登入成功 * @param lm 封裝登入資料的Model * @return true表示登入成功,false表示登入失敗 */public boolean login(LoginModel lm) {//1:根據工作人員編號去擷取工作人員的資料WorkerModel wm = this.findWorkerByWorkerId(lm.getWorkerId());//2:判斷從前台傳遞過來的使用者名稱和加密後的密碼資料,和資料庫中已有的資料是否匹配//先判斷工作人員是否存在,如果wm為null,說明工作人員肯定不存在//但是不為null,工作人員不一定存在,//因為資料層可能返回new WorkerModel();因此還需要做進一步的判斷if (wm != null) {//3:把從前台傳來的密碼資料,使用相應的密碼編譯演算法進行加密運算String encryptPwd = this.encryptPwd(lm.getPwd());//如果工作人員存在,檢查工作人員編號和密碼是否匹配if (wm.getWorkerId().equals(lm.getWorkerId())&& wm.getPwd().equals(encryptPwd)) {return true;}}return false;}/** * 對密碼資料進行加密 * @param pwd 密碼資料 * @return 加密後的密碼資料 */private String encryptPwd(String pwd){//這裡對密碼進行加密,省略了return pwd;}/** * 根據工作人員編號擷取工作人員的詳細資料 * @param workerId 工作人員編號 * @return 對應的工作人員的詳細資料 */private WorkerModel findWorkerByWorkerId(String workerId) {// 這裡省略具體的處理,僅做示意,返回一個有預設資料的對象WorkerModel wm = new WorkerModel();wm.setWorkerId(workerId);wm.setName("Worker1");wm.setPwd("worker1");wm.setUuid("Worker0001");return wm;}}**********************************************************************package demo15.templatemethod.example2;/** * 描述工作人員資訊的資料模型 */public class WorkerModel {private String uuid,workerId,pwd,name;public String getUuid() {return uuid;}public void setUuid(String uuid) {this.uuid = uuid;}public String getWorkerId() {return workerId;}public void setWorkerId(String workerId) {this.workerId = workerId;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}public String getName() {return name;}public void setName(String name) {this.name = name;}}
2.有何問題

1.重複或者相似代碼太多了
2.擴充起來很不方便,如果改動一個功能的話,那麼兩種使用者可能都要更改。
3.使用模板方法模式來解決問題3.1定義

定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可以重新定義該演算法的某些特定步驟。
3.2模板方法的結構和說明

模板方法就是要抽象出相同或者類似的,找到變與不變的地方,那麼就可以使用模板方法模式了。
結構圖如下
 
3.3模板方法模式範例程式碼

package demo15.templatemethod.example3;/** * 定義模板方法、原語操作等的抽象類別 */public abstract class AbstractClass {/** * 原語操作1,所謂原語操作就是抽象的操作,必須要由子類提供實現 */public abstract void doPrimitiveOperation1();/** * 原語操作2 */public abstract void doPrimitiveOperation2();/** * 模板方法,定義演算法骨架 */public final void templateMethod() {doPrimitiveOperation1();doPrimitiveOperation2();}}********************************************************************package demo15.templatemethod.example3;/** * 具體實作類別,實現原語操作 */public class ConcreteClass extends AbstractClass {public void doPrimitiveOperation1() { //具體的實現}public void doPrimitiveOperation2() { //具體的實現}}
4.使用模板方法重寫樣本4.1LoginModel不變,下面是定義公用的登入控制演算法
package demo15.templatemethod.example4;/** *登入控制的模板 */public abstract class LoginTemplate {/** * 判斷登入資料是否正確,也就是是否能登入成功 * @param lm 封裝登入資料的Model * @return true表示登入成功,false表示登入失敗 */public final boolean login(LoginModel lm){//1:根據登入人員的編號去擷取相應的資料LoginModel dbLm = this.findLoginUser(lm.getLoginId());if(dbLm!=null){//2:對密碼進行加密String encryptPwd = this.encryptPwd(lm.getPwd());//把加密後的密碼設定回到登入資料模型裡面lm.setPwd(encryptPwd);//3:判斷是否匹配return this.match(lm, dbLm);}return false;}/** * 根據登入編號來尋找和擷取儲存中相應的資料 * @param loginId 登入編號 * @return 登入編號在儲存中相對應的資料 */public abstract LoginModel findLoginUser(String loginId);/** * 對密碼資料進行加密 * @param pwd 密碼資料 * @return 加密後的密碼資料 */public String encryptPwd(String pwd){return pwd;}/** * 判斷使用者填寫的登入資料和儲存中對應的資料是否匹配得上 * @param lm 使用者填寫的登入資料 * @param dbLm 在儲存中對應的資料 * @return true表示匹配成功,false表示匹配失敗 */public boolean match(LoginModel lm,LoginModel dbLm){if(lm.getLoginId().equals(dbLm.getLoginId()) && lm.getPwd().equals(dbLm.getPwd())){return true;}return false;}}
4.2普通使用者
package demo15.templatemethod.example4;/** * 普通使用者登入控制的邏輯處理 */public class NormalLogin extends LoginTemplate{public LoginModel findLoginUser(String loginId) {// 這裡省略具體的處理,僅做示意,返回一個有預設資料的對象LoginModel lm = new LoginModel();lm.setLoginId(loginId);lm.setPwd("testpwd");return lm;}}
4.3工作人員登陸
package demo15.templatemethod.example4;/** * 工作人員登入控制的邏輯處理 */public class WorkerLogin extends LoginTemplate{public LoginModel findLoginUser(String loginId) {// 這裡省略具體的處理,僅做示意,返回一個有預設資料的對象LoginModel lm = new LoginModel();lm.setLoginId(loginId);lm.setPwd("workerpwd");return lm;}public String encryptPwd(String pwd){//覆蓋父類的方法,提供真正的加密實現//這裡對密碼進行加密,比如使用:MD5、3DES等等,省略了System.out.println("使用MD5進行密碼加密");return pwd;}}
4.4用戶端使用
package demo15.templatemethod.example4;public class Client {public static void main(String[] args) {//準備登入人的資訊LoginModel lm = new LoginModel();lm.setLoginId("admin");lm.setPwd("workerpwd");//準備用來進行判斷的對象LoginTemplate lt = new WorkerLogin();LoginTemplate lt2 = new NormalLogin();//進行登入測試boolean flag = lt.login(lm);System.out.println("可以登入工作平台="+flag);boolean flag2 = lt2.login(lm);System.out.println("可以進行普通人員登入="+flag2);}}
5.模式講解5.1要點

功能:在於固定演算法骨架,而讓具體演算法實現可擴充。
為啥不是介面:介面是一種特殊的抽象類別,抽象類別不一定包含抽象方法,有抽象方法的一定是抽象類別。
什麼時候使用抽象類別:紀要約束子類的行為,又要為子類提供公用功能
好萊塢法則:不要找我們,我們會聯絡你。作為父類的模板會在需要的時候調用子類相應的方法,也就是父類來找子類,而不是子類找父類。
模板方法很好的體現了開閉原則和裡氏替換原則
java回調技術,就是一種特殊的模板方法模式

5.2本質

固定演算法骨架
5.3優缺點

優點:實現代碼的複用
缺點:演算法骨架不容易升級

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.