适配器 – 适配器模式的任何实例
我想演示如何使用适配器模式给我的团队。 我在网上阅读了很多书和文章。 大家都在引用一个有助于理解概念(形状,存储卡,电子适配器等)的例子,但没有真正的案例研究。
你可以请分享适配器模式的任何案例研究?
PS我试图在stackoverflow上search现有的问题,但没有find答案,所以张贴它作为一个新的问题。 如果您知道已经有这个答案,请redirect。
许多适配器的例子是微不足道的或不切实际的( Rectangle vs. LegacyRectangle,Ratchet vs. Socket , SquarePeg vs RoundPeg , Duck vs. Turkey )。 更糟糕的是,许多不显示适用于不同适配器的多个适配器 ( 有人引用Java的Arrays.asList作为适配器模式的示例 )。 调整只有一个类的接口与另一个接口一起工作似乎是GoF适配器模式的一个弱示例。 这种模式使用inheritance和多态,所以可以期望一个很好的例子来显示不同适配器的多个适配器实现 。
我发现的最好的例子是应用UML和模式:面向对象分析和devise和迭代开发(第3版)的第26章。 以下图像来自本书的FTP站点上提供的教师资料。
第一个显示了应用程序如何使用function相似的多个实现(适配器)(例如税务计算器,会计模块,信用授权服务等),但具有不同的API。 我们希望避免硬编码我们的领域层代码,以处理计算税款,售后,授权信用卡请求等各种可能的方式。这些都是可能会有所不同的外部模块,我们不能修改码。 适配器允许我们在适配器中进行硬编码,而我们的域层代码总是使用相同的接口(IWhateverAdapter接口)。
在上图中我们没有看到实际的适应者。 但是,下图显示了如何在IAccountingAdapter接口中对postSale(...)
进行多态调用,这会导致通过SOAP向SAP系统发布销售。
任何适配器模式的真实例子
为了连接电源,我们在世界各地有不同的接口。 使用适配器,我们可以像明智地轻松连接
如何把一个法国人变成一个正常的人…
public interface IPerson { string Name { get; set; } } public interface IFrenchPerson { string Nom { get; set; } } public class Person : IPerson { public string Name { get; set; } } public class FrenchPerson : IFrenchPerson { public string Nom { get; set; } } public class PersonService { public void PrintName(IPerson person) { Debug.Write(person.Name); } } public class FrenchPersonAdapter : IPerson { private readonly IFrenchPerson frenchPerson; public FrenchPersonAdapter(IFrenchPerson frenchPerson) { this.frenchPerson = frenchPerson; } public string Name { get { return frenchPerson.Nom; } set { frenchPerson.Nom = value; } } }
例
var service = new PersonService(); var person = new Person(); var frenchPerson = new FrenchPerson(); service.PrintName(person); service.PrintName(new FrenchPersonAdapter(frenchPerson));
适配器模式作为两个不兼容的接口之间的桥梁。 这种模式涉及一个称为适配器的类,它负责两个独立或不兼容的接口之间的通信。
真实世界的例子可能是一个语言翻译或移动充电器。 更多在这里youtubevideo:
Youtube – 适配器devise模式:简介
这是一个模拟将analog data
转换为digit data
的例子。
它提供了一个将浮点数字数据转换为二进制数据的适配器,在现实世界中可能没有用处,它只是帮助解释适配器模式的概念。
码
AnalogSignal.java
package eric.designpattern.adapter; public interface AnalogSignal { float[] getAnalog(); void setAnalog(float[] analogData); void printAnalog(); }
DigitSignal.java
package eric.designpattern.adapter; public interface DigitSignal { byte[] getDigit(); void setDigit(byte[] digitData); void printDigit(); }
FloatAnalogSignal.java
package eric.designpattern.adapter; import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FloatAnalogSignal implements AnalogSignal { private Logger logger = LoggerFactory.getLogger(this.getClass()); private float[] data; public FloatAnalogSignal(float[] data) { this.data = data; } @Override public float[] getAnalog() { return data; } @Override public void setAnalog(float[] analogData) { this.data = analogData; } @Override public void printAnalog() { logger.info("{}", Arrays.toString(getAnalog())); } }
BinDigitSignal.java
package eric.designpattern.adapter; import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BinDigitSignal implements DigitSignal { private Logger logger = LoggerFactory.getLogger(this.getClass()); private byte[] data; public BinDigitSignal(byte[] data) { this.data = data; } @Override public byte[] getDigit() { return data; } @Override public void setDigit(byte[] digitData) { this.data = digitData; } @Override public void printDigit() { logger.info("{}", Arrays.toString(getDigit())); } }
AnalogToDigitAdapter.java
package eric.designpattern.adapter; import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <p> * Adapter - convert analog data to digit data. * </p> * * @author eric * @date Mar 8, 2016 1:07:00 PM */ public class AnalogToDigitAdapter implements DigitSignal { public static final float DEFAULT_THRESHOLD_FLOAT_TO_BIN = 1.0f; // default threshold, private Logger logger = LoggerFactory.getLogger(this.getClass()); private AnalogSignal analogSignal; private byte[] digitData; private float threshold; private boolean cached; public AnalogToDigitAdapter(AnalogSignal analogSignal) { this(analogSignal, DEFAULT_THRESHOLD_FLOAT_TO_BIN); } public AnalogToDigitAdapter(AnalogSignal analogSignal, float threshold) { this.analogSignal = analogSignal; this.threshold = threshold; this.cached = false; } @Override public synchronized byte[] getDigit() { if (!cached) { float[] analogData = analogSignal.getAnalog(); int len = analogData.length; digitData = new byte[len]; for (int i = 0; i < len; i++) { digitData[i] = floatToByte(analogData[i]); } } return digitData; } // not supported, should set the inner analog data instead, @Override public void setDigit(byte[] digitData) { throw new UnsupportedOperationException(); } public synchronized void setAnalogData(float[] analogData) { invalidCache(); this.analogSignal.setAnalog(analogData); } public synchronized void invalidCache() { cached = false; digitData = null; } @Override public void printDigit() { logger.info("{}", Arrays.toString(getDigit())); } // float -> byte convert, private byte floatToByte(float f) { return (byte) (f >= threshold ? 1 : 0); } }
代码 – testing用例
AdapterTest.java
package eric.designpattern.adapter.test; import java.util.Arrays; import junit.framework.TestCase; import org.junit.Test; import eric.designpattern.adapter.AnalogSignal; import eric.designpattern.adapter.AnalogToDigitAdapter; import eric.designpattern.adapter.BinDigitSignal; import eric.designpattern.adapter.DigitSignal; import eric.designpattern.adapter.FloatAnalogSignal; public class AdapterTest extends TestCase { private float[] analogData = { 0.2f, 1.4f, 3.12f, 0.9f }; private byte[] binData = { 0, 1, 1, 0 }; private float[] analogData2 = { 1.2f, 1.4f, 0.12f, 0.9f }; @Test public void testAdapter() { AnalogSignal analogSignal = new FloatAnalogSignal(analogData); analogSignal.printAnalog(); DigitSignal digitSignal = new BinDigitSignal(binData); digitSignal.printDigit(); // adapter AnalogToDigitAdapter adAdapter = new AnalogToDigitAdapter(analogSignal); adAdapter.printDigit(); assertTrue(Arrays.equals(digitSignal.getDigit(), adAdapter.getDigit())); adAdapter.setAnalogData(analogData2); adAdapter.printDigit(); assertFalse(Arrays.equals(digitSignal.getDigit(), adAdapter.getDigit())); } }
依赖 – 通过maven
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.2</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.13</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.13</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency>
如何testing
只要运行unit testing。
您可以在这里find一个用于防御注入攻击的适配器模式的PHP实现:
http://www.php5dp.com/category/design-patterns/adapter-composition/
Adapter模式的一个有趣的方面是它有两种风格:依赖多重inheritance的类适配器和依赖于组合的对象适配器。 上面的例子依赖于组合。
一个真实的例子是Qt-Dbus。
qt-dbus有一个实用程序,用于从所提供的xml文件生成适配器和接口代码。 以下是这样做的步骤。
1. Create the xml file - this xml file should have the interfaces that can be viewed by the qdbus-view in the system either on the system or session bus. 2.With the utility - qdbusxml2cpp , you generate the interface adaptor code. This interface adaptor does the demarshalling of the data that is received from the client. After demarshalling, it invokes the user defined - custom methods ( we can say as adaptee). 3. At the client side, we generate the interface from the xml file. This interface is invoked by the client. The interface does the marshalling of the data and invokes the adaptor interface. As told in the point number 2, the adaptor interface does the demarshalling and calls the adaptee - user defined methods.
你可以在这里看到Qt-Dbus的完整例子 –
http://www.tune2wizard.com/linux-qt-signals-and-slots-qt-d-bus/
一个真实的例子可以是在应用程序中报告文档。 这里简单的代码。
我认为适配器对编程结构非常有用。
class WordAdaptee implements IReport{ public void report(String s) { System.out.println(s +" Word"); } } class ExcellAdaptee implements IReport{ public void report(String s) { System.out.println(s +" Excel"); } } class ReportAdapter implements IReport{ WordAdaptee wordAdaptee=new WordAdaptee(); @Override public void report(String s) { wordAdaptee.report(s); } } interface IReport { public void report(String s); } public class Main { public static void main(String[] args) { //create the interface that client wants IReport iReport=new ReportAdapter(); //we want to write a report both from excel and world iReport.report("Trial report1 with one adaptee"); //we can directly write the report if one adaptee is avaliable //assume there are N adaptees so it is like in our example IReport[] iReport2={new ExcellAdaptee(),new WordAdaptee()}; //here we can use Polymorphism here for (int i = 0; i < iReport2.length; i++) { iReport2[i].report("Trial report 2"); } } }
结果将是:
Trial report1 with one adaptee Word Trial report 2 Excel Trial report 2 Word
当你有一个你不能改变的界面时,使用适配器,但是你需要使用它。 看到你是办公室里的新人,你不能让灰发遵循你的规则 – 你必须适应他们的规则。 下面是一个真实的例子,我从事的是一个真正的项目,其中用户界面是给定的。
您有一个应用程序,将文件中的所有行读取到List数据结构中,并将它们显示在网格中(让我们调用底层数据存储接口IDataStore)。 用户可以通过点击“第一页”,“上一页”,“下一页”,“最后一页”button浏览这些数据。 一切工作正常。
现在应用程序需要与生产日志一起使用,这些日志太大而无法读入内存,但用户仍然需要浏览它! 一种解决scheme是实现一个caching,存储第一页,下一页,上一页和最后一页。 我们想要的是,当用户点击“下一页”时,我们从caching中返回页面并更新caching; 当他们点击最后一页时,我们从caching中返回最后一页。 在后台我们有一个文件stream,做所有的魔术。 通过这样做,我们在内存中只有四个页面,而不是整个文件。
您可以使用适配器将这个新的cachingfunction添加到您的应用程序,而无需用户注意到它。 我们扩展当前的IDataStore并将其称为CacheDataStore。 如果要加载的文件很大,我们使用CacheDataStore。 当我们请求第一个,下一个,上一页和最后一页的时候,这些信息会被路由到我们的caching。
谁知道,明天老板想要开始从数据库表中读取文件。 你所做的只是继续扩展IDataStore到SQLDataStore,就像你为Cache所做的那样,在后台build立连接。 当他们单击下一页时,您将生成必要的SQL查询以从数据库中读取下几百行数据。
本质上,应用程序的原始界面没有改变。 我们简单地调整现代和酷的function,同时保留传统的界面。
@Justice o的例子并不清楚适配器模式。 扩展他的答案 – 我们有消费者代码使用的现有接口IDataStore,我们不能改变它。 现在我们被要求使用XYZ库中一个很酷的新类来实现我们想要实现的function,但是但是,我们不能改变这个类来扩展我们的IDataStore,看到这个问题了吗? 创build一个新的类 – ADAPTER,实现我们的消费者代码所期望的接口,即IDataStore,并通过使用我们需要的特性库ADAPTEE中的类作为ADAPTER中的成员,我们可以实现我们想要的。
这是适配器实现的一个例子:
interface NokiaInterface { chargementNokia(x:boolean):void } class SamsungAdapter implements NokiaInterface { //nokia chargement adapted to samsung chargementNokia(x:boolean){ const old= new SamsungCharger(); let y:number = x ? 20 : 1; old.charge(y); } } class SamsungCharger { charge(x:number){ console.log("chrgement x ==>", x); } } function main() { //charge samsung with nokia charger const adapter = new SamsungAdapter(); adapter.chargementNokia(true); }