1、什么是适配器模式?
有时,当我们试图将遗留代码与新代码集成时,或者在代码中更改第三方API时,会出现两个对象不匹配的情况。这是由于两个对象的接口不兼容造成的。适配器模式允许您调整对象或类向另一个对象或类公开的内容。它将类的接口转换为客户机期望的另一个接口。它让那些由于接口不兼容而无法协同工作的类一起工作。它允许在不直接修改对象和类的情况下修复对象和类之间的接口。您可以将适配器看作是一个实际的适配器,它用于连接两个不能直接连接的不同设备。
2、场景分析
举个例子:软件开发人员Max在一家电子商务网站工作。该网站允许用户在线购物和支付,该网站与第三方支付集成,用户可以通过第三方支付使用信用卡支付账单。一切都进行得很顺利,直到他的经理打电话给他要求改变项目。经理告诉他,他们计划更改支付供应商,他必须在代码中实现这一点。这里出现的问题是,站点本来使用的是Xpay支付。新供应商PayD只允许PayD类型的对象允许该流程。Max不想改变整个100个类的集合,这些类都引用了一个类型为XPay的对象。这也增加了项目的风险,该项目已经在生产中运行。由于代码的两个不同部分之间存在不兼容的接口,所以出现了这个问题。为了使流程正常工作,Max需要找到一种方法使代码与供应商提供的API兼容。
3、代码实现
package com.zte.test.adapter;
public interface PayD {
public String getCustCardNo();
public String getCardOwnerName();
public String getCardExpMonthDate();
public Integer getCVVNo();
public Double getTotalAmount();
public void setCustCardNo(String custCardNo);
public void setCardOwnerName(String cardOwnerName);
public void setCardExpMonthDate(String cardExpMonthDate);
public void setCVVNo(Integer cVVNo);
public void setTotalAmount(Double totalAmount);
}
它包含一组setter和getter方法,用于获取关于信用卡和客户名的信息。这个Xpay接口在代码中实现,代码用于实例化这种类型的对象,并将该对象公开给供应商的API。下面的类定义了Xpay接口的实现。
package com.zte.test.adapter;
public class XpayImpl implements Xpay {
private String creditCardNo;
private String customerName;
private String cardExpMonth;
private String cardExpYear;
private Short cardCVVNo;
private Double amount;
@Override
public String getCreditCardNo() {
return creditCardNo;
}
@Override
public String getCustomerName() {
return customerName;
}
@Override
public String getCardExpMonth() {
return cardExpMonth;
}
@Override
public String getCardExpYear() {
return cardExpYear;
}
@Override
public Short getCardCVVNo() {
return cardCVVNo;
}
@Override
public Double getAmount() {
return amount;
}
@Override
public void setCreditCardNo(String creditCardNo) {
this.creditCardNo = creditCardNo;
}
@Override
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
@Override
public void setCardExpMonth(String cardExpMonth) {
this.cardExpMonth = cardExpMonth;
}
@Override
public void setCardExpYear(String cardExpYear) {
this.cardExpYear = cardExpYear;
}
@Override
public void setCardCVVNo(Short cardCVVNo) {
this.cardCVVNo = cardCVVNo;
}
@Override
public void setAmount(Double amount) {
this.amount = amount;
}
}
package com.zte.test.adapter;
public interface PayD {
public String getCustCardNo();
public String getCardOwnerName();
public String getCardExpMonthDate();
public Integer getCVVNo();
public Double getTotalAmount();
public void setCustCardNo(String custCardNo);
public void setCardOwnerName(String cardOwnerName);
public void setCardExpMonthDate(String cardExpMonthDate);
public void setCVVNo(Integer cVVNo);
public void setTotalAmount(Double totalAmount);
}
可以看到,这个接口有一组不同的方法,需要在代码中实现。但是Xpay是由大部分代码创建的,更改整个类集是非常困难和有风险的。我们需要一些方法,能够满足供应商的要求,以便处理付款,并作出较少或没有改变,在目前的代码。方法由适配器模式提供。我们将创建一个类型为PayD的适配器,它封装了一个Xpay对象。
package com.zte.test.adapter;
public class XpayToPayDAdapter implements PayD {
private String custCardNo;
private String cardOwnerName;
private String cardExpMonthDate;
private Integer cVVNo;
private Double totalAmount;
private final Xpay xpay;
public XpayToPayDAdapter(Xpay xpay) {
this.xpay = xpay;
setProp();
}
@Override
public String getCustCardNo() {
return custCardNo;
}
@Override
public String getCardOwnerName() {
return cardOwnerName;
}
@Override
public String getCardExpMonthDate() {
return cardExpMonthDate;
}
@Override
public Integer getCVVNo() {
return cVVNo;
}
@Override
public Double getTotalAmount() {
return totalAmount;
}
@Override
public void setCustCardNo(String custCardNo) {
this.custCardNo = custCardNo;
}
@Override
public void setCardOwnerName(String cardOwnerName) {
this.cardOwnerName = cardOwnerName;
}
@Override
public void setCardExpMonthDate(String cardExpMonthDate) {
this.cardExpMonthDate = cardExpMonthDate;
}
@Override
public void setCVVNo(Integer cVVNo) {
this.cVVNo = cVVNo;
}
@Override
public void setTotalAmount(Double totalAmount) {
this.totalAmount = totalAmount;
}
private void setProp() {
setCardOwnerName(this.xpay.getCustomerName());
setCustCardNo(this.xpay.getCreditCardNo());
setCardExpMonthDate(this.xpay.getCardExpMonth() + "/" + this.xpay.
getCardExpYear());
setCVVNo(this.xpay.getCardCVVNo().intValue());
setTotalAmount(this.xpay.getAmount());
}
}
package com.zte.test.adapter;
public class RunAdapterExample {
public static void main(String[] args) {
// Object for Xpay
Xpay xpay = new XpayImpl();
xpay.setCreditCardNo("4789565874102365");
xpay.setCustomerName("Max Warner");
xpay.setCardExpMonth("09");
xpay.setCardExpYear("25");
xpay.setCardCVVNo((short) 235);
xpay.setAmount(2565.23);
PayD payD = new XpayToPayDAdapter(xpay);
testPayD(payD);
}
private static void testPayD(PayD payD) {
System.out.println(payD.getCardOwnerName());
System.out.println(payD.getCustCardNo());
System.out.println(payD.getCardExpMonthDate());
System.out.println(payD.getCVVNo());
System.out.println(payD.getTotalAmount());
}
}
在上面的代码中,我们创建了一个适配器(XpayToPayDAdapter)。适配器实现了PayD接口,因为需要模仿PayD类型的对象。对象通过其构造函数传递到适配器。现在请注意,我们有两种不兼容的接口类型,为了使代码工作,我们需要使用适配器将它们组合在一起。这两个接口有一组不同的方法。但是这些接口的唯一目的非常相似,即向特定的供应商提供客户和信用卡信息。
4、何时使用适配器模式?
适配器模式应该在以下情况下使用:
1、存在现有类,且其接口与您需要的接口不匹配。
2、您想要创建一个可重用的类,该类与不相关的或不可预见的类协作,即不一定具有兼容接口的类。
3、有几个现有的子类要使用,但是通过子类化每个子类来调整它们的接口是不切实际的。对象适配器可以调整其父类的接口。