我们什么时候需要适配器模式?

发布于 2024-09-14 02:37:47 字数 48 浏览 3 评论 0原文

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

When do we need to go for Adapter pattern? If possible give me a real world example that suits that pattern.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

ゝ偶尔ゞ 2024-09-21 02:37:47

我开发了一个需要与外部 DVR 接口的系统。大多数情况下,所有 DVR 都具有相同的基本功能:从某个视频源开始录制;停止录音;从某个时间开始播放; 每个 DVR 制造商都提供了一个软件库,允许我们编写代码来

控制他们的设备(为了讨论起见,我将其称为 SDK)。尽管每个 SDK 都提供了所有基本功能的 API,但它们都不完全相同。这是一个非常粗略的示例,但您明白了:

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

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

  • Playback(DateTime startTime);

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

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

I worked on a system which needed to interface with external DVRs. For the most part, all DVRs have the same basic functionality: start recording from a certain video source; stop recording; start playback from a certain time; stop playback, etc.

Every DVR manufacturer provided a software library, allowing us to write code to control their device (for sake of this discussion, I'll refer to it as the SDK). Even though every SDK provided APIs for all the basic functionality, none of them were quite the same. Here's a very rough example, but you get the idea:

  • BeginPlayback(DateTime startTime);
  • StartPlayback(long startTimeTicks);
  • Playback(string startDate, string startTime);

Our software needed to be able to interact with all DVRs. So instead of writing horrible switch/cases for each different SDK, we created our own common IDVRController interface, and wrote all of our system code to that interface:

  • Playback(DateTime startTime);

We then wrote a different adapter implementation for each SDK, all of which implemented our IDVRController interface. We used a config file to specify the type of DVR the system would connect to, and a Factory pattern to instantiate the correct implementer of IDVRController for that DVR.

In that way, the adapter pattern made our system code simpler: we always coded to IDVRController. And it allowed us to roll out adapters for new SDKs post-deployment (our Factory used reflection to instantiate the correct IDVRController instance).

若沐 2024-09-21 02:37:47

现有接口

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));
    }
}

希望这可以让您更好地了解何时使用它。
另请参阅什么是装饰器模式?

Existing Interface

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

Current Implementation for Shape interface

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

Now Consider that you want Circle class to adapt to our existing interface which in no way we can modify (Written by third party).

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

Now we have adapt Circle implementation to our Shape interface. So we need an adaptor as they are incompatible.

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

CircleAdaptor - Is the Adaptor for Circle;
Circle - Is the Adaptee;
Shape - Is the Target Interface.

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));
    }
}

Hope this gives a better idea about when to use it.
See also what is Decorator pattern?

揽清风入怀 2024-09-21 02:37:47

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

替代文本

维基百科!!!

In computer programming, the adapter
pattern (often referred to as the
wrapper pattern or simply a wrapper)
is a design pattern that translates
one interface for a class into a
compatible interface. An adapter
allows classes to work together that
normally could not because of
incompatible interfaces, by providing
its interface to clients while using
the original interface. The adapter
translates calls to its interface into
calls to the original interface, and
the amount of code necessary to do
this is typically small. The adapter
is also responsible for transforming
data into appropriate forms. For
instance, if multiple boolean values
are stored as a single integer but
your consumer requires a
'true'/'false', the adapter would be
responsible for extracting the
appropriate values from the integer
value.

alt text

Wikipedia!!!

晚风撩人 2024-09-21 02:37:47

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

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

然后适配器将实现目标接口,将其方法调用委托给通过适配器传递给适配器Adaptees构造函数。

为了让您在 Java 代码中实现这一点,我使用与上面提到的完全相同的示例编写了一个非常简单的项目,使用适配器来处理多个智能电视接口。该代码很小,文档齐全且不言自明,因此请深入研究它以了解现实世界的实现是什么样子。

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

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

