好久沒更新了,今天給大家更新一篇設(shè)計(jì)模式文章——適配器模式。如果你做過Android開發(fā),那么你肯定對(duì)適配這個(gè)詞很熟悉。適配的作用就是讓我們的應(yīng)用程序能適用于各大主流的Android手機(jī)平臺(tái)。
適配,通俗地講,就是將極具個(gè)性的人,加以合適的引導(dǎo),讓其發(fā)揮其最大的作用。這個(gè)極具個(gè)性的人,就是Adaptee(適配者),發(fā)揮作用就是Target(我們的目標(biāo)),引導(dǎo)的這個(gè)人就是Adapter(適配器)。
舉個(gè)簡(jiǎn)單的例子,想必這個(gè)段子大家之前都聽過。段子這么說,12306的老板說,我們開發(fā)個(gè)網(wǎng)上售票的系統(tǒng)。老板下達(dá)命令給經(jīng)理,經(jīng)理把這個(gè)項(xiàng)目外包給高校的老師來做,高校的老師說,同學(xué)們給你們個(gè)畢設(shè)題目,限什么時(shí)候完成。(當(dāng)然這僅僅是個(gè)段子,實(shí)際肯定不是這樣的,12306真的已經(jīng)很不錯(cuò)了,為了引起大家學(xué)習(xí)的興趣,就當(dāng)嘩眾取寵吧)。
我們簡(jiǎn)化下中間的層層關(guān)系,留下3個(gè)主要的角色,一個(gè)是老板,一個(gè)是經(jīng)理,一個(gè)是學(xué)生。老板提出需求Target,老板不懂技術(shù),所以實(shí)際干活的是學(xué)生Adaptee。
你可能會(huì)問了,為什么需要經(jīng)理呢?
為什么不是老板直接和學(xué)生交流呢?當(dāng)然不行,倒不是因?yàn)槊孀拥膯栴},而是老板還有更重要的事情做,做整體的調(diào)度以及重大策略的決斷,這個(gè)項(xiàng)目只是其中的一個(gè)決定而已,所以是不方便也沒時(shí)間與學(xué)生直接溝通的,所以經(jīng)理Adapter出現(xiàn)了,負(fù)責(zé)向上級(jí)匯報(bào),以及與學(xué)生溝通。
那么用關(guān)系圖如何描述呢?

由圖可以看出,在Manager里面,我們實(shí)際調(diào)用的是Student的codingBuild()方法。那么如何用偽代碼實(shí)現(xiàn)呢?
class Manager implements Boss extends Student{
@Override
public void buildWebSystem(){
codingBuild();
}
}
上面就是類適配器的簡(jiǎn)單寫法,那么這么寫有沒有什么問題呢?
我們默認(rèn)Target是Interface,萬一Target類是個(gè)抽象類呢?我們知道Java是不支持多繼承的,所以這么寫肯定是有問題的。
如果Adaptee是final類型,那么就不能被繼承。
我們知道盡量少用繼承,多用組合。
那么有沒有其他寫法呢?所以對(duì)象適配器出現(xiàn)了。
class Manager implements Boss{
private Student stu;//適配者
public void setStudent(Student stu){
this.stu = stu;
}
@Override
public void buildWebSystem(){
tu.codingBuild();
}
}
我們可以看到,在適配器(Adapter)類中持有一個(gè)適配者(Adaptee)對(duì)象,在適配器的方法中調(diào)用該適配者的方法。
那么這樣寫有什么好處呢?
我們不用關(guān)心具體系統(tǒng)是如何實(shí)現(xiàn)的,所以對(duì)Adapter是透明的,將任務(wù)委托給Adaptee來實(shí)現(xiàn),只要知道達(dá)到我們的任務(wù)要求就可以了,也不用改動(dòng)現(xiàn)有的組織結(jié)構(gòu)。
假如我們對(duì)該適配者所做的工作不滿意,我們可以隨時(shí)更換其他的適配者,而不用改動(dòng)太多代碼。
那么適配者模式的使用場(chǎng)景呢?
- 不想了解具體的業(yè)務(wù)實(shí)現(xiàn)或者無法了解到具體的業(yè)務(wù)實(shí)現(xiàn)時(shí)可以使用該模式。因?yàn)閷?duì)調(diào)用者是透明的,我只需要制定規(guī)范,將具體實(shí)現(xiàn)“外包”給Adaptee來做;
- 如果需求更換較為頻繁,需要將具體的實(shí)現(xiàn)靈活性提高的時(shí)候可以使用該模式。說得專業(yè)點(diǎn)就是更換算法更為方便;
- 如果想使得具體實(shí)現(xiàn)(Adaptee)的復(fù)用性更好,可以使用該模式。
適配者模式基本上說完了,是不是很簡(jiǎn)單?
稍微拓展一下,說一下雙向適配器。雙向適配器(Adapter)同時(shí)持有Target和Adaptee。簡(jiǎn)單的示例代碼說下。
class Manager implements Boss extends Student{
private Boss boss; // Target
private Student stu; //Adaptee
public setBoss(Boss boss){
this.boss = boss;
}
public setStudent(Student stu){
this.stu = stu;
}
@Override
public void buildWebSystem(){
tu.codingBuild();
}
public void codingBuild(){
boss.buildWebSystem();
}
}
這種實(shí)現(xiàn)我見得不多,作為了解。
還有一種缺省適配器有必要說一下。假設(shè)我們現(xiàn)在有接口類A里面有很多方法。
interface A{
public void methodA();
public void methodB();
public void methodC();
...
}
我們現(xiàn)在有個(gè)類B要用到A接口里面的方法methodA(),怎么辦呢?很簡(jiǎn)單啊,直接讓B實(shí)現(xiàn)接口A不行嗎?當(dāng)然行。但是我們就必須為了這個(gè)methodB()而實(shí)現(xiàn)methodA()、methodC()...如果A里面的方法數(shù)目很多,那么將在我們的B類中出現(xiàn)大量的無用代碼,因?yàn)槌薽ethodA()都是空實(shí)現(xiàn)。有沒有比較好的實(shí)現(xiàn)方式呢?
//適配者接口
public interface A {
public void methodA();
public void methodB();
public void methodC();
public void methodD();
}
//缺省適配器類
public abstract class B implements A {
@Override
public void methodA() {
}
@Override
public void methodB() {
}
@Override
public void methodC() {
}
@Override
public void methodD() {
}
}
//具體業(yè)務(wù)類
public class C extends B {
@Override
public void methodA() {
//TODO
}
}
由代碼可以看出,我們?cè)谥虚g引入了一個(gè)抽象類B,用于對(duì)接口A中的所有方法空實(shí)現(xiàn)。最后在C中具體實(shí)現(xiàn)我們想要的目標(biāo)方法。
這么做的好處呢?
雖然也出現(xiàn)了沒用的代碼,但是我們這么做只需要讓這些代碼出現(xiàn)一次,而不是沒一個(gè)新的類中都要空實(shí)現(xiàn)一遍。
那么為什么B要是abstract的呢?
我是這么理解的,因?yàn)锽中所有的方法都是空實(shí)現(xiàn),所以這個(gè)類沒實(shí)際意義,真正起作用的是具體的實(shí)現(xiàn)類C,這里面具體實(shí)現(xiàn)我們想要的功能。為了避免大家實(shí)例化這個(gè)沒實(shí)際用處的類,所以聲明為abstract類型。每個(gè)類都有自己的職責(zé)以及名字,A(適配者接口),B(缺省適配器類),C(具體業(yè)務(wù)類)。
好了,適配器模式基本上說完了,希望可以給新手一點(diǎn)點(diǎn)幫助。