適配器模式
什么是適配器模式
- 適配器模式屬于結(jié)構(gòu)型模式,可以使得兩個(gè)不匹配的接口可以協(xié)同工作。
- 適配器模式允許兩個(gè)不匹配的類通過將其中一個(gè)接口類型轉(zhuǎn)換成另一個(gè)客戶端期望的接口類型,從而達(dá)到二者協(xié)同工作。
- 適配器模式也叫包裝器。
適配器模式在 Gang of Four 書中原始的定義如下:
將一個(gè)類的接口類型轉(zhuǎn)換成另一個(gè)客戶端期望的接口類型。
適配器可以讓多個(gè)類協(xié)同工作即使他們本來是不匹配的接口類型。
適配器模式的應(yīng)用場景
考慮一個(gè)這樣的場景,你在印度購買了一個(gè)輕便的筆記本,最近你剛搬到英國。但是英國的電子插座和印度的不一樣。因此,你的筆記本不能直接工作了。
你必須去購買一個(gè)適配器,可以為你的印度筆記本可以在英國的插座上充電。當(dāng)你有一個(gè)需要與新系統(tǒng)集成的遺留接口時(shí),新系統(tǒng)不能直接接收遺留庫的工作方式。由于遺留庫不再進(jìn)行開發(fā)了,所以我們需要使用適配器促使兩種不同的類型進(jìn)行工作。
你使用 Mac 時(shí),經(jīng)常需要轉(zhuǎn)接頭才能連接到會(huì)議室的投影儀,這個(gè)轉(zhuǎn)接頭,就是適配器,使得原本 Mac 、投影儀互不相容的兩個(gè)物件可以協(xié)同工作。
適配器模式的特點(diǎn)
- 客戶端通過使用目標(biāo)接口調(diào)用適配器的方法向適配器發(fā)起請求。
- 適配器通過適配器接口將請求轉(zhuǎn)換成適配者的一個(gè)或多個(gè)調(diào)用。
- 客戶端收到調(diào)用結(jié)果,并且不感知存在一個(gè)適配器在做這個(gè)轉(zhuǎn)換工作。
何時(shí)使用適配器模式
- 如果你想要將已經(jīng)存在的類和他們的接口類型去匹配你最后需要的接口類型,就可以使用適配器模式。
- 如果你想創(chuàng)建可重用類以幫助在不匹配的兩個(gè)類之間進(jìn)行接口式交互。
適配器模式示例
勞埃德銀行是一家提供全球性服務(wù)的國際性銀行。境外賬戶持有人的稅率為 0.03%。
在印度,它提供2種類型的賬戶,普通和白金。稅法不適用于印度賬戶。
現(xiàn)在離岸賬戶就匹配不了印度賬戶了。
所以需要設(shè)計(jì)出一個(gè)賬戶適配器 AccountAdapter 促使2種不同的賬戶類型還可以繼續(xù)一塊工作。
這個(gè)示例的交互圖如下所示。
在這里,客戶端僅僅需要調(diào)用適配器的 getBalance() 方法。
適配器調(diào)用適配者的 getOffshoreBalance() 方法并返回客戶端期望的結(jié)果。
適配器內(nèi)部的 getBalance() 方法將會(huì)通過扣除稅金來計(jì)算賬戶余額。

Adapter-Design-Pattern-Sequence-Diagram-620x492.png)
這個(gè)對象適配器使用組合方式去將一個(gè)不匹配的接口適配到另一個(gè)接口。
適配器繼承了客戶端期望的目標(biāo)接口,同時(shí)它持有適配者的一個(gè)實(shí)例。
這樣使得客戶端和適配者完全解耦。只有適配器知道它們兩個(gè)(客戶端、適配者)。

