本文由 Prefert 發表在 ScalaCool 團隊博客。html
無論你是否是果粉,確定對 iphone X 都有所耳聞。最近的「掉漆門」和「人臉識別被破解」更是將其推到了風口浪尖上。git
可是對於我而言,最難以忍受的仍是耳機接口被取消這一改變(自 Iphone 7 開始),你能夠想象這樣一幅畫面:當你開開心心地和小夥伴開着語音吃(song)着(kuai)雞(di)或者是多人一塊兒上(song)分時——你的電量見底,爲了避免影響隊友(shou)的遊戲體驗,確定得充電玩下去。github
這時你得面對一個難題:只有一個孔,插耳機仍是插電源!?(在沒有藍牙耳機的前提下)數據庫
(侵刪)編程
因爲生活常常會欺騙咱們,以及各類環境因素,因此不是每一個人都選擇藍牙耳機(貧窮使我理智)。設計模式
是否存在別的解決方法呢?還好有轉接線這樣的好東西app
(侵刪)框架
在編程中,咱們也會趕上相似的問題:iphone
本文會經過 Adapter Pattern 來探究如何解決這類問題。ide
本篇文章結構以下:
適配器模式(Adapter Pattern)有時候也稱包裝樣式或者包裝(Wrapper)。定義以下:
將一個類的接口轉接成用戶所期待的。一個適配使得因接口不兼容而不能在一塊兒工做的類能在一塊兒工做,作法是將類本身的接口包裹在一個已存在的類中。
適配器模式將現有接口轉化爲客戶類所指望的接口,實現了對現有類的複用,它是一種使用頻率很是高的設計模式,在軟件開發中得以普遍應用,在 Spring
等框架、驅動程序設計(如 JDBC
中的數據庫驅動程序)中也使用了適配器模式。
小 A 是個蘋果控 + 耳機控,以前買了一款很貴的耳機,對其愛不釋手。咱們都知道通常耳機接口都是 3.5mm 的。
public interface PhoneJackInterface {
// 傳統的播放音頻
void audioTraditionally();
}
public class PhoneJackConnector implements PhoneJackInterface {
@Override
public void audioTraditionally() {
System.out.println("經過 PhoneJack 播放聲音");
}
}
複製代碼
iphone 7 以前的 iphone 支持 3.5mm 接口:
public class Iphone {
private PhoneJackInterface phoneJack;
public Iphone(PhoneJackInterface phoneJack) {
this.phoneJack = phoneJack;
}
// Iphone 具有播放聲音的功能
public void play() {
// 經過 3.5mm 接口耳機播放
phoneJack.audioTraditionally();
}
}
複製代碼
這樣的狀況下,小 A 還能夠愉快地聽歌:
// test
PhoneJackInterface phoneJack = new PhoneJackConnector();
Iphone iphone6 = new Iphone(phoneJack);
iphone6.play();
// 控制檯輸出 「經過 PhoneJack 播放聲音」
複製代碼
在 iphone 7 問世後,問題出現了:小 A 發現其不支持 3.5mm 接口 —— 將有線耳機的插口改成了 lightning。
public interface LightningInterface {
void audioWithLightning();
}
public class LightningConnector implements LightningInterface {
@Override
public void audioWithLightning() {
System.out.println("經過 Lightning 播放聲音");
}
}
複製代碼
一邊是耳機,一邊是手機,這太難以抉擇了。但蘋果怎麼可能沒考慮到這點了,能夠經過贈送的耳機轉接器 —— 將傳統的耳機頭轉爲 lightning:
public class HeadsetAdapter implements PhoneJackInterface { // 基於傳統耳機接口
LightningInterface lightning; // 兼容新接口
/** * 傳入 lightning 接口 * @param lightning */
public HeadsetAdapter(LightningInterface lightning) {
this.lightning = lightning;
}
/** * 對傳統接口兼容 */
@Override
public void audioTraditionally() {
lightning.audioWithLightning();
}
}
複製代碼
這樣不夠簡潔,咱們能夠改一改:
public class HeadsetAdapter extends LightningConnector implements PhoneJackInterface {
@Override
public void audioTraditionally() {
// 傳統接口兼容 lightning
super.audioWithLightning();
}
}
複製代碼
測試:
// test
HeadsetAdapter headsetAdapter = new HeadsetAdapter();
Iphone iphone7 = new Iphone(headsetAdapter);
iphone7.play();
// 控制檯輸出 「經過 Lightning 播放聲音」
複製代碼
咱們通常將上面的適配器稱做「類適配器」,除此以外還有一種 「對象適配器」,咱們能夠對適配器類進行以下修改:
public class ObjectHeadsetAdapter implements PhoneJackInterface { // 基於傳統耳機接口
LightningConnector lightning; // 兼容新接口
/** * 傳入 lightning 接口 * @param lightning */
public ObjectHeadsetAdapter(LightningConnector lightning) {
this.lightning = lightning;
}
/** * 對傳統接口兼容 */
@Override
public void audioTraditionally() {
// 使用委託實現兼容
this.lightning.audioWithLightning();
}
}
複製代碼
測試:
ObjectHeadsetAdapter objectHeadsetAdapter = new ObjectHeadsetAdapter(new LightningConnector());
Iphone iphoneX = new Iphone(objectHeadsetAdapter);
iphoneX.play();
複製代碼
經過以上簡單的例子,相信你對適配器模式有一個大體瞭解了。「類適配器」與「對象適配器」的區別歸納以下:
- | 類適配器 | 對象適配器 |
---|---|---|
建立方式 | 須要經過建立自身建立出一個新的 Adapter | 能夠經過已有的 Adapter 對象來轉換接口 |
擴展性 | 經過 Override 來擴展新需求 | 由於包含關係因此不能擴展 |
其餘 | 繼承被適配類,因此相對靜態 | 包含被適配類,因此相對靈活 |
總的來講,適配器模式主要有如下幾個優勢:
看完 Java 的實現方式,咱們再來看看 Scala 是如何實現的。
在 Scala 中,因爲方便的語法糖,咱們並不須要像 Java 那樣麻煩,已知傳統接口類(此處省略一些接口)
class PhoneJackConnector {
def audioTraditionally = println("經過 PhoneJack 播放聲音")
}
複製代碼
若是咱們有須要適配的,爲其建立一個 trait
便可:
trait Lightning {
def audioWithLightning()
}
複製代碼
其次再新建一個類,繼承傳統類:
class HeadsetAdapter extends PhoneJackConnector with Lightning {
override def audioTraditionally: Unit = super.audioTraditionally
override def audioWithLightning: Unit = println("經過 Lightning 播放聲音")
}
複製代碼
你會開心的發現:在這個新的類裏,咱們能夠對新老方法一塊兒擴展——在 Java 中,這是「對象適配器」和 「類適配器」比較大的一個劣勢。
測試:
val headsetAdapter = new HeadsetAdapter
headsetAdapter.audioTraditionally
複製代碼
固然,除了這種方式,Scala 裏還能夠經過隱式轉換來實現適配 final
類的適配器:
final class FinalPhoneJackConector {
def audioTraditionally = println("經過 PhoneJack 播放聲音")
}
object FinalPhoneJackConector {
implicit class ImplictHeadsetAdapter(phoneJackConnector: FinalPhoneJackConector) extends Lightning {
override def audioWithLightning: Unit = println("經過 Lightning 播放聲音")
}
}
複製代碼
測試:
val headsetAdapter = new HeadsetAdapter
headsetAdapter.audioTraditionally
// 隱式
val light: Lightning = new FinalPhoneJackConector
light.audioWithLightning()
複製代碼
Hint: 對於不熟悉
implicit
的朋友能夠 看一下這裏
光從代碼量來講,Scala 簡潔比 Java 表現的好太多。
其次,Scala 結合了「類適配器」和「對象適配器」全部的優勢,並消除了自身問題。與 Java 相比,Scala 有以下特色:
源碼連接 若有錯誤和講述不恰當的地方還請指出,不勝感激!