You can use the Adapter design pattern when you have to deal with different interfaces with similar behavior (which usually means classes with similar behavior but with different methods). An example of it would be a class to connect to a Samsung TV and another one to connect to a Sony TV. They will share common behavior like open menu, start playback, connect to a network and etc but each library will have a different implementation of it (with different method names and signatures). These different vendor specific implementations are called Adaptee in the UML diagrams.

So, in your code (called Client in the UML diagrams), instead of hard code the method calls of each vendor (or Adaptee), you could then create a generic interface (called Target in UML diagrams) to wrap these similar behaviors and work with only one type of object.

The Adapters will then implement the Target interface delegating its method calls to the Adaptees that are passed to the Adapters via constructor.

For you to realize this in Java code, I wrote a very simple project using exactly the same example mentioned above using adapters to deal with multiple smart TV interfaces. The code is small, well documented and self explanatory so dig on it to see how a real world implementation would look like.

Just download the code and import it to Eclipse (or your favorite IDE) as a Maven project. You can execute the code by running org.example.Main.java. Remember that the important thing here is to understand how classes and interfaces are assembled together to design the pattern. I also created some fake Adaptees in the package com.thirdparty.libs. Hope it helps!

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

夜唯美灬不弃 2024-09-21 02:37:47

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

定义了接口 I1

假设您已经使用方法 M1M2 C1 ,并且C2 实现此接口 I1,现在用于 C1,同时实现 M1M2 您没有找到其他现有类的帮助,因此您需要自己编写所有逻辑。

现在,在实现类C2时,您遇到了类C3,其方法M3M4可用于实现M1M2 用于 C2,以便在类 M3M4 中使用这些 M3M4 code>C2 扩展类 C3 并使用 C3M3M4

在此示例中,C2 变为 Adapter 类C3 变为 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");
    }
}

Adapter pattern is required in following scenario:

Say you have defined an interface I1 with method M1 and M2

C1 and C2 implements this interface I1, now for C1 while implementing M1 and M2 you have found no help from other existing classes so you need to write all logic by yourself.

Now while implementing class C2 you have come across class C3 with methods M3 and M4 that can be used to implement M1 and M2 for C2 so to utilize those M3 and M4 in class C2 you extends class C3 and use M3 and M4 of C3.

In this example C2 becomes Adapter class and C3 becomes 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");
    }
}
梦幻的味道 2024-09-21 02:37:47

不兼容的接口

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()
}

创建适配器

当我们有 USSocket 和 < code>EuroPlug,我们创建一个适配器将 EuroPlug 转换为 USPlug

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

现在 EuroToUSPlugAdapter 适配接口 USPlug< /code> 已经存在的类 USSocket 而不对其进行更改。


使用适配器

这里我们有一个 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)
}

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

Incompatible interfaces

EuroPlug connector connects only to european electrical sockets:

interface EuroPlug {
   fun plugIn()
}

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

USPlug connector connects only to US electrical sockets:

interface USPlug {
   fun plugIn()
}

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

Creating an adapter

When we have USSocket and EuroPlug, we create an adapter to convert the EuroPlug to USPlug:

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

Now EuroToUSPlugAdapter adapts the interface USPlug of the already existing class USSocket without changing it.


Using the adapter

Here we have a USSocket but with a EuroPlug object. So, we pass the EuroPlug object to the EuroToUSPlugAdapter which does the work of converting a EuroPlug to the 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)
}

That's it! This is how the adapter pattern allows two incompatible interfaces to work together. Hope that helps.

对风讲故事 2024-09-21 02:37:47

适配器模式的一个非常常见的示例是通过服务提供者接口< /a> 并且在很多 Java EE 框架中常用。

其原因是允许 Java EE 的不同实现,但程序员只需根据 Java EE 规范进行编码,而不是特定于实现的内容。

与直接使用 WebSphere 类进行编码之类的东西相反,这会限制您使用 WebSphere。

或者更糟糕(根据我的经验),Apache HTTP Client 并后来发现,因为您编码为该实现而不是正常的 HttpUrlConnection,所以您必须进行大量重新编码,因为它不支持当前版本的 TLS,这将是如果原始开发人员编码为更稳定的 API,而我们只需要升级 Java 运行时,就可以避免这种情况。

