【问题标题】:when do we need Adapter pattern?我们什么时候需要适配器模式?
【发布时间】:2010-08-13 15:14:18
【问题描述】:

我们什么时候需要使用适配器模式?如果可能的话,请给我一个适合该模式的真实示例。

【问题讨论】:

  • 您是否打算通读整本 GoF 书籍并询问何时需要 X 模式?
  • 只要它给了他所有这些支持,如果我是他,我会询问每一种设计模式:-P
  • @Thomas Owens:我所寻求的只是一个很好的真实世界示例,它将帮助我了解这种模式。

标签: design-patterns adapter


【解决方案1】:

我在一个需要与外部 DVR 接口的系统上工作。大多数情况下,所有 DVR 都具有相同的基本功能:从某个视频源开始录制;停止录音;从某个时间开始播放;停止播放等。

每个 DVR 制造商都提供了一个软件库,允许我们编写代码来控制他们的设备(为了便于讨论,我将其称为 SDK)。尽管每个 SDK 都为所有基本功能提供了 API,但它们都不完全相同。这是一个非常粗略的例子,但你明白了:

  • BeginPlayback(DateTime startTime);
  • StartPlayback(long startTimeTicks);
  • 播放(字符串开始日期,字符串开始时间);

我们的软件需要能够与所有 DVR 交互。因此,我们没有为每个不同的 SDK 编写可怕的开关/案例,而是创建了自己的通用 IDVRController 接口,并将我们所有的系统代码写入该接口:

  • 播放(日期时间开始时间);

然后我们为每个 SDK 编写了不同的适配器实现,所有这些都实现了我们的 IDVRController 接口。我们使用配置文件来指定系统将连接到的 DVR 的类型,并使用工厂模式来实例化该 DVR 的 IDVRController 的正确实现者。

通过这种方式,适配器模式使我们的系统代码更简单:我们总是对 IDVRController 进行编码。它允许我们在部署后为新的 SDK 推出适配器(我们的工厂使用反射来实例化正确的 IDVRController 实例)。

【讨论】:

  • 优秀的现实生活例子。它帮助我理解了这种模式的真正目的。我已经看到这种模式主要与服务定位器或工厂模式一起使用。
【解决方案2】:

现有接口

interface Shape {
    public int calculateArea(int r);
}

Shape 接口的当前实现

class Square implements Shape {
    @Override
    public int calculateArea(int r) {
        return r * r;
    }
}

现在考虑您希望 Circle 类适应我们现有的接口,我们无法修改(由第三方编写)。

class Circle {
    public double calculateCircularArea (int r) {
        return 3.14 * r * r;
    }
}

现在我们已经使 Circle 实现适应我们的 Shape 接口。所以我们需要一个适配器,因为它们不兼容。

class CircleAdaptor extends Circle implements Shape {
    @Override
    public int calculateArea(int r) {
        return (int) calculateCircularArea(r);
    }
}

CircleAdaptor - 是圆形的适配器;
Circle - 是适应者;
形状 - 是目标界面。

public class AdapterPattern {
    public static void main(String[] args) {
        Shape circle = new CirCleAdaptor();
        System.out.println("Circle Area " + circle.calculateArea(5));
        Shape square = new Square();
        System.out.println("Square Area " + square.calculateArea(5));
    }
}

希望这能让您更好地了解何时使用它。
另见what is Decorator pattern?

