冒號(hào)課堂§10.1:多態(tài)類型
冒號(hào)課堂
第十課 多態(tài)機(jī)制(1)課前導(dǎo)讀
本課通過(guò)實(shí)例編程和對(duì)抽象類型的解讀,顯示了OOP中多態(tài)機(jī)制和抽象類型的重要性,有助于培養(yǎng)和加深讀者的OOP語(yǔ)感。
本課共分兩節(jié)——
1.多態(tài)類型——靜中之動(dòng)
2.抽象類型——實(shí)中之虛
動(dòng)靜屈伸,唯變所適
-
繼承是多態(tài)的基礎(chǔ),多態(tài)是繼承的目的
-
多態(tài)是動(dòng)靜結(jié)合的產(chǎn)物,將靜態(tài)類型的安全性和動(dòng)態(tài)類型的靈活性融為一體
-
前者(參數(shù)多態(tài))是發(fā)散式的,讓相同的實(shí)現(xiàn)代碼應(yīng)用于不同的場(chǎng)合
-
后者(包含多態(tài))是收斂式的,讓不同的實(shí)現(xiàn)代碼應(yīng)用于相同的場(chǎng)合
-
模板方法模式突出的是穩(wěn)定堅(jiān)固的骨架,策略模式突出的是靈活多變的手腕
-
多態(tài)與繼承有何關(guān)系?
-
多態(tài)的重要意義何在?
-
多態(tài)有哪幾種形式?它們各自有什么特點(diǎn)?
-
什么是策略模式?它與模板方法模式有何相同點(diǎn)和不同點(diǎn)?多態(tài)在其中起到了什么作用?
當(dāng)冒號(hào)邁著不變的步伐出現(xiàn)在教室時(shí),手上有了一點(diǎn)變化:左手仍拎著筆記本包,右手卻多了一樣?xùn)|西。大家定睛一看,原來(lái)是個(gè)電腦主板,不由得暗自納悶:難道軟件課改成了硬件課?
冒號(hào)照例直入主題:“上節(jié)課我們對(duì)繼承的利弊作了詳細(xì)的分析,其中最重要的觀點(diǎn)是:繼承的主要用途不是代碼重用,而是代碼被重用。這依賴于兩個(gè)前提,一個(gè)是在語(yǔ)義上遵循里氏代換原則,另一個(gè)是在語(yǔ)法上支持多態(tài)(polymorphism)機(jī)制。因此不妨說(shuō),對(duì)于靜態(tài)類型語(yǔ)言來(lái)說(shuō),繼承是多態(tài)的基礎(chǔ),多態(tài)是繼承的目的。”
問(wèn)號(hào)忍不住問(wèn):“為什么要強(qiáng)調(diào)靜態(tài)類型呢?”
“還記得鴨子類型[1]嗎?那就是一種不依賴于繼承的多態(tài)類型,也是動(dòng)態(tài)類型語(yǔ)言一大優(yōu)劣參半的特色。”冒號(hào)提醒道,“靜態(tài)類型語(yǔ)言中的多態(tài)是動(dòng)靜結(jié)合的產(chǎn)物,將靜態(tài)類型的安全性和動(dòng)態(tài)類型的靈活性融為一體。它一般有兩種實(shí)現(xiàn)方式:一種利用GP(泛型編程)中的參數(shù)多態(tài)(parametric polymorphism),一種利用OOP中的包含多態(tài)(inclusion polymorphism)或稱子類型多態(tài)(subtyping polymorphism)。從實(shí)現(xiàn)機(jī)制上看,二者的不同之處在于何時(shí)將一個(gè)變量與其實(shí)際類型所定義的行為掛鉤。前者在編譯期,屬于早綁定 (early binding)或靜態(tài)綁定(static binding)[2];后者在運(yùn)行期,屬于遲綁定 (late binding)或動(dòng)態(tài)綁定(dynamic binding)。從應(yīng)用形式上看,前者是發(fā)散式的,讓相同的實(shí)現(xiàn)代碼應(yīng)用于不同的場(chǎng)合;后者是收斂式的,讓不同的實(shí)現(xiàn)代碼應(yīng)用于相同的場(chǎng)合。從思維方式上看,前者是泛型式編程風(fēng)格,看重的是算法的普適性;后者是對(duì)象式編程風(fēng)格,看重的是接口與實(shí)現(xiàn)的分離度。盡管二者從范式到語(yǔ)法、語(yǔ)義都大相徑庭,但都是為著同一個(gè)目的:在保證必要的類型安全的前提下,突破編譯期間過(guò)于嚴(yán)苛的類型限制。對(duì)于既是靜態(tài)類型語(yǔ)言又是靜態(tài)語(yǔ)言、既支持OOP又支持GP的C++、Java和C#而言,多態(tài)機(jī)制是保證代碼的靈活性、可維護(hù)性和可重用性的終極武器。為了說(shuō)明問(wèn)題,我們看一個(gè)簡(jiǎn)單而實(shí)用的例子:編寫一個(gè)類,讓它能儲(chǔ)存用戶名和密碼,以作今后驗(yàn)證之用。”
嘆號(hào)一愣:“這題是不是太簡(jiǎn)單了?還有別的要求嗎?”
冒號(hào)搖搖頭。
引號(hào)卻認(rèn)為:“要求太少反而不好做。比如是把數(shù)據(jù)放在內(nèi)存、還是文件或者數(shù)據(jù)庫(kù)?密碼以明文還是密文的形式存儲(chǔ)?”
句號(hào)提出:“無(wú)論是數(shù)據(jù)的存放方式還是密碼的加密方式,都不應(yīng)該硬編碼。”
“循此思路,我們就來(lái)編寫一個(gè)可重用的抽象類。”冒號(hào)投放了一段Java代碼——
abstract class Authenticator
{
/** 保存用戶名和密碼 */
final public void save(String user, String password)
{
if (password == null)
password = "";
store(user, encrypt(password));
}
/** 驗(yàn)證用戶名和密碼 */
final public boolean authenticate(String user, String password)
{
String storedPassword = retrieve(user);
if (storedPassword == null) return false; // 無(wú)此用戶
if (password == null)
password = "";
return storedPassword.equals(encrypt(password));
}
/** 保存用戶名和加密過(guò)的密碼 */
protected abstract void store(String user, String encryptedPassword);
/** 從用戶名獲取相應(yīng)的加密過(guò)的密碼 */
protected abstract String retrieve(String user);
/** 給明文單向(one-way)加密,默認(rèn)不加密 */
protected String encrypt(String text) { return text; }
}
冒號(hào)解說(shuō)道:“該抽象類有兩個(gè)public接口,一個(gè)用來(lái)保存,一個(gè)用來(lái)驗(yàn)證。它們用final修飾符來(lái)禁止子類覆蓋,因?yàn)檎嬲臄U(kuò)展點(diǎn)是三個(gè)protected方法。其中store和retrieve是抽象的,encrypt有一個(gè)平凡實(shí)現(xiàn)。以此為基礎(chǔ),再根據(jù)實(shí)際需要來(lái)編寫子類,具體實(shí)現(xiàn)這三個(gè)方法。”
幻燈片轉(zhuǎn)到下一頁(yè)——
import java.util.HashMap;
/** 一個(gè)簡(jiǎn)單的驗(yàn)證類,數(shù)據(jù)放在內(nèi)存,密碼保持明文 */
class SimpleAuthenticator extends Authenticator
{
private Map<String, String> usrPwd = new HashMap<String, String>();
@Override protected void store(String user, String encryptedPassword)
{
usrPwd.put(user, encryptedPassword);
}
@Override protected String retrieve(String user)
{
return usrPwd.get(user);
}
}
“我們利用HashMap來(lái)儲(chǔ)存數(shù)據(jù),密碼保持明文。這大概是最簡(jiǎn)單的一種子類了。”冒號(hào)仿佛在輕輕地把玩著一件小物什,“為安全起見(jiàn),最好還是將密碼加密。于是我們?cè)O(shè)計(jì)了稍微復(fù)雜一點(diǎn)的子類——”
/** 一個(gè)安全的驗(yàn)證類,數(shù)據(jù)放在內(nèi)存,密碼經(jīng)過(guò)SHA-1加密 */
class Sha1Authenticator extends SimpleAuthenticator
{
private static final String ALGORITHM = "SHA-1"; // SHA-1算法
private static final String CHARSET = "UTF-8"; // 避免依賴平臺(tái)
@Override protected String encrypt(String plainText)
{
try
{
MessageDigest md = MessageDigest.getInstance(ALGORITHM);
md.update(plainText.getBytes(CHARSET));
byte digest[] = md.digest();
// BASE64編碼比十六進(jìn)制編碼節(jié)省空間
//為簡(jiǎn)便起見(jiàn)用到了非標(biāo)準(zhǔn)的API,因此以下代碼有警告
return (new sun.misc.BASE64Encoder()).encode(digest);
}
catch (java.security.NoSuchAlgorithmException e)
{
throw new InternalError(e.getMessage()); // 不可能發(fā)生
}
catch (java.io.UnsupportedEncodingException e)
{
throw new InternalError(e.getMessage()); // 不可能發(fā)生
}
}
}
逗號(hào)質(zhì)疑道:“不是具體類不宜被繼承的嗎?怎么Sha1Authenticator類卻繼承了具體類SimpleAuthenticator?”
冒號(hào)略表贊許:“很高興你沒(méi)有忘記這個(gè)原則。不過(guò)考慮到Sha1Authenticator類需要覆蓋父類的encrypt方法,這么做也是情有可原的。當(dāng)然最好選擇讓該類直接繼承抽象類Authenticator,但作為示例代碼,我們還是希望它簡(jiǎn)潔一些,不想讓過(guò)多的細(xì)枝末節(jié)掩蓋核心主干。下面是測(cè)試代碼——”
{ // 為避免額外依賴,沒(méi)有采用JUnit等單元測(cè)試工具
public static void main(String[] args)
{
test(new SimpleAuthenticator());
test(new Sha1Authenticator());
}
// 測(cè)試給定的Authenticator
private static void test(Authenticator authenticator) // 子類型多態(tài)
{
test(authenticator, "user", "password");
test(authenticator, "user", "newPassword");
test(authenticator, "admin", "admin");
test(authenticator, "guest", null);
test(authenticator, null, "pass");
authenticator.save("scott", "tiger");
assert(!authenticator.authenticate("scott", "TIGER")); // 大小寫敏感
assert(!authenticator.authenticate("SCOTT", "tiger")); // 大小寫敏感
}
private static void test(Authenticator authenticator, String user, String password)
{
authenticator.save(user, password);
assert(authenticator.authenticate(user, password));
}
}
引號(hào)覺(jué)得眼熟:“這不是上節(jié)課講的模板方法模式嗎?”
“正是此公。”冒號(hào)確認(rèn),“該模式的核心思想是:固定整體框架和流程以保證可重用性,留出一些子類定制點(diǎn)以保證可擴(kuò)展性。在測(cè)試代碼的兩個(gè)test方法中,傳入的參數(shù)是Authenticator類,但數(shù)據(jù)存放和密碼加密的方式是在運(yùn)行中才確定的,即先后遵照SimpleAuthenticator類和Sha1Authenticator類的實(shí)現(xiàn)。這就是我們所說(shuō)的子類型多態(tài)的效果——讓不同的實(shí)現(xiàn)代碼應(yīng)用于相同的場(chǎng)合。假設(shè)沒(méi)有多態(tài)機(jī)制,這種效果就只能靠if/else或switch之類的條件語(yǔ)句才能實(shí)現(xiàn),非常地痛苦。”
冒號(hào)的眉頭皺成了粗體的“川”字。
“還有更好的方法嗎?”句號(hào)察言觀色,斷定老冒還留有后手。
果不其然,冒號(hào)的眉毛立刻又舒展開(kāi)來(lái),中氣充沛地應(yīng)道:“有!諸位請(qǐng)看——”
interface KeyValueKeeper
{
public void store(String key, String value);
public String retrieve(String key);
}
// 加密接口
interface Encrypter
{
public String encrypt(String plainText);
}
class Authenticator
{
private KeyValueKeeper keeper;
private Encrypter encrypter;
public Authenticator(KeyValueKeeper keeper, Encrypter encrypter)
{
this.keeper = keeper;
this.encrypter = encrypter;
}
public void save(String user, String password)
{
if (password == null)
password = "";
keeper.store(user, encrypter.encrypt(password));
}
public boolean authenticate(String user, String password)
{
String storedPassword = keeper.retrieve(user);
if (storedPassword == null) return false;
if (password == null)
password = "";
return storedPassword.equals(encrypter.encrypt(password));
}
}
冒號(hào)加以引導(dǎo):“如果仔細(xì)比較兩種設(shè)計(jì),就會(huì)發(fā)現(xiàn)它們很相似。后者只不過(guò)把前者對(duì)子類開(kāi)放的接口合成為自己的兩個(gè)成員。再看接口的實(shí)現(xiàn)類——”
{
private Map<String, String> keyValue = new HashMap<String, String>();
@Override public void store(String key, String value)
{
keyValue.put(key, value);
}
@Override public String retrieve(String key)
{
return keyValue.get(key);
}
}
class PlainEncrypter implements Encrypter
{
@Override public String encrypt(String plainText)
{
return plainText;
}
}
class Sha1Encrypter implements Encrypter
{
private static final String ALGORITHM = "SHA-1";
private static final String CHARSET = "UTF-8";
@Override public String encrypt(String plainText)
{
try
{
MessageDigest md = MessageDigest.getInstance(ALGORITHM);
md.update(plainText.getBytes(CHARSET));
byte digest[] = md.digest();
return (new sun.misc.BASE64Encoder()).encode(digest);
}
catch (java.security.NoSuchAlgorithmException e)
{
throw new InternalError(e.getMessage());
}
catch (java.io.UnsupportedEncodingException e)
{
throw new InternalError(e.getMessage());
}
}
}
逗號(hào)比較后得出結(jié)論:“MemoryKeeper與SimpleAuthenticator、Sha1Encrypter與Sha1Authenticator除了超類型和方法訪問(wèn)修飾符外,其他毫無(wú)二致。”
屏幕滾動(dòng)出另一段代碼——
{
public static void main(String[] args)
{
test(new Authenticator(new MemoryKeeper(), new PlainEncrypter()));
test(new Authenticator(new MemoryKeeper(), new Sha1Encrypter()));
}
private static void test(Authenticator authenticator) // 隱含子類型多態(tài)
{ /* 同上,略 */}
}
“測(cè)試代碼區(qū)別也不大,只是Authenticator的多態(tài)性更加隱蔽。”冒號(hào)如是說(shuō)。
嘆號(hào)挑剔說(shuō):“后一種創(chuàng)建實(shí)例稍顯麻煩一些。”
“但它是以小弊換大利。”冒號(hào)朗聲而道,“首先,后者用的是合成與接口繼承,比前者的實(shí)現(xiàn)繼承更值得推薦,理由在上堂課業(yè)已闡明。其次,假設(shè)共有M種數(shù)據(jù)存取方式,包括內(nèi)存、文件、數(shù)據(jù)庫(kù)等等;共有N種加密方式,包括明文、SHA-1、SHA-256、MD5等等。按第一種設(shè)計(jì),需要(M×N)個(gè)實(shí)現(xiàn)類;按第二種設(shè)計(jì),只要(M+N)個(gè)實(shí)現(xiàn)類。這還只是兩種變化因素,假如需要考慮更多的因素,二者差距將更大。比如增加編碼方式:加密后的數(shù)據(jù)可以選擇費(fèi)空間省時(shí)間的十六進(jìn)制編碼、費(fèi)時(shí)間省空間的BASE64編碼、省時(shí)間省空間卻包含非打印字符的原始形式等;比如增加安全強(qiáng)度:引入salt、nonce或IV等[3];比如增加密碼狀態(tài):已生效密碼、未生效密碼、已過(guò)期密碼等等。對(duì)比下面的UML類圖,孰優(yōu)孰劣更加一目了然。”
眾人眼前出現(xiàn)了兩幅圖——
冒號(hào)指著屏幕問(wèn):“圖二不僅比圖一少了三個(gè)實(shí)現(xiàn)類,而且可重用性也更高。大家說(shuō)是為什么?”
引號(hào)應(yīng)答:“圖一中的九個(gè)Authenticator的子類只能作為驗(yàn)證類來(lái)重用,而圖二中六個(gè)實(shí)現(xiàn)類不僅可以合作完成驗(yàn)證類的功能,還能分別單獨(dú)提供鍵值存儲(chǔ)和加密字符串的功能。”
冒號(hào)作出肯定:“這就是職責(zé)分離的好處。存儲(chǔ)與加密本是兩樣不相干的工作,必要時(shí)可以合作,但平時(shí)最好分開(kāi)管理,符合‘低耦合、高內(nèi)聚’的原則。”
問(wèn)號(hào)注意到圖中的注釋,遂問(wèn):“第二種采用的是策略模式?”
冒號(hào)頷首:“簡(jiǎn)單地說(shuō),策略模式(strategy pattern或policy pattern)的基本思想是:把一個(gè)模塊所依賴的某類算法委交其他模塊實(shí)現(xiàn)。比如Java中的Comparable和Comparator、C#中的IComparer就是比較算法的接口,當(dāng)一個(gè)類的某個(gè)方法接收了此種類型的參數(shù),實(shí)質(zhì)上就采用了策略模式。”
逗號(hào)不以為奇:“這豈非很平常?”
“你認(rèn)為設(shè)計(jì)模式真的高不可攀嗎?”冒號(hào)反問(wèn)道,“包括模板方法模式,你們很可能也在編程實(shí)踐中采用過(guò),只不過(guò)相交不相識(shí)罷了。”
句號(hào)看出:“模板方法模式與策略模式非常神似,都是把一個(gè)類的可變部分移交給其他類處理。”
“照你這么說(shuō),絕大多數(shù)設(shè)計(jì)模式都是神似的,這也是為什么我們不專門談設(shè)計(jì)模式的緣故。GoF設(shè)計(jì)模式是OOP大樹(shù)上結(jié)出的碩果,在你心中培養(yǎng)的OOP成熟之前,匆忙締結(jié)的果實(shí)多半是青澀弱小的。”冒號(hào)忠告,“我們也不會(huì)對(duì)設(shè)計(jì)模式避而不談,但凡提及都是水到渠成的產(chǎn)物。再說(shuō)回這兩種設(shè)計(jì)模式,雖然有相通的思想,也能解決相同的問(wèn)題,在穩(wěn)定性與靈活性之間都取得了某種平衡,但還是各有側(cè)重的。模板方法模式突出的是穩(wěn)定堅(jiān)固的骨架,策略模式突出的是靈活多變的手腕。不妨拿國(guó)家政策作比:一個(gè)強(qiáng)調(diào)對(duì)內(nèi)要穩(wěn),老一輩制訂了大政方針,下一代必須在堅(jiān)持原則的前提下進(jìn)行完善;一個(gè)強(qiáng)調(diào)對(duì)外要活,不能或不便自行開(kāi)發(fā)的技術(shù)不妨從國(guó)外引進(jìn)。”
嘆號(hào)一樂(lè):“哈!設(shè)計(jì)模式上升到了政策模式。”
冒號(hào)抽絲剝繭:“正如模板方法模式可看作控制反轉(zhuǎn)的特例,策略模式與依賴注射(Dependency Injection)也異曲同工。第二個(gè)Authenticator所依賴的兩個(gè)功能KeyValueKeeper和Encrypter,就是是通過(guò)構(gòu)造方法‘注射’進(jìn)來(lái)的[4]。當(dāng)然策略只是一種特殊的依賴,是自內(nèi)而外的——將算法抽出來(lái)外包;依賴注射的機(jī)制更復(fù)雜、涵蓋面更廣,是自外而內(nèi)的——從外部嵌入定制功能。后者被廣泛地用于框架應(yīng)用之中,尤以Spring Framework和Google Guice為代表。”
引號(hào)聽(tīng)得起勁:“這下熱鬧了,設(shè)計(jì)模式、框架與OOP范式全攪和到一塊了。”
“還有GP范式呢。”冒號(hào)順接話題,“讓我們?cè)儆肅++的模板來(lái)實(shí)現(xiàn)一下Authenticator類吧。沒(méi)有繼續(xù)采用Java,是因?yàn)樗姆盒腿噪x不開(kāi)子類型多態(tài)。”
說(shuō)著,他換上了C++代碼——
#include <map>
using namespace std;
template <typename KeyValueKeeper, typename Encrypter>
class Authenticator
{
private:
KeyValueKeeper keeper;
Encrypter encrypter;
public:
void save(const string& user, const string& password)
{
keeper.store(user, encrypter.encrypt(password));
}
bool authenticate(const string& user, const string& password) const
{
string storedPassword;
if (!keeper.retrieve(user, storedPassword)) return false;
return storedPassword == encrypter.encrypt(password);
}
};
class MemoryKeeper
{
private:
map<string, string> keyValue;
public:
void store(const string& key, const string& value)
{
keyValue[key] = value;
}
bool retrieve(const string& key, string& value) const
{
map<string, string>::const_iterator itr = keyValue.find(key);
if (itr == keyValue.end()) return false;
value = itr->second;
return true;
}
};
class PlainEncrypter
{
public:
string encrypt(const string& plainText) const { return plainText; }
};
class Sha1Encrypter
{
public:
string encrypt(const string& plainText) const { /* 省略代碼 */ }
};
namespace
{
template <typename K, typename E>
void test(Authenticator<K, E> authenticator) // 參數(shù)多態(tài)
{ /* 省略代碼 */ }
}
int main()
{
test(Authenticator<MemoryKeeper, PlainEncrypter>());
test(Authenticator<MemoryKeeper, Sha1Encrypter>());
return 0;
}
“以上代碼與Java版的策略模式代碼很相似,主要的區(qū)別是把KeyValueKeeper和Encrypter兩個(gè)接口換成了模板參數(shù)。由于模板是在編譯期間實(shí)例化的,因此沒(méi)有動(dòng)態(tài)綁定的運(yùn)行開(kāi)銷,但缺點(diǎn)是不能動(dòng)態(tài)改變策略[5]。”冒號(hào)分析道,“至此,我們通過(guò)一個(gè)驗(yàn)證類的三種解法,分別展示了三種形式的多態(tài):基于類繼承的多態(tài)、基于接口繼承的多態(tài)和基于模板的多態(tài)。它們殊途同歸,都能讓代碼更簡(jiǎn)潔、更靈活、可重用性更高、更易維護(hù)和擴(kuò)展。”
問(wèn)號(hào)想到一個(gè)問(wèn)題:“C語(yǔ)言既沒(méi)有子類型多態(tài)也沒(méi)有參數(shù)多態(tài),又如何保證高質(zhì)量的C程序呢?”
冒號(hào)眉梢輕挑:“C語(yǔ)言有指針啊,C++、Java和C#的多態(tài)在底層就是用指針實(shí)現(xiàn)的。C中的函數(shù)指針比Java中的接口更加靈活高效,當(dāng)然對(duì)程序員的要求也更高。”
引號(hào)驀地記起:“重載不也是一種多態(tài)嗎?”
“剛才所說(shuō)的多態(tài)都屬于通用多態(tài)(universal polymorphism)。此外,還有一類特別多態(tài)(ad-hoc polymorphism),常見(jiàn)有兩種形式。一種是強(qiáng)制多態(tài)(coercion polymorphism),即一種類型的變量在作為參數(shù)傳遞時(shí)隱式轉(zhuǎn)換成另一種類型,比如一個(gè)整型變量可以匹配浮點(diǎn)型變量的函數(shù)參數(shù)。另一種就是重載多態(tài)(overloading polymorphism),它允許不同的函數(shù)或方法擁有相同的名字。特別多態(tài)淺顯易懂,其重要性與通用多態(tài)也不可同日而語(yǔ),故不在我們關(guān)注之列。只是要注意一點(diǎn),正如子類型應(yīng)遵守超類型的規(guī)范,同名的函數(shù)或方法也應(yīng)遵守相同的規(guī)范。如果為貪圖取名方便而濫用重載,早晚因小失大。”冒號(hào)告誡道。
逗號(hào)突發(fā)奇論:“一個(gè)多態(tài)類型的對(duì)象可以在不同的類型之間變來(lái)變?nèi)ィ遣皇墙?#8216;變態(tài)類型’更生動(dòng)些?”
“我看你就屬于典型的變態(tài)類型。”句號(hào)乘機(jī)拿他開(kāi)涮。
全班哈哈大笑。
-
雖然C#具體的泛型類型是在運(yùn)行期間實(shí)例化的,但每類泛型對(duì)應(yīng)相同的實(shí)現(xiàn)代碼,故變量的行為仍是在編譯期間決定的。
-
salt、nonce和IV都是密碼學(xué)中的術(shù)語(yǔ),是在加密過(guò)程中混入的一次性數(shù)據(jù),以增加預(yù)計(jì)算攻擊(如字典攻擊)的難度。
-
這被稱為constructor injection,另外兩種常用的注射方法是setter injection和interface injection。
-
對(duì)用Java實(shí)現(xiàn)的Authenticator類(策略模式版)稍作修改,就能讓客戶動(dòng)態(tài)改變策略。
-
在靜態(tài)類型語(yǔ)言中,繼承是多態(tài)的基礎(chǔ),多態(tài)是繼承的目的。
-
多態(tài)結(jié)合了靜態(tài)類型的安全性和動(dòng)態(tài)類型的靈活性。
-
多態(tài)可分為通用多態(tài)和特別多態(tài)兩種。
-
通用多態(tài)主要包括參數(shù)多態(tài)和包含多態(tài)(或子類型多態(tài))。它們都是為了克服靜態(tài)類型過(guò)于嚴(yán)格的語(yǔ)法限制。
-
特別多態(tài)主要包括強(qiáng)制多態(tài)和重載多態(tài)。
-
參數(shù)多態(tài)是靜態(tài)綁定,重在算法的普適性,好讓相同的實(shí)現(xiàn)代碼應(yīng)用于不同的場(chǎng)合。
-
包含多態(tài)是動(dòng)態(tài)綁定,重在接口與實(shí)現(xiàn)的分離度,好讓不同的實(shí)現(xiàn)代碼應(yīng)用于相同的場(chǎng)合。
-
策略模式授予客戶自由選擇算法(策略)的權(quán)力。
-
模板方法模式重在穩(wěn)定堅(jiān)固的骨架,策略模式重在靈活多變的手腕。
-
合理地運(yùn)用基于類繼承的多態(tài)、基于接口繼承的多態(tài)和基于模板的多態(tài),能增強(qiáng)程序的簡(jiǎn)潔性、靈活性、可維護(hù)性、可重用性和可擴(kuò)展性。
友情提示:如果您對(duì)本文感興趣,歡迎到http://blog.zhenghui.org上發(fā)表評(píng)論。
posted on 2009-10-20 18:18 鄭暉 閱讀(3182) 評(píng)論(0) 編輯 收藏 所屬分類: 冒號(hào)課堂