A very common example of the adapter pattern is done through the Service Provider Interface and is commonly used in a lot of the Java EE framework.

The reason for it is to allow different implementations of Java EE but programmers simply code to the Java EE spec rather than something implementation specific.

As opposed to something like coding directly using WebSphere classes which lock you into using WebSphere.

Or worse (from my experience), Apache HTTP Client and find out later that because you coded to that implementation rather than the normal HttpUrlConnection you have to do a lot of recoding because it does not support the current version of TLS which would've been avoided if the original developer coded to a more stable API and we just need to upgrade Java runtime.

忘你却要生生世世 2024-09-21 02:37:47

摘自:Alexey Soshin 的书《Kotlin 设计模式与最佳实践第二版》:

Adapter 设计模式的主要目标是将一个接口转换为
另一个界面。
在物理世界中,这个想法的最好例子是
可以是电源插头适配器USB 适配器
想象一下你深夜在酒店房间里,电池电量还剩 7%
你的手机。您的手机充电器落在办公室另一端
城市。您只有一个欧盟插头充电器迷你 USB 数据线。但你的
手机使用USB-C,因为您必须升级。你在纽约,所以所有
您的插座(当然)是 USB-A。那么,你做什么呢?哦,这很容易。你
在半夜寻找一个迷你 USB 转 USB-C 适配器并希望
您还记得带上欧盟至美国插头适配器
电量仅剩 5% – 时间不多了!
那么,既然我们了解了适配器在物理世界中的用途,那么让我们
看看我们如何在代码中应用相同的原则。
让我们从接口开始。
USPlug 假定电源为 Int。如果它有幂,它的值就是 1
任何其他值(如果没有):

interface USPlug {
 val hasPower: Int
}

EUPlug 将电源视为字符串,为 TRUE 或 FALSE:

interface EUPlug {
 val hasPower: String // "TRUE" or "FALSE"
}

对于 UsbMini,电源是一个枚举:

interface UsbMini {
 val hasPower: Power
}
enum class Power {
 TRUE, FALSE
}

最后,对于 UsbTypeC,功率是一个布尔值:

interface UsbTypeC {
 val hasPower: Boolean
}

我们的目标是将美国电源插座的功率值带到我们的
手机
,将由此函数表示:

fun cellPhone(chargeCable: UsbTypeC) {
 if (chargeCable.hasPower) {
 println("I've Got The Power!")
 } else {
 println("No power")
 }
}

让我们首先在代码中声明美国电源插座的外观。它
将是一个返回 USPlug 的函数:

// Power outlet exposes USPlug interface
fun usPowerOutlet(): USPlug {
 return object : USPlug {
 override val hasPower = 1
 }
}

我们的充电器将是一个将 EUPlug 作为输入和输出的函数
UsbMini

// Charger accepts EUPlug interface and exposes UsbMini
// interface
fun charger(plug: EUPlug): UsbMini {
 return object : UsbMini {
 override val hasPower=Power.valueOf(plug.hasPower)
 }
}

接下来,让我们尝试将我们的手机、充电器和usPowerOutlet结合起来
函数:

cellPhone(
 // Type mismatch: inferred type is UsbMini but // UsbTypeC
was expected
 charger(
 // Type mismatch: inferred type is USPlug but //
EUPlug was expected
 usPowerOutlet()
 )
)

如您所见,我们遇到了两种不同类型的错误 - 适配器设计
模式应该可以帮助我们解决这些问题。

调整现有代码
我们需要两种类型的适配器:一种用于电源插头,另一种用于电源插头
我们的 USB 端口。
我们可以通过定义以下内容来使美国插头欧盟插头配合使用
以下扩展功能:

fun USPlug.toEUPlug(): EUPlug {
 val hasPower = if (this.hasPower == 1) "TRUE" else
 "FALSE"
 return object : EUPlug {
 // Transfer power
 override val hasPower = hasPower
 }
}

