我想演示使用的适配器模式 ,以我的团队。 我读过很多书和网上的文章。 每个人都引用这是理解的概念(形状,存储卡,电子适配器等)有用的例子,但没有真实的案例研究。
能否请您分享适配器模式的任何案例?
PS我试图寻找计算器上存在的问题,但并没有找到答案,以便发布它作为一个新的问题。 如果你已经知道有这个答案,那么请重定向。
我想演示使用的适配器模式 ,以我的团队。 我读过很多书和网上的文章。 每个人都引用这是理解的概念(形状,存储卡,电子适配器等)有用的例子,但没有真实的案例研究。
能否请您分享适配器模式的任何案例?
PS我试图寻找计算器上存在的问题,但并没有找到答案,以便发布它作为一个新的问题。 如果你已经知道有这个答案,那么请重定向。
适配器的许多实例是微不足道的或不现实的( 矩形与LegacyRectangle,棘轮与插座 , SquarePeg VS RoundPeg , 鸭与土耳其 )。 更糟的是,很多人不显示不同Adaptees多个适配器 ( 有人引用Java的Arrays.asList因为适配器模式的一个例子 )。 适应只有一个类的接口与其他工作似乎GoF的Adapter模式的弱例子。 该模式使用继承和多态,所以可以预期一个很好的例子,以示对不同adaptees适配器的多种实现 。
我发现最好的例子就是在第26章UML和模式应用:介绍面向对象分析与设计和迭代开发(第3版) 。 以下图片是从提供的FTP站点上这本书的教练材料。
第一个显示的应用程序如何使用多种实现(adaptees)在功能上是相似的(如税务计算器,会计模块,信贷授权服务等),但有不同的API。 我们要避免硬编码我们的领域层的代码来处理不同的可能方法计算纳税,售后,授权信用卡的请求,等等这些都是可能改变所有外部组件,为此我们不能修改码。 该适配器允许我们做硬编码的调节器,而我们的领域层的代码总是使用相同的界面(IWhateverAdapter接口)。
我们没有在上面的图中看到的实际adaptees。 但是,下面的图显示了如何将一个多态调用postSale(...)
在IAccountingAdapter接口由,导致通过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));
转换一个接口到另一个接口。
适配器模式的任何真实的例子
为了连接能力,我们可以在世界各地不同的接口。 使用适配器,我们可以轻松连接像明智的。
下面是模拟转换的示例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);
}
}
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()));
}
}
<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>
只要运行单元测试。
Adapter模式可以作为两个不兼容的接口之间的桥梁。 该图案涉及单个类称为适配器,其负责两个独立的或不兼容的接口之间的通信。
真实世界的例子可能是一个语言翻译或手机充电器。 这里更多在这个YouTube视频:
YouTube的-适配器设计模式:简介
你可以找到作为对这里注入攻击防御Adapter模式的PHP实现:
http://www.php5dp.com/category/design-patterns/adapter-composition/
其中一个适配器模式的一个有趣的方面是,它有两种形式:一类适配器依靠多重继承和对象适配器依靠组合物。 上面的示例依赖于组合物中。
一个真实的例子是Qt的乌思。
的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的乌思看过来完整的例子 -
http://www.tune2wizard.com/linux-qt-signals-and-slots-qt-d-bus/
当你要处理类似行为不同的接口(这通常意味着有类似的行为,但用不同的方法的类),您可以使用适配器的设计模式。 它的一个例子是一类连接到三星电视,另一个连接到索尼电视机。 他们将分享像打开的菜单共同的行为,开始播放时,连接到网络和等,但每个库都会有不同的实现它(用不同的方法名称和签名)。 这些不同的供应商特定的实施方式中被称为在UML图适配者 。
所以,在你的代码(称为客户端的UML图),而不是硬编码每个供应商(或适配者 ),然后你可以创建一个通用的接口(在UML图中称为目标 )来包装这些相似的行为和工作的方法调用只用一种类型的对象。
然后, 适配器将实现目标接口的委托方法调用到通过构造函数传递给适配器的Adaptees。
为你实现这在Java代码中,我使用完全相同上面使用的适配器来处理多个智能电视的接口中提到的相同的例子写了一个非常简单的项目。 该代码是小的,有据可查和自我解释这样挖就可以看到一个真实的世界实现会是什么样子。
只需下载代码,然后将其导入到Eclipse(或者您喜欢的IDE)作为Maven项目。 您可以通过运行org.example.Main.java执行代码。 请记住,这里最重要的是要了解如何类和接口组装在一起设计模式。 我也创造了包com.thirdparty.libs一些假Adaptees。 希望能帮助到你!
https://github.com/Dannemann/java-design-patterns
一个真实的例子可以在上报的申请文件。 简单的代码在这里。
适配器我认为是编程结构是非常有用的。
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
使用适配器时,你有你需要使用一个接口,你无法改变,但是。 把它看作你在办公室新来的家伙,你不能让灰色头发遵循你的规则 - 你必须适应他们的。 下面是一个真实的项目一个真实的例子,我有时曾在其中一个给定的用户界面。
您有读取文件中的所有行到一个列表数据结构,并在网格中显示他们的应用程序(我们称之为底层数据存储接口IDataStore)。 用户可以通过点击按钮“第一页”,“上一页”,“下一页”,“最后一页”通过对这些数据进行导航。 一切工作正常。
现在,应用程序需要与生产日志条目过大,读入内存,但用户仍然需要通过它来浏览使用! 一个解决方案是实现一个缓存存储的第一页,接下来,以前的页和最后一页。 我们要的是当用户点击“下一页”,我们从缓存返回页面和更新缓存; 当他们点击最后一页,我们从缓存中返回最后一页。 在此背景下,我们有一个FILESTREAM做所有的魔法。 通过这样做,我们只在内存中有四个页面,而不是整个文件。
您可以使用适配器到这个新的缓存功能添加到您的应用程序,而用户注意到它。 我们扩展当前IDataStore并调用它CacheDataStore。 如果要加载的文件很大,我们使用CacheDataStore。 当我们为一,下一首,上页和最后一页的请求,该信息被发送到我们的缓存。
谁知道,明天老板要开始从数据库表中读取文件。 你要做的就是仍然延长IDataStore到SQLDataStore像你一样的高速缓存,安装在后台的连接。 当他们点击下一页,你生成必要的SQL查询,接下来的几个几百行从数据库中读取。
从本质上讲,应用程序的初始界面并没有改变。 我们只是适应现代和很酷的功能去解决问题,同时保留原有的接口。
@JusticeØ的例子不谈论适配器模式清晰。 扩展他的答案 - 我们现有的,我们的消费者的代码使用接口IDataStore,我们不能改变它。 现在,我们被要求使用来自XYZ库,做什么,我们要实现一个很酷的新阶级,但却但是,我们无法改变这个类来扩展我们的IDataStore,已经看到这个问题? 创建一个新的类 - 适配器,实现了接口我们的消费代码期望,即IDataStore并且通过使用从它的功能,我们需要有库班 - 适配者,在我们ADAPTER一员,我们能够达到我们想要。
按“C#3.0的设计模式”一书由朱主教,苹果使用适配器模式适应的Mac OS与英特尔产品的工作(详见第4号解释,在这里摘录2 )
这是适配器实现的例子:
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);
}