【讨论】:

    【解决方案3】:

    在计算机编程中,适配器 模式(通常称为 包装器模式或只是一个包装器) 是一种设计模式,可以翻译 一个类的一个接口变成一个 兼容的接口。一个适配器 允许类一起工作 通常不能因为 不兼容的接口,通过提供 它在使用时与客户端的接口 原来的界面。适配器 将对其接口的调用转换为 调用原始接口,以及 需要做的代码量 这通常很小。适配器 也负责改造 数据转换成适当的形式。为了 例如,如果多个布尔值 存储为单个整数,但 您的消费者需要 'true'/'false',适配器将是 负责提取 整数中的适当值 价值。

    Wikipedia!!!

    【讨论】:

      【解决方案4】:

      当您必须处理具有相似行为的不同接口(这通常意味着具有相似行为但具有不同方法的类)时,您可以使用适配器设计模式。一个例子是一个连接到三星电视的类和另一个连接到索尼电视的类。它们将共享常见的行为,如打开菜单、开始播放、连接到网络等,但每个库都有不同的实现(具有不同的方法名称和签名)。这些不同的供应商特定实现在 UML 图中称为 Adaptee

      因此,在您的代码(在 UML 图中称为 Client)中,您可以创建每个供应商(或 Adaptee)的方法调用,而不是硬编码一个通用接口(在 UML 图中称为 Target)来包装这些类似的行为并只使用一种类型的对象。

      Adapters 然后将实现 Target 接口,将其方法调用委托给传递给 AdaptersAdapters 的 Adaptees strong> 通过构造函数。

      为了让您在 Java 代码中实现这一点,我编写了一个非常简单的项目,使用与上面提到的完全相同的示例,使用适配器来处理多个智能电视接口。代码很小,有据可查且一目了然,因此请深入研究它以了解实际实现的样子。

      只需下载代码并将其作为 Maven 项目导入 Eclipse(或您最喜欢的 IDE)。您可以通过运行 org.example.Main.java 来执行代码。请记住,这里重要的是了解类和接口如何组合在一起来设计模式。我还在 com.thirdparty.libs 包中创建了一些假的 Adaptees。希望对您有所帮助!

      https://github.com/Dannemann/java-design-patterns

      【讨论】:

        【解决方案5】:

        以下场景需要适配器模式:

        假设您使用方法M1M2 定义了一个接口I1

        C1C2 实现了这个接口I1,现在是C1,同时实现M1M2 你没有找到其他现有类的帮助,所以你需要自己编写所有逻辑。

        现在在实现C2 类时,您遇到了C3 类,其方法M3M4 可用于为C2 实现M1M2,以便利用这些@987654337 @ 和 M4 在类 C2 中扩展类 C3 并使用 M3M4C3

        在此示例中,C2 变为 Adapter classC3 变为 adaptee

        package com.design.patterns;
        
        public class AdapterExample {
            public static void main(String[] args) {
                Shape line = new LineShape();
                line.draw();
        
                Shape text = new TextShape();
                text.draw();
            }
        }
        
        //==Start from here
        interface Shape{
            public void draw();
        }
        
        class LineShape implements Shape{
            @Override
            public void draw() {
                System.out.println("write some logic and draw line");
            }   
        }
        
        //Adapter
        class TextShape extends TextView implements Shape{
            @Override
            public void draw() {
                System.out.println("logic is already there in class TextView");
                drawText();
            }   
        }
        
        // Adaptee
        class TextView{
            public void drawText() {
                System.out.println("Drawing Text Shape");
            }
        }
        

        【讨论】:

          【解决方案6】:

          不兼容的接口

          EuroPlug 连接器仅连接到欧洲电源插座:

          interface EuroPlug {
             fun plugIn()
          }
          
          class EuroSocket {
             fun supplyCurrent(plug: EuroPlug) = plug.plugIn()
          }
          

          USPlug 连接器仅连接到美国电源插座:

          interface USPlug {
             fun plugIn()
          }
          
          class USSocket {
             fun supplyCurrent(plug: USPlug) = plug.plugIn()
          }
          

          创建适配器

          当我们有USSocketEuroPlug 时,我们创建一个适配器来将EuroPlug 转换为USPlug

          class EuroToUSPlugAdapter(private val euroPlug: EuroPlug) : USPlug {
             override fun plugIn() = euroPlug.plugIn()
          }
          

          现在EuroToUSPlugAdapter 不改变现有类USSocket 的接口USPlug


          使用适配器

          这里我们有一个USSocket,但有一个EuroPlug 对象。因此,我们将EuroPlug 对象传递给EuroToUSPlugAdapter,它负责将EuroPlug 转换为USPlug

          fun main() {
             val usSocket = USSocket()
             val euroPlug = object : EuroPlug {
                 override fun plugIn() {
                     println("Euro plug adapted for US Socket")
                 }
             }
            
             val euroAdapter = EuroToUSPlugAdapter(euroPlug)
             usSocket.supplyCurrent(euroAdapter)
          }
          

          就是这样!这就是适配器模式允许两个不兼容的接口一起工作的方式。希望对您有所帮助。

          【讨论】:

            【解决方案7】:

            适配器模式的一个非常常见的示例是通过 Service Provider Interface 完成的,并且在很多 Java EE 框架中都普遍使用。

            这样做的原因是允许 Java EE 的不同实现,但程序员只是按照 Java EE 规范编写代码,而不是特定于实现的代码。

            与直接使用 WebSphere 类进行编码之类的东西相反,后者会锁定您使用 WebSphere。

            或者更糟(根据我的经验),Apache HTTP 客户端,后来发现因为你编码到那个实现而不是正常的 HttpUrlConnection 你必须做很多重新编码,因为它不支持当前版本的 TLS如果原始开发人员编写了更稳定的 API 而我们只需要升级 Java 运行时,就可以避免这种情况。

            【讨论】:

              【解决方案8】:

              我在 Bharath Thippireddy 设计模式课程中找到了最清晰、最真实的例子。 我们有WeatherUI 类,它通过邮政编码查找温度,我们有WeatherFinderImpl 类,它通过城市名称知道温度,所以我们需要创建WeatherAdapter 类,它将采用邮政编码,将其更改为城市名称并调用@ 987654324@ 类返回适当的温度。 WeatherUi类:

              public class WeatherUI {
                  public void showTemperature(int zipcode) {
                      //I just have zipcode
                  }
              }
              

              WeatherFinder接口:

              public interface WeatherFinder {
                  int find(String city);
              }
              

              WeatherFinderImpl只能通过城市名称查找温度:

              public class WeatherFinderImpl implements WeatherFinder{
                  @Override
                  public int find(String city) {
                      return 25;
                  }
              }
              

              将这三个放在WeatherUI 类中,我们可以创建一个将邮政编码转换为城市的方法,或者我们可以创建一个新的适配器类:

              public class WeatherAdapter {
                  public int findTemperature(int zipcode){
                      String city = null;
                      
                      //here most probably we would take it from db, in example hardcode is enough
                      if(zipcode == 63400){ 
                          city = "Ostrow Wielkopolski";
                      }
              
                      WeatherFinder finder = new WeatherFinderImpl();
                      int temperature = finder.find(city);
                      return temperature;
                  }
              }
              

              并在WeatherUI 中调用它:

              public class WeatherUI {
                  public void showTemperature(int zipcode) {
                      WeatherAdapter adapter = new WeatherAdapter();
                      System.out.println(adapter.findTemperature(63400));
                  }
              }
              

              对其进行测试很简单:

                  public static void main(String[] args) {
                      WeatherUI ui = new WeatherUI();
                      ui.showTemperature(63400);
                  }
              

              【讨论】:

                猜你喜欢
                • 2011-03-29
                • 1970-01-01
                • 2017-06-14
                • 2019-07-17
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-03-30
                相关资源
                最近更新 更多