适配器设计模式示例
本文是我们名为“ Java设计模式 ”的学院课程的一部分。
在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因,并了解何时以及如何应用模式中的每一个。 在这里查看 !
1.适配器模式
Max的软件开发人员已在一个电子商务网站上工作。 该网站允许用户在线购物和付款。 该站点与第三方支付网关集成在一起,用户可以通过该网关使用信用卡支付账单。 一切进展顺利,直到他的经理打电话给他要求更改项目。
经理告诉他,他们正计划更改支付网关供应商,他必须在代码中实现这一点。
这里出现的问题是该站点连接到采用Xpay类型对象的Xpay支付网关。 新的供应商PayD仅允许对象的PayD类型允许该过程。 Max不想更改引用了XPay类型对象的100个类的整个集合。 这也增加了已经在生产中运行的项目的风险。 他都不能更改支付网关的第三方工具。 由于代码的两个不同部分之间的接口不兼容,导致出现了问题。 为了使过程正常进行,Max需要找到一种使代码与供应商提供的API兼容的方法。
Xpay API的当前代码
现在,当前代码接口与新供应商的接口不兼容。
2.抢救适配器
Max在这里需要的是一个适配器,它可以位于代码和供应商的API之间,并可以使流程顺畅进行。 但是在解决方案之前,让我们首先了解一下适配器是什么以及它如何工作。
有时,可能会出现两个对象无法放在一起的情况,因为它们应该按顺序完成工作。 当我们尝试将旧代码与新代码集成在一起时,或在代码中更改第三方API时,可能会出现这种情况。 这是由于两个对象的接口不兼容而导致的。
适配器模式使您可以使对象或类公开的内容适应另一个对象或类期望的内容。 它将类的接口转换为客户端期望的另一个接口。 它允许类因接口不兼容而无法一起工作。 它允许修复对象和类之间的接口,而无需直接修改对象和类。
您可以将适配器视为真实世界的适配器,该适配器用于连接无法直接连接的两个不同的设备。 适配器位于这些设备之间,它从设备中获取流量并将其以所需的形式提供给其他设备,否则由于接口不兼容而无法获得。
适配器使用合成来存储应该适应的对象,并且在调用适配器的方法时,适配器会将这些调用转换为适应的对象可以理解的内容,并将这些调用传递给适应的对象。 调用适配器的代码永远不需要知道它不是在处理它认为的对象类型,而是在处理对象。
现在,让我们看看如何解决Max的问题。
3.解决问题
当前,该代码公开给Xpay接口。 该界面如下所示:
package com.javacodegeeks.patterns.adapterpattern.xpay; public interface Xpay { public String getCreditCardNo(); public String getCustomerName(); public String getCardExpMonth(); public String getCardExpYear(); public Short getCardCVVNo(); public Double getAmount(); public void setCreditCardNo(String creditCardNo); public void setCustomerName(String customerName); public void setCardExpMonth(String cardExpMonth); public void setCardExpYear(String cardExpYear); public void setCardCVVNo(Short cardCVVNo); public void setAmount(Double amount); }
它包含一组setter和getter方法,用于获取有关信用卡和客户名称的信息。 此Xpay
接口在用于实例化此类型对象的代码中实现,并将该对象暴露给供应商的API。
下列类定义了Xpay
接口的实现。
package com.javacodegeeks.patterns.adapterpattern.site; import com.javacodegeeks.patterns.adapterpattern.xpay.Xpay; 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.javacodegeeks.patterns.adapterpattern.payd; 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.javacodegeeks.patterns.adapterpattern.site; import com.javacodegeeks.patterns.adapterpattern.payd.PayD; import com.javacodegeeks.patterns.adapterpattern.xpay.Xpay; 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()); } }
在上面的代码中,我们创建了一个Adapter( XpayToPayDAdapter
)。 适配器实现了PayD接口,因为需要像PayD
类型的对象一样进行模拟。 适配器使用对象合成来保存对象(应该是自适应的),该对象是Xpay
类型的对象。 该对象通过其构造函数传递到适配器中。
现在,请注意,我们有两种不兼容的接口类型,为了使代码正常工作,我们需要使用适配器将它们配合在一起。 这两个接口具有一组不同的方法。 但是,这些接口的唯一用途非常相似,即向其特定供应商提供客户和信用卡信息。
上一类的setProp()
方法用于将xpay的属性设置为payD的对象。 我们在两个界面中设置工作方式相似的方法。 但是,只有在PAYD接口单一方法来设置月份和信用卡的一年,而在这两种方法Xpay
接口。 我们加入了Xpay对象的两种方法( this.xpay.getCardExpMonth()+"/"+this.xpay.getCardExpYear()
)的结果,并将其设置为setCardExpMonthDate()
方法。
让我们测试上面的代码,看看它是否可以解决Max的问题。
package com.javacodegeeks.patterns.adapterpattern.site; import com.javacodegeeks.patterns.adapterpattern.payd.PayD; import com.javacodegeeks.patterns.adapterpattern.xpay.Xpay; 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()); } }
在上面的类中,首先我们创建了一个Xpay对象并设置了它的属性。 然后,我们创建了一个适配器,并将该xpay对象传递给它的构造函数,并将其分配给PayD
接口。 testPayD()
静态方法采用PayD
类型作为参数,该参数运行并打印其方法以进行测试。 就传递给testPayD()
方法的类型为PayD
类型PayD
该方法将PayD
执行对象。 上面,我们给它传递了一个适配器,它看起来像是PayD
的类型,但是在内部它包装了Xpay
类型的对象。
因此,在Max的项目中,我们需要在代码中实现供应商的API,并将此适配器传递给供应商的方法以使付款工作正常。 我们不需要更改现有代码中的任何内容。
4.类适配器
适配器有两种类型,对象适配器和类适配器。 到目前为止,我们已经看到了使用对象组成的对象适配器的示例,而类适配器则依靠多重继承来使一个接口适应另一个接口。 由于Java不支持多重继承,因此我们无法向您展示多重继承的示例,但是您可以牢记这一点,并可以用您喜欢的支持多种继承的面向对象语言(如c ++)之一来实现。
为了实现类适配器,适配器将从Target公开继承,并从Adaptee私有继承。 结果,适配器将是Target的子类型,但不是Adaptee的子类型。
5.何时使用适配器模式
在以下情况下应使用适配器模式:
- 有一个现有的类,并且其接口与您需要的接口不匹配。
- 您想创建一个可重用的类,与不相关或无法预料的类配合使用,即,不一定具有兼容接口的类。
- 现有多个子类可以使用,但是通过对每个子类进行子类化来调整其接口是不切实际的。 对象适配器可以调整其父类的接口。
6.下载源代码
这是有关适配器模式的课程。 您可以在此处下载源代码: AdapterPattern-Project
翻译自: https://www.javacodegeeks.com/2015/09/adapter-design-pattern.html