在項(xiàng)目中經(jīng)常會(huì)遇到一個(gè)類(lèi)的某些方法和另一個(gè)類(lèi)的某些方法功能是相同的,只有部分方法是不同的。這個(gè)時(shí)候就可以使用模板方法來(lái)操作了。其實(shí)這種情況很常見(jiàn):比如我們項(xiàng)目里面用到的基類(lèi),BaseActivity之類(lèi)的。這種設(shè)計(jì)就是模板方法,是不是有點(diǎn)熟悉。。。下面來(lái)看看是怎么實(shí)現(xiàn)的把。
模版方法模式的結(jié)構(gòu)
模版方法模式由一個(gè)抽象類(lèi)和一個(gè)(或一組)實(shí)現(xiàn)類(lèi)通過(guò)繼承結(jié)構(gòu)組成,抽象類(lèi)中的方法分為三種:
抽象方法:父類(lèi)中只聲明但不加以實(shí)現(xiàn),而是定義好規(guī)范,然后由它的子類(lèi)去實(shí)現(xiàn)。
模版方法:由抽象類(lèi)聲明并加以實(shí)現(xiàn)。一般來(lái)說(shuō),模版方法調(diào)用抽象方法來(lái)完成主要的邏輯功能,并且,模版方法大多會(huì)定義為final類(lèi)型,指明主要的邏輯功能在子類(lèi)中不能被重寫(xiě)。
鉤子方法:由抽象類(lèi)聲明并加以實(shí)現(xiàn)。但是子類(lèi)可以去擴(kuò)展,子類(lèi)可以通過(guò)擴(kuò)展鉤子方法來(lái)影響模版方法的邏輯。
抽象類(lèi)的任務(wù)是搭建邏輯的框架,通常由經(jīng)驗(yàn)豐富的人員編寫(xiě),因?yàn)槌橄箢?lèi)的好壞直接決定了程序是否穩(wěn)定性。
實(shí)現(xiàn)類(lèi)用來(lái)實(shí)現(xiàn)細(xì)節(jié)。抽象類(lèi)中的模版方法正是通過(guò)實(shí)現(xiàn)類(lèi)擴(kuò)展的方法來(lái)完成業(yè)務(wù)邏輯。只要實(shí)現(xiàn)類(lèi)中的擴(kuò)展方法通過(guò)了單元測(cè)試,在模版方法正確的前提下,整體功能一般不會(huì)出現(xiàn)大的錯(cuò)誤。
模版方法的優(yōu)點(diǎn)
(一)容易擴(kuò)展。一般來(lái)說(shuō),抽象類(lèi)中的模版方法是不易反生改變的部分,而抽象方法是容易反生變化的部分,因此通過(guò)增加實(shí)現(xiàn)類(lèi)一般可以很容易實(shí)現(xiàn)功能的擴(kuò)展,符合開(kāi)閉原則。
(二)便于維護(hù)。對(duì)于模版方法模式來(lái)說(shuō),正是由于他們的主要邏輯相同,才使用了模版方法,假如不使用模版方法,任由這些相同的代碼散亂的分布在不同的類(lèi)中,維護(hù)起來(lái)是非常不方便的。
(三)比較靈活。因?yàn)橛秀^子方法,因此,子類(lèi)的實(shí)現(xiàn)也可以影響父類(lèi)中主邏輯的運(yùn)行。但是,在靈活的同時(shí),由于子類(lèi)影響到了父類(lèi),違反了里氏替換原則,也會(huì)給程序帶來(lái)風(fēng)險(xiǎn)。這就對(duì)抽象類(lèi)的設(shè)計(jì)有了更高的要求。
模版方法的適用場(chǎng)景
在多個(gè)子類(lèi)擁有相同的方法,并且這些方法邏輯相同時(shí),可以考慮使用模版方法模式。在程序的主框架相同,細(xì)節(jié)不同的場(chǎng)合下,也比較適合使用這種模式。
示例1:下面代碼實(shí)例說(shuō)明:
比如說(shuō)使用glide加載圖片的時(shí)候,我可以寫(xiě)一個(gè)抽象類(lèi)來(lái)管理一些共性的東西,然后之內(nèi)具體去實(shí)現(xiàn),下面我寫(xiě)一個(gè)抽象類(lèi):
第1步:先定義下載抽象類(lèi)AbstractImageLoader
/**
* 先定義下載抽象類(lèi)AbstractImageLoader
*/
public abstract class AbstractImageLoader {
//抽象類(lèi)定義整個(gè)流程骨架
public final void downloadImage(String imageUrl,int width,int height){
//先獲取最終的數(shù)據(jù)源URL
String finalImageUrl=getUrl(imageUrl,width,height);
//然后開(kāi)始執(zhí)行下載
}
//以下是不同子類(lèi)根據(jù)自身特性完成的具體步驟
protected abstract String getUrl(String imageUrl,int width,int height);
}
第2步: jpg下載類(lèi)
/**
* jpg下載類(lèi)
*/
public class jpgImageLoader extends AbstractImageLoader{
@Override
protected String getUrl(String imageUrl, int width, int height) {
return String.format("%s?imageView2/1/w/%d/h/%d/format/webp",
imageUrl, width, height);
}
}
第3步: webp下載類(lèi)
/**
* webp下載類(lèi)
*/
public class WebpImageLoader extends AbstractImageLoader{
@Override
protected String getUrl(String imageUrl, int width, int height) {
return String.format("%s?imageView2/1/w/%d/h/%d/format/jpg",
imageUrl, width, height);
}
}
第4步: 代碼里面使用
public class MainActivity5 extends AppCompatActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main5);
findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String imageUrl =
"http://img.my.csdn.net/uploads/201309/01/
1378037235_7476.jpg";
AbstractImageLoader loader =new WebpImageLoader();
loader.downloadImage(imageUrl,200,200);
}
});
}
}
示例2:再來(lái)一個(gè)實(shí)例說(shuō)明一下:
第1步: 寫(xiě)一個(gè)人類(lèi) 抽象類(lèi)
/**
* 人類(lèi) 抽象類(lèi)
*/
public abstract class AbstractPerson {
//抽象類(lèi)定義整個(gè)流程骨架
public void prepareGotoSchool(){
dressUp();
eatBreakfast();
takeThings();
}
//以下是不同子類(lèi)根據(jù)自身特性完成的具體步驟 ,交給子類(lèi)實(shí)現(xiàn)
protected abstract void dressUp();
protected abstract void eatBreakfast();
protected abstract void takeThings();
}
第2步: 學(xué)生類(lèi)
/**
* 學(xué)生類(lèi)
*/
public class StudentOne extends AbstractPerson {
@Override
protected void dressUp() {
Log.i("dressUp: ","穿校服");
}
@Override
protected void eatBreakfast() {
Log.i("eatBreakfast: ","吃媽媽做好的早飯");
}
@Override
protected void takeThings() {
Log.i("takeThings: ","背書(shū)包,帶上家庭作業(yè)和紅領(lǐng)巾");
}
}
第3步: 老師類(lèi)
/**
* 老師類(lèi)
*/
public class TeacherOne extends AbstractPerson {
@Override
protected void dressUp() {
Log.i("dressUp: ", "穿工作服");
}
@Override
protected void eatBreakfast() {
Log.i("eatBreakfast: ", "做早飯,照顧孩子吃早飯");
}
@Override
protected void takeThings() {
Log.i("takeThings: ", "帶上昨晚準(zhǔn)備的考卷");
}
}
第4步: 代碼里面使用
TeacherOne teacherOne = new TeacherOne();
teacherOne.dressUp();
通用模板
public abstract class AbstractClass{
//基本方法
protected abstract void doSomething();
//基本方法
protected abstract void doAnything();
//模板方法
public final void tempeteMethod(){
this.doSomething();
this.doAnything();
}
}
public class ConcreteClass1 extends AbstractClass{
protected void doAnything(){
//邏輯處理
}
protected void doSomething(){
//邏輯處理
}
}
public class ConcreteClass2 extends AbstractClass{
protected void doAnything(){
//邏輯處理
}
protected void doSomething(){
//邏輯處理
}
}