我们可以在Mini USB和USB-C实例之间创建一个USB适配器
以类似的方式:

fun UsbMini.toUsbTypeC(): UsbTypeC {
 val hasPower = this.hasPower == Power.TRUE
 return object : UsbTypeC {
 override val hasPower = hasPower
 }
}

最后,我们可以通过组合所有这些适配器再次重新上线
一起:

cellPhone(
 charger(
 usPowerOutlet().toEUPlug()
 ).toUsbTypeC()
)

适配器设计模式比其他设计更简单
模式,你会看到它被广泛使用。现在,让我们更详细地讨论它在现实世界中的一些用途。

现实世界中的适配器

您可能遇到过适配器设计模式的许多用途
已经。这些通常用于适应概念和
实施。例如,让我们采用JVM集合的概念
JVM 流的概念不同。
列表是可以使用 listOf() 创建的元素集合
功能:

val list = listOf("a", "b", "c")

流是元素的惰性集合。你不能简单地通过
收集到接收流的函数,即使它可能会使
sense:

fun printStream(stream: Stream<String>) {
 stream.forEach(e -> println(e))
}

printStream(list) // Doesn't compile

幸运的是,集合为我们提供了 .stream() 适配器方法:

printStream(list.stream()) // Adapted successfully

许多其他 Kotlin 对象都有适配器方法,通常以 to 开头
作为前缀。例如,toTypedArray() 将列表转换为数组。

使用适配器的注意事项

您是否曾经将 110 V 美国电器插入 220 V EU 插座
通过适配器,然后完全炸了?
如果您不小心,您的代码也可能会发生这种情况。
以下示例使用另一个适配器,它也可以很好地编译:

val stream = Stream.generate { 42 }
stream.toList()

但它永远不会完成,因为 Stream.generate() 生成无限列表
整数。因此,请小心并明智地采用这种设计模式。

From: Alexey Soshin's Book “Kotlin Design Patterns and Best Practices Second Edition”:

The main goal of the Adapter design pattern is to convert one interface to
another interface.
In the physical world, the best example of this idea would
be an electrical plug adapter or a USB adapter.
Imagine yourself in a hotel room late in the evening, with 7% battery left on
your phone. Your phone charger was left in the office at the other end of the
city. You only have an EU plug charger with a Mini USB cable. But your
phone uses USB-C, as you had to upgrade. You're in New York, so all of
your outlets are (of course) USB-A. So, what do you do? Oh, it's easy. You
look for a Mini USB to USB-C adapter in the middle of the night and hope
that you have remembered to bring your EU to US plug adapter as well.
Only 5% battery left – time is running out!
So, now that we understand what adapters are for in the physical world, let's
see how we can apply the same principle in code.
Let's start with interfaces.
USPlug assumes that power is Int. It has 1 as its value if it has power and
any other value if it doesn't:

interface USPlug {
 val hasPower: Int
}

EUPlug treats power as String, which is either TRUE or FALSE:

interface EUPlug {
 val hasPower: String // "TRUE" or "FALSE"
}

For UsbMini, power is an enum:

interface UsbMini {
 val hasPower: Power
}
enum class Power {
 TRUE, FALSE
}

Finally, for UsbTypeC, power is a Boolean value:

interface UsbTypeC {
 val hasPower: Boolean
}

Our goal is to bring the power value from a US power outlet to our
cellphone
, which will be represented by this function:

fun cellPhone(chargeCable: UsbTypeC) {
 if (chargeCable.hasPower) {
 println("I've Got The Power!")
 } else {
 println("No power")
 }
}

Let's start by declaring what a US power outlet will look like in our code. It
will be a function that returns a USPlug:

// Power outlet exposes USPlug interface
fun usPowerOutlet(): USPlug {
 return object : USPlug {
 override val hasPower = 1
 }
}

Our charger will be a function that takes EUPlug as an input and outputs
UsbMini:

