學(xué)習(xí)-輸出-再學(xué)習(xí)箱歧,形成一個(gè)閉環(huán)矾飞,才是最有效果的學(xué)習(xí)方式。如果只有學(xué)習(xí)呀邢,沒(méi)有輸出洒沦,那學(xué)到的只是就像一團(tuán)漿糊塞到你的腦子里,無(wú)法融會(huì)貫通价淌,形成自己的有效知識(shí)體系申眼。因此希望在這里對(duì)自己學(xué)習(xí)的知識(shí)通過(guò)博文的方式進(jìn)行輸出,也希望自己不要半途而廢蝉衣。輸出時(shí)豺型,盡量不看書籍的內(nèi)容,或者只看書籍中的章節(jié)名买乃;憑印象和思考姻氨,復(fù)盤學(xué)習(xí)到的內(nèi)容。復(fù)盤完剪验,再回去對(duì)照書中內(nèi)容肴焊,看看有哪些缺漏。
==================================================================
第一個(gè)例子功戚,介紹了如何使用策略模式娶眷。
本例中,涉及到兩個(gè)重要的設(shè)計(jì)原則:
1. 封裝原則:在軟件設(shè)計(jì)時(shí)要區(qū)分容易發(fā)生變化的部分以及不會(huì)經(jīng)常發(fā)生變化的部分啸臀;對(duì)于容易發(fā)生變化的部分届宠,應(yīng)將其封裝起來(lái),使對(duì)其的改動(dòng)不會(huì)影響到其他部分乘粒。
2. 針對(duì)接口進(jìn)行設(shè)計(jì)而不是針對(duì)實(shí)現(xiàn)進(jìn)行設(shè)計(jì)豌注。
例子:
有一個(gè)游戲開發(fā)公司,設(shè)計(jì)了一款模擬鴨子的游戲灯萍。首先設(shè)計(jì)了一個(gè)類,類中實(shí)現(xiàn)了一些鴨子的基本行為轧铁,比如嘎嘎叫,游泳等等:
class Duck {
public void quack()
{...}
public void swim()
{...}
}
該公司設(shè)計(jì)的所有鴨子類旦棉,都繼承自Duck類齿风,那么這些子類都會(huì)繼承quack和swim行為的代碼。
突然有一天绑洛,市場(chǎng)有新需求救斑,需要對(duì)鴨子增加fly的行為,面對(duì)這個(gè)需求真屯,最直觀最簡(jiǎn)單的做法脸候,就是在Duck類中,新增實(shí)現(xiàn)fly()行為,如下:
class Duck {
public void quack()
{...}
public void swim()
{...}
public void fly()
{...}
}
但是這么做以后纪他,不好的后果出現(xiàn)了鄙煤,所有鴨子的子類都直接繼承了fly行為晾匠。那些本來(lái)不可能飛的鴨子茶袒,也被迫實(shí)現(xiàn)了飛行的功能,比如玩具鴨凉馆,出現(xiàn)了如此大的bug薪寓,程序員緊急修復(fù),因此在玩具鴨類中澜共,覆蓋Duck中的fly接口:
public class RubberDuck extends Duck {
public void fly()
????{
????//do nothing;
????}
...
}
這樣做確實(shí)是臨時(shí)解決了玩具鴨會(huì)飛的尷尬情況向叉。但卻不是長(zhǎng)久之計(jì),假如以后需要新增一種鴨子種類,這種鴨子不僅不會(huì)飛嗦董,也不是嘎嘎叫母谎,而是吱吱叫,那么這個(gè)鴨子類就得同時(shí)覆蓋quack和fly行為京革;如果又要新增一種鴨子奇唤,會(huì)飛,但不會(huì)游泳匹摇,咬扇。利花。侄泽。 這樣每新增一種鴨子,都要檢查是否要覆蓋某些方法拣展, 簡(jiǎn)直是無(wú)窮無(wú)盡的噩夢(mèng)坡垫。
于是優(yōu)化方案1.0:
將Duck類中的quack, fly 等會(huì)變化的行為從類刪除梭灿,提取出來(lái),做成接口的形式:
public interface quackable
{
? ?public? void quack();
}
public interface flyable
{
? ? void fly();
}
當(dāng)要定義一個(gè)鴨子類時(shí)冰悠,先繼承Duck類胎源,然后選擇性地實(shí)現(xiàn)行為接口,比如class RubberDuck就可以改為:
public class RubberDuck extends Duck implements quack
{
? ? public void quack{...}
? ? //不實(shí)現(xiàn)flyable接口屿脐,因此就不會(huì)飛
}
這個(gè)方案的優(yōu)點(diǎn)是涕蚤,再也不用一一檢查哪些類需要重寫,哪些不用的诵,并且每種鴨子可以定義自己的行為万栅。
但是新的問(wèn)題又出現(xiàn)了,有一天西疤,領(lǐng)導(dǎo)要求對(duì)鴨子的fly行為增加一段代碼烦粒。因?yàn)楝F(xiàn)在所有的鴨子類的fly行為都是在各自類中實(shí)現(xiàn)的,所以我們要把所有鴨子的fly代碼都改寫一遍。扰她。兽掰。如果游戲中有幾百種鴨子類型,簡(jiǎn)直無(wú)法想象徒役。這里就牽涉到第一條設(shè)計(jì)原則:封裝的原則孽尽。如果那些擁有相同代碼的函數(shù),從一開始就被設(shè)計(jì)成可復(fù)用的忧勿,就不會(huì)有現(xiàn)在的困惱了杉女。
因此優(yōu)化方案2.0:
仍然設(shè)計(jì)行為接口,flyBehavior, quackBehavior等等,但現(xiàn)在不再由鴨子類來(lái)實(shí)現(xiàn)具體的行為方法鸳吸,而是定義一些行為類熏挎,這些行為類實(shí)現(xiàn)具體不同的fly和quack方法:
public interface quackBehavior
{
?public?void quack();
}
public interface FlyBehavior
{
? ? void fly();
}
class NormalFly implements flyBehavior
{
? ? public void fly()
????{
? ? ? ? //最通用的飛行方式
????}
}
class FlyNoWay implements flyBehavior
{
public void fly()
????{
????????//do nothing; 針對(duì)無(wú)法飛行的鴨子類
????}
}
在Duck類中,持有這些行為接口的引用晌砾,并利用多態(tài)(所謂多態(tài)坎拐,就是對(duì)同一個(gè)消息,可以有不同的響應(yīng)养匈。編寫代碼時(shí)哼勇,不需要關(guān)心是調(diào)用了哪個(gè)子類的方法,只需關(guān)心調(diào)用了哪個(gè)接口乖寒。這里就牽涉到第二個(gè)設(shè)計(jì)原則猴蹂,針對(duì)接口編程而不是針對(duì)實(shí)現(xiàn)編程):
public class Duck
{
? ? FlyBehavior flyBehavior;
? ? public void performFly
????{
? ? ? ? //利用多態(tài),對(duì)不同的flyBehavior的引用楣嘁,會(huì)調(diào)用到不同的fly實(shí)現(xiàn)方法
? ? ? ? flyBehavior.fly();
????}
}
這樣定義一個(gè)鴨子類:
public class RubberDuck extends Duck
{
//構(gòu)造函數(shù):
? ? public RubberDuck()
? ? {
? ??????flyBehavior = new FlyNoway(); //別忘了磅轻,flyBehavior是從父類Duck中繼承的
????}
}
這樣,RubberDuck就不會(huì)飛行逐虚, 而其他鴨子類只要給flyBehavior new一個(gè)NormalFly的行為對(duì)下聋溜,就很方便的設(shè)定了fly行為。而且所有的NormalFly行為代碼都是可復(fù)用的叭爱。