首先介紹Java程序設計的兩個重要思想DRY和OAOO:
DRY:Don’t Repeat Yourself 不要重複你自己(也叫做DIE:Duplication Is Evil 重複就是魔鬼)
OAOO: Only and Once Only 一次且僅一次
這兩個思想略有區別:OAOO要求的是同樣的代碼只能出現一次且一次足矣;
DRY 要求的是邏輯、常量、標準、功能、服務不要重現重複,範圍比OAOO更加廣。
介紹這個設計模式前,先舉個應用例子:
我們去喫午飯:1進入餐館 ,2點菜 ,3喫掉
public class Party {
public void partyDinner(){
//do something
System.out.println("進入飯館");
//do something
System.out.println("來一份菜");
//do something
System.out.println("喫掉");
}
}
但是,我們點菜不一樣,有點魚,有點肉的。若我們直接定義類 爲 點肉 和 點魚 的人進行服務
public class Party4Meat {
public void partyDinner(){
//do something
System.out.println("進入飯館");
//do something
System.out.println("來一份肉");
//do something
System.out.println("喫掉");
}
}
public class Party4Fish {
public void partyDinner(){
//do something
System.out.println("進入飯館");
//do something
System.out.println("來一份魚");
//do something
System.out.println("喫掉");
}
}
我們不難發現我們重複進行定義了進”餐館”和”喫掉”這兩個邏輯。不僅如此,這樣寫出來的程序極難維護,當你的eat需要修改時,你要找到所有的eat邏輯進行修改。
可以看到,在這個例子中,不管點菜的需求怎麼變,entry和eat都不會改變,變的只是order的邏輯。
爲此我們引入模板方法來減少這種repeat!
讓party中的三種邏輯變成三種方法,其中會發生變化的邏輯延遲到子類中去實現。即把party變成抽象父類,把order變成一個抽象的方法讓子類去實現,同時,我們還需要在父類中定義一個邏輯方法,讓我們拆分後的邏輯仍是原先的邏輯。
public abstract class Party {
/**
* 對外提供服務,並用來保持原有的邏輯
* @author majin
*/
public void partyDinner(){
entry();//entry a restaurant
order();//the method will be rewrite by subclass
eat();//eat your dinner
}
private void entry() {System.out.println("進餐館");}
/**
* the method will have deferent realization in subclass
* @author majin
*/
protected abstract void order();
private void eat() {System.out.println("喫");}
}
//in subclass
public class Party4Meat extends Party{
@Override
protected void order() {
System.out.println("來一份肉");
}
}
//同樣可定義其他的類,爲不同點單的人服務
到此,我們使用這種設計模式到底產生了什麼樣的好處?
1.增加了程序的可維護性和可擴展性。
2.減少了代碼的冗餘,使代碼更加容易閱讀。
3.父類中某些算法的步驟延遲到子類中其實現。
4.父類提供了算法的框架、算法的控制流程,子類不能改變方法的執行流程,子類方法的調用有父類來決定。
5.爲了增強封裝性,可以把父類中的不想讓子類修改的方法 設成 private 或者是 protected final
====================================================
那麼,問題又來了,你把每一個要作的服務類型都定義出一個子類來,那我的服務類型很多怎麼辦啊?
爲了解決子類氾濫的問題,我們引入 回調(callback)。
相信回調這個詞,對大家來說都不會陌生。在c語言中,使用了函數指針來完成的回調;在c#中,使用了代理(delegate)來實現的……不一一列舉了(別的我也不知道)。在我們大java中使用的是匿名內部類完成的回調。簡單理解回調,在適當的時候調用一段邏輯的引用。
在數據庫操作時,
1.get connection
2.get statement
3.excute and get result
一、二步對每次查詢來說都是相同的,只有第三步的查詢結果處理不一樣。我們若是考慮上面的設計思路,那我們要創建的子類會有多少那並且你不可能設計出所有的可能處理。
import java.sql.ResultSet;
/**
* @author majin
*
* @param <T> 要返回的結果類型
*/
public interface SetresultHandler<T> {
public T hanle(ResultSet rs);
}
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* 對外提供jdbc的查詢功能,查詢結果的處理依賴於 回調函數ResultSetHandler.query()
* @author majin
*
*/
public class SimpleJDBCDemo {
public <T> T query(String queryStr, ResultSetHandler<T> handler) {
//url name password is need
String url=" ", user=" ", password=" ";
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
try {
//class is need
Class.forName("");
connection = DriverManager.getConnection(url, user, password);
stmt = connection.createStatement();
rs = stmt.executeQuery(queryStr);
} catch (Exception e) {
//do something and close resource
}
return handler.hanle(rs);//return handle result
}
}
//use SimpleJDBCDemo
public void main(String args[]){
new SimpleJDBCDemo().query("select * from emp", new ResultSetHandler<Boolean>() {
@Override
public Boolean hanle(ResultSet rs) {
//do something to rs
return true;
}
});
}
這樣我們就解決了子類過多的煩惱。
相信,熟悉android開發的朋友,對上面這種簡單的內部類不會陌生。
據說,這種 模板方法 + 回調 在Spring中使用非常的廣泛。
===================================================
最後,總結一下,模板方法讓我們減少了代碼的冗餘,增加了代碼的重用性,使代碼容易閱讀和維護。但是,單純的使用模板方法會讓我們的類越來越多。於是,我們引入了回調—匿名內部類。但是,匿名內部類又帶來另外的問題,當我們要實現的接口比較多、實現代碼比較長時,匿名內部類的弊端就出現了,看得人頭疼啊!那麼,就需要我們適時的使用他們了。
最後,吐槽一下,UML咋加上去啊!