OffshoreAccount.java
package org.byron4j.cookbook.designpattern.adapter;
/**
* 離岸賬戶
*/
public class OffshoreAccount {
private double balance;
/**稅率*/
private static final double TAX_RATE = 0.04;
public OffshoreAccount(final double balance) {
this.balance = balance;
}
public double getTaxRate() {
return TAX_RATE;
}
public double getOffshoreBalance() {
return balance;
}
public void debit(final double debit) {
if (balance >= debit) {
balance -= debit;
}
}
public void credit(final double credit) {
balance += balance;
}
}
Account.java
package org.byron4j.cookbook.designpattern.adapter;
/**
* 賬戶接口類型
*/
public interface Account {
/**
* 獲取賬戶余額
* @return
*/
public double getBalance();
/**
* 是否可以透支
* @return
*/
public boolean isOverdraftAvailable();
/**
* 貸款; 貸款后賬戶余額增多
* @param credit
*/
public void credit(final double credit);
}
AbstractAccount.java
package org.byron4j.cookbook.designpattern.adapter;
public class AbstractAccount implements Account {
/**
* 賬戶余額
*/
private double balance;
/**
* 是否可以透支
*/
private boolean isOverdraftAvailable;
public AbstractAccount(final double size) {
this.balance = size;
}
@Override
public double getBalance() {
return balance;
}
@Override
public boolean isOverdraftAvailable() {
return isOverdraftAvailable;
}
public void setOverdraftAvailable(boolean isOverdraftAvailable) {
this.isOverdraftAvailable = isOverdraftAvailable;
}
@Override
public String toString() {
return getClass().getSimpleName() + " Balance=" + getBalance()
+ " Overdraft:" + isOverdraftAvailable();
}
@Override
public void credit(final double credit) {
balance += credit;
}
}
PlatinumAccount.java
package org.byron4j.cookbook.designpattern.adapter;
/**
* 白金帳戶: 可以透支
*/
public class PlatinumAccount extends AbstractAccount {
public PlatinumAccount(final double balance) {
super(balance);
// 可以透支
setOverdraftAvailable(true);
}
}
StandardAccount.java
package org.byron4j.cookbook.designpattern.adapter;
/**
* 普通賬戶: 不能透支
*/
public class StandardAccount extends AbstractAccount {
public StandardAccount(final double balance) {
super(balance);
// 不能透支
setOverdraftAvailable(false);
}
}
AccountAdapter.java
package org.byron4j.cookbook.designpattern.adapter;
/**
* 賬戶適配器; 適配器繼承于目標(biāo)賬戶。
* 適配器(轉(zhuǎn)接頭)的目標(biāo)是將離岸賬戶(會(huì)議室中的Mac電腦)轉(zhuǎn)為 AbstractAccount(可以連接投影儀)
*/
public class AccountAdapter extends AbstractAccount {
/**需要被適應(yīng)的賬戶--適配者*/
private OffshoreAccount offshoreAccount;
/**
*
* @param offshoreAccount 適配者--會(huì)議室中的 mac 電腦
*/
public AccountAdapter(final OffshoreAccount offshoreAccount) {
super(offshoreAccount.getOffshoreBalance());
// 適配器持有適配者的引用
this.offshoreAccount = offshoreAccount;
}
/**
* 計(jì)算扣除稅款后的離岸賬戶余額
* @return
*/
@Override
public double getBalance() {
// 離岸稅率
final double taxRate = offshoreAccount.getTaxRate();
// 離岸賬戶余額
final double grossBalance = offshoreAccount.getOffshoreBalance();
// 需要扣除的稅款
final double taxableBalance = grossBalance * taxRate;
// 扣除離岸稅款后的賬戶余額
final double balanceAfterTax = grossBalance - taxableBalance;
return balanceAfterTax;
}
}
AdapterTest.java
package org.byron4j.cookbook.designpattern;
import org.byron4j.cookbook.designpattern.adapter.AccountAdapter;
import org.byron4j.cookbook.designpattern.adapter.OffshoreAccount;
import org.byron4j.cookbook.designpattern.adapter.StandardAccount;
import org.junit.Test;
public class AdapterTest {
@Test
public void test(){
StandardAccount sa = new StandardAccount(2000);
System.out.println("Account Balance= " + sa.getBalance());
//Calling getBalance() on Adapter
AccountAdapter adapter = new AccountAdapter(new OffshoreAccount(2000));
System.out.println("Account Balance= " + adapter.getBalance());
}
}