// Charger accepts EUPlug interface and exposes UsbMini
// interface
fun charger(plug: EUPlug): UsbMini {
 return object : UsbMini {
 override val hasPower=Power.valueOf(plug.hasPower)
 }
}

Next, let's try to combine our cellPhone, charger, and usPowerOutlet
functions:

cellPhone(
 // Type mismatch: inferred type is UsbMini but // UsbTypeC
was expected
 charger(
 // Type mismatch: inferred type is USPlug but //
EUPlug was expected
 usPowerOutlet()
 )
)

As you can see, we get two different type errors – the Adapter design
pattern should help us solve these.

Adapting existing code
We need two types of adapters: one for our power plugs and another one for
our USB ports.
We could adapt the US plug to work with the EU plug by defining the
following extension function:

fun USPlug.toEUPlug(): EUPlug {
 val hasPower = if (this.hasPower == 1) "TRUE" else
 "FALSE"
 return object : EUPlug {
 // Transfer power
 override val hasPower = hasPower
 }
}

We can create a USB adapter between the Mini USB and USB-C instances
in a similar way:

fun UsbMini.toUsbTypeC(): UsbTypeC {
 val hasPower = this.hasPower == Power.TRUE
 return object : UsbTypeC {
 override val hasPower = hasPower
 }
}

Finally, we can get back online again by combining all those adapters
together:

cellPhone(
 charger(
 usPowerOutlet().toEUPlug()
 ).toUsbTypeC()
)

The Adapter design pattern is more straightforward than the other design
patterns, and you'll see it used widely. Now, let's discuss some of its real world uses in more detail.

Adapters in the real world

You've probably encountered many uses of the Adapter design pattern
already. These are normally used to adapt between concepts and
implementations. For example, let's take the concept of a JVM collection
versus the concept of a JVM stream.
A list is a collection of elements that can be created using the listOf()
function:

val list = listOf("a", "b", "c")

A stream is a lazy collection of elements. You cannot simply pass a
collection to a function that receives a stream, even though it may make
sense:

fun printStream(stream: Stream<String>) {
 stream.forEach(e -> println(e))
}

printStream(list) // Doesn't compile

Luckily, collections provide us with the .stream() adapter method:

printStream(list.stream()) // Adapted successfully

Many other Kotlin objects have adapter methods that usually start with to
as a prefix. For example, toTypedArray() converts a list to an array.

Caveats of using adapters

Have you ever plugged a 110 V US appliance into a 220 V EU socket
through an adapter, and fried it totally?
If you're not careful, that's something that could also happen to your code.
The following example uses another adapter, and it also compiles well:

val stream = Stream.generate { 42 }
stream.toList()

But it never completes because Stream.generate() produces an infinite list
of integers. So, be careful and adopt this design pattern wisely.

扎心 2024-09-21 02:37:47

我在 Bharath Thippireddy 设计模式课程中找到了最清晰、最真实的例子。
我们有 WeatherUI 类,它通过邮政编码查找温度,我们有 WeatherFinderImpl 类,它通过城市名称了解温度,因此我们需要创建 WeatherAdapter > 类将获取邮政编码,将其更改为城市名称并调用 WeatherFinder 类以返回正确的温度。
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 类我们可以创建一个将邮政编码转换为城市的方法,或者我们可以创建一个新的 adapter 类:

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);
    }

I found the clearest and kind of real life example in Bharath Thippireddy design pattern course.
We have WeatherUI class which is looking for temperature by zip-code and we have WeatherFinderImpl class which knows temperature by city name so we need to create WeatherAdapter class which will take zipcode, change it to city name and call WeatherFinder class to return proper temperature.
WeatherUi class:

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

and WeatherFinder interface:

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

and WeatherFinderImpl which can find temperature only by city name:

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

having these three in WeatherUI class we can create a method which will translate zipcode into city or we can create a new adapter class:

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;
    }
}

and call it in WeatherUI:

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

test for it is as simple as:

    public static void main(String[] args) {
        WeatherUI ui = new WeatherUI();
        ui.showTemperature(63400);
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文