????一看到XXXTemplate,首先想到的是什么?對(duì),沒(méi)錯(cuò)!就是Spring框架的JdbcTemplate。那么我們來(lái)說(shuō)說(shuō)如何使用設(shè)計(jì)模式和泛型來(lái)實(shí)現(xiàn)一個(gè)JedisTemplate 。
模版方法模式
在模板模式(Template Pattern)中,一個(gè)抽象類(lèi)公開(kāi)定義了執(zhí)行它的方法的方式/模板。它的子類(lèi)可以按需要重寫(xiě)方法實(shí)現(xiàn),但調(diào)用將以抽象類(lèi)中定義的方式進(jìn)行。這種類(lèi)型的設(shè)計(jì)模式屬于行為型模式。
意圖:定義一個(gè)操作中的算法的骨架,而將一些步驟延遲到子類(lèi)中。模板方法使得子類(lèi)可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。
主要解決:一些方法通用,卻在每一個(gè)子類(lèi)都重新寫(xiě)了這一方法。
何時(shí)使用:有一些通用的方法。
如何解決:將這些通用算法抽象出來(lái)。
關(guān)鍵代碼:在抽象類(lèi)實(shí)現(xiàn),其他步驟在子類(lèi)實(shí)現(xiàn)。
應(yīng)用實(shí)例:?
1、在造房子的時(shí)候,地基、走線(xiàn)、水管都一樣,只有在建筑的后期才有加壁櫥加?xùn)艡诘炔町悺?
2、西游記里面菩薩定好的 81 難,這就是一個(gè)頂層的邏輯骨架。?
3、spring 中對(duì) Hibernate 的支持,將一些已經(jīng)定好的方法封裝起來(lái),比如開(kāi)啟事務(wù)、獲取 Session、關(guān)閉 Session 等,程序員不重復(fù)寫(xiě)那些已經(jīng)規(guī)范好的代碼,直接丟一個(gè)實(shí)體就可以保存。
優(yōu)點(diǎn):?1、封裝不變部分,擴(kuò)展可變部分。 2、提取公共代碼,便于維護(hù)。 3、行為由父類(lèi)控制,子類(lèi)實(shí)現(xiàn)。
缺點(diǎn):每一個(gè)不同的實(shí)現(xiàn)都需要一個(gè)子類(lèi)來(lái)實(shí)現(xiàn),導(dǎo)致類(lèi)的個(gè)數(shù)增加,使得系統(tǒng)更加龐大。
使用場(chǎng)景:?1、有多個(gè)子類(lèi)共有的方法,且邏輯相同。 2、重要的、復(fù)雜的方法,可以考慮作為模板方法。
注意事項(xiàng):為防止惡意操作,一般模板方法都加上 final 關(guān)鍵詞。
實(shí)現(xiàn)
我們將創(chuàng)建一個(gè)定義操作的?Game?抽象類(lèi),其中,模板方法設(shè)置為 final,這樣它就不會(huì)被重寫(xiě)。Cricket?和?Football?是擴(kuò)展了?Game?的實(shí)體類(lèi),它們重寫(xiě)了抽象類(lèi)的方法。
TemplatePatternDemo,我們的演示類(lèi)使用?Game?來(lái)演示模板模式的用法。
Java 泛型
Java 泛型(generics)是 JDK 5 中引入的一個(gè)新特性, 泛型提供了編譯時(shí)類(lèi)型安全檢測(cè)機(jī)制,該機(jī)制允許程序員在編譯時(shí)檢測(cè)到非法的類(lèi)型。
泛型的本質(zhì)是參數(shù)化類(lèi)型,也就是說(shuō)所操作的數(shù)據(jù)類(lèi)型被指定為一個(gè)參數(shù)。
假定我們有這樣一個(gè)需求:寫(xiě)一個(gè)排序方法,能夠?qū)φ蛿?shù)組、字符串?dāng)?shù)組甚至其他任何類(lèi)型的數(shù)組進(jìn)行排序,該如何實(shí)現(xiàn)?
答案是可以使用?Java 泛型。
使用 Java 泛型的概念,我們可以寫(xiě)一個(gè)泛型方法來(lái)對(duì)一個(gè)對(duì)象數(shù)組排序。然后,調(diào)用該泛型方法來(lái)對(duì)整型數(shù)組、浮點(diǎn)數(shù)數(shù)組、字符串?dāng)?shù)組等進(jìn)行排序。
泛型方法
你可以寫(xiě)一個(gè)泛型方法,該方法在調(diào)用時(shí)可以接收不同類(lèi)型的參數(shù)。根據(jù)傳遞給泛型方法的參數(shù)類(lèi)型,編譯器適當(dāng)?shù)靥幚砻恳粋€(gè)方法調(diào)用。
下面是定義泛型方法的規(guī)則:
所有泛型方法聲明都有一個(gè)類(lèi)型參數(shù)聲明部分(由尖括號(hào)分隔),該類(lèi)型參數(shù)聲明部分在方法返回類(lèi)型之前(在下面例子中的<E>)。
每一個(gè)類(lèi)型參數(shù)聲明部分包含一個(gè)或多個(gè)類(lèi)型參數(shù),參數(shù)間用逗號(hào)隔開(kāi)。一個(gè)泛型參數(shù),也被稱(chēng)為一個(gè)類(lèi)型變量,是用于指定一個(gè)泛型類(lèi)型名稱(chēng)的標(biāo)識(shí)符。
類(lèi)型參數(shù)能被用來(lái)聲明返回值類(lèi)型,并且能作為泛型方法得到的實(shí)際參數(shù)類(lèi)型的占位符。
泛型方法體的聲明和其他方法一樣。注意類(lèi)型參數(shù)只能代表引用型類(lèi)型,不能是原始類(lèi)型(像int,double,char的等)。
JedisTemplate實(shí)現(xiàn)
JedisTemplate是公司前輩封裝的一個(gè)redis包里的一個(gè)類(lèi),其實(shí)可以在很多地方看到類(lèi)似的封裝,并不復(fù)雜的類(lèi),這里借這個(gè)類(lèi)分享一下寫(xiě)代碼的技巧--簡(jiǎn)潔優(yōu)雅。
在這里能看到:
通過(guò)嵌套類(lèi)減少文件數(shù)量
模板方法設(shè)計(jì)模式如何減少重復(fù)代碼
泛型的寫(xiě)法,讓你少些類(lèi)和方法的利器
最重要的?。?!怎么提高碼代碼功力的方法----有基礎(chǔ)后,讀優(yōu)秀源碼,吸收模仿,最后隨心所欲的寫(xiě)。千萬(wàn)別止步于使用別人的代碼,難道你就不想品味
不啰嗦,上代碼
/**
* 提供對(duì)Redis常用命令的封裝,支持pipeline模式
*/
public class JedisStandAloneTemplate extends JedisTemplate {
private static Logger logger = LoggerFactory.getLogger(JedisStandAloneTemplate.class);
private JedisPool jedisPool;
public JedisStandAloneTemplate(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
/**
* Callback interface for template.
*/
// ==筆記1== 嵌套接口類(lèi),通常用于回調(diào),放在內(nèi)部能有效減少文件數(shù)量,降低閱讀障礙,而且主要是內(nèi)部使用。
// ==筆記2== 這里只是定義接口,通常配合模板方法使用。
//? ? ? 使用時(shí),new一個(gè)某種功能(get/del)的匿名實(shí)現(xiàn)類(lèi)作為參數(shù)傳入模板方法。
public interface JedisAction<T> {
// ==筆記3== jedis作為參數(shù),有模板方法提供,作為一種資源,無(wú)需定義一個(gè)類(lèi)屬性。
T action(Jedis jedis);
}
/**
* Callback interface for template without result.
*/
public interface JedisActionNoResult {
void action(Jedis jedis);
}
/**
* Execute with a call back action with result.
*/
// ==筆記4== 模板方法。通常用于消除冗余代碼,每個(gè)jedis調(diào)用都會(huì)涉及get連接,釋放連接,異常處理。
//? ? ? 唯一不同的地方是action,做的業(yè)務(wù)操作不同。
// ==筆記5== so我們把此業(yè)務(wù)操作抽象成一個(gè)接口,所有的具體業(yè)務(wù)操作類(lèi)實(shí)現(xiàn)此接口。
//? ? ? 為了簡(jiǎn)化代碼,我們使用匿名實(shí)現(xiàn)類(lèi)。為了簡(jiǎn)化使用,我們?cè)诖祟?lèi)暴露出使用優(yōu)化的方法(get/del)
// ==筆記6== 在此對(duì)外方法中,調(diào)用模板方法,new一個(gè)匿名實(shí)現(xiàn)類(lèi)作為入?yún)?,傳入模板方法,最終就像模板那樣。
//? ? ? 所有的公共操作都是一樣的,只有具體的業(yè)務(wù)操作被替換了。
// ==筆記7== 雖然看起來(lái)很好了,但是action接口返回值類(lèi)型是變化的,可能是String Boolean,我們不能定義很多action接口,
//? ? ? ? 所以這時(shí)要引入泛型編程<T>。接口類(lèi)是泛型接口interface JedisAction<T>,
//? ? ? ? 模板方法是泛型方法<T> T execute(JedisAction<T> jedisAction)。這里的<T>是限定符。
public <T> T execute(JedisAction<T> jedisAction) throws JedisException {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
return jedisAction.action(jedis);
} catch (JedisException e) {
throw e;
} finally {
closeResource(jedis);
}
}
/**
* Return the internal JedisPool.
*/
public JedisPool getJedisPool() {
return jedisPool;
}
protected void closeResource(Jedis jedis) {
try {
if (jedis != null) {
jedis.close();
}
} catch (Exception e) {
logger.error("return back jedis failed, will fore close the jedis.", e);
JedisUtils.destroyJedis(jedis);
}
}
@Override
public Boolean del(final String... keys) {
return execute(new JedisAction<Boolean>() {
@Override
public Boolean action(Jedis jedis) {
return jedis.del(keys) == keys.length ? true : false;
}
});
}
@Override
public String get(final String key) {
return execute(new JedisAction<String>() {
@Override
public String action(Jedis jedis) {
return jedis.get(key);
}
});
}
/**
* Get the value of the specified key as Long.If the key does not exist null is returned.
*/
@Override
public Long getAsLong(final String key) {
String result = get(key);
return result != null ? Long.valueOf(result) : null;
}
/**
* Get the value of the specified key as Integer.If the key does not exist null is returned.
*/
@Override
public Integer getAsInt(final String key) {
String result = get(key);
return result != null ? Integer.valueOf(result) : null;
}
}