searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

java设计模式之适配器模式

2023-10-07 06:58:32
13
0

1、前言

适配器模式是结构型设计模式的一种,用途主要是让两个没有关联的接口能够一起工作,连接这些不相关接口的对象称为适配器。

2、适配器模式

适配器模式是在无法直接连接的场景下,充当两个不兼容接口之间的连接器。此模式的主要目的是将现有接口转换为客户期望的另一个接口。
该模式的结构与装饰模式类似。然而,装饰模式通常是在考虑到扩展的情况下实现的。适配器通常在编写初始代码后实现,用于连接不兼容的接口。实现此模式有两种主要方法,让我们回顾一下它们。

2.1、对象适配器

 

此实现使用组合将逻辑委托给适配器。这是实现接口一致性的一种非常简单的方法:
Object Adapter

在此场景下,Adapter 包含 Adaptee,并将 request() 方法委托给 Adaptee 中的 specificRequest() 方法。






2.2、类适配器

这个版本的适配器模式需要多重继承,如果我们不考虑具有默认方法的接口,这在 Java 中技术上是不可能的。主要思想是通过实现 Target接口 和 继承Adaptee 类来创建 Adapter。但是,当我们将 Target 作为接口时,我们更容易在 Java 中实现这一点,因为 Target 是我们可以控制的部分:

它看起来与对象适配器非常相似,但现在Adapter继承了Adaptee,而不是通过组合的方式包含它。这种方法的好处之一是Adapter可以在两种上下文中使用,既可以作为Target,也可以作为Adaptee。从技术上讲,我们创建了一个双向适配器,在某些情况下会非常方便。

2.3、好处和权衡

类适配器最适合在Target和Adaptee方法之间进行一对一映射。这样,我们就可以使用委托,而无需在适配器中进行额外的实现。但是,如果Target接口更复杂,则此方法可能需要在适配器中进行额外的工作。不过,我们可以通过委托来解决这个问题:

在这里,我们仅将 request() 方法委托给Adaptee。其余部分取自 ConcreteTarget。我们可以使用组合将这些接口方法委托给实现,以避免代码重复。同时,如果我们不需要双向适配器,我们可以使用对象适配器,这样结构就会简单很多:


因此,实现此模式的方法在很大程度上取决于代码库的初始状态,是否可以使用接口,以及是否需要提供适配器在两种上下文中工作的能力。

3、适配器模式例子

Java 有一个关于适配器模式的优秀示例,我们可以在这里回顾一下。Enumeration和Iterator是两个相关的接口,它们是适配器-被适配者关系的很好的例子。

3.1、Enumeration

这两个接口都非常简单,但让我们从Enumeration开始:
public interface Enumeration<E> {
    
    boolean hasMoreElements();

    E nextElement();

    default Iterator<E> asIterator() {
        return new Iterator<>() {
            @Override public boolean hasNext() {
                return hasMoreElements();
            }
            @Override public E next() {
                return nextElement();
            }
        };
    }
    
}

3.2、Iterator

Iterator接口的描述如下:
An iterator over a collection. Iterator takes the place of Enumeration in the Java Collections Framework. Iterators differ from enumerations in two ways:
Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.
Method names have been improved.
从技术上讲,Enumeration具有相同的接口,唯一的区别是方法名称:
public interface Iterator<E> {

    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
    
}

3.3. Adapter实现

正如我们所看到的,这些接口是相似的并且具有相同的目标。默认的 asIterator() 方法是在 Java 9 中添加的,并且包含使用匿名类的 Adapter 模式的实现:
 
default Iterator<E> asIterator() {
    return new Iterator<>() {
        @Override public boolean hasNext() {
            return hasMoreElements();
        }
        @Override public E next() {
            return nextElement();
        }
    };
}
这个例子使用了组合,但在这种情况下并不明确。我们不将 Enumeration 实例传递给 Iterator,因为我们在 Enumeration 的上下文中创建 Iterator。这样,我们就可以直接访问Enumeration方法。这是一种非常强大的技术,它允许隐藏接口的一部分并使用委托给私有方法。前面的类适配器和对象适配器示例需要公共 API 来进行委托。
然而,只有当我们能够控制Adapter和Adaptee时,这种使用匿名类实现适配器模式的方法才是可能的,而这在大多数情况下是不可能的。让我们想象一下在 Java 9 之前我们如何实现相同的功能:
public class IteratorAdapter<E> implements Iterator<E> {
    
    private Enumeration<E> enumeration;
    
    public IteratorAdapter(Enumeration<E> enumeration) {
        this.enumeration = enumeration;
    }
    
    @Override
    public boolean hasNext() {
        return enumeration.hasMoreElements();
    }
    
    @Override
    public E next() {
        return enumeration.nextElement();
    }

}
此示例与上面的对象适配器示例相同。让我们使用类适配器实现相同的功能。我们将在本示例中使用 StringTokenizer,因为它实现了 Enumeration 接口:
public class StringTokenizerIteratorAdapter extends StringTokenizer implements Iterator<String> {

    public StringTokenizerIteratorAdapter(final String str, final String delim, final boolean returnDelims) {
        super(str, delim, returnDelims);
    }

    public StringTokenizerIteratorAdapter(final String str, final String delim) {
        super(str, delim);
    }

    public StringTokenizerIteratorAdapter(final String str) {
        super(str);
    }

    @Override
    public boolean hasNext() {
        return hasMoreTokens();
    }

    @Override
    public String next() {
        return nextToken();
    }
}
我们创建了一个双向适配器,可以用作 Iterator 和 StringTokenizer。 Iterator 方法不直接委托给 Enumerator 中的方法,而是委托给 StringTokenizer 中更具体的方法。

4、结论

在本文中,我们研究了 Java 中的适配器设计模式。这是管理代码库复杂性和使用遗留系统的最重要的模式之一。此外,它还允许重用第三方库,而无需更改应用程序,并且始终能够轻松更改实现。
 
0条评论
0 / 1000
好****p
6文章数
2粉丝数
好****p
6 文章 | 2 粉丝
原创

java设计模式之适配器模式

2023-10-07 06:58:32
13
0

1、前言

适配器模式是结构型设计模式的一种,用途主要是让两个没有关联的接口能够一起工作,连接这些不相关接口的对象称为适配器。

2、适配器模式

适配器模式是在无法直接连接的场景下,充当两个不兼容接口之间的连接器。此模式的主要目的是将现有接口转换为客户期望的另一个接口。
该模式的结构与装饰模式类似。然而,装饰模式通常是在考虑到扩展的情况下实现的。适配器通常在编写初始代码后实现,用于连接不兼容的接口。实现此模式有两种主要方法,让我们回顾一下它们。

2.1、对象适配器

 

此实现使用组合将逻辑委托给适配器。这是实现接口一致性的一种非常简单的方法:
Object Adapter

在此场景下,Adapter 包含 Adaptee,并将 request() 方法委托给 Adaptee 中的 specificRequest() 方法。






2.2、类适配器

这个版本的适配器模式需要多重继承,如果我们不考虑具有默认方法的接口,这在 Java 中技术上是不可能的。主要思想是通过实现 Target接口 和 继承Adaptee 类来创建 Adapter。但是,当我们将 Target 作为接口时,我们更容易在 Java 中实现这一点,因为 Target 是我们可以控制的部分:

它看起来与对象适配器非常相似,但现在Adapter继承了Adaptee,而不是通过组合的方式包含它。这种方法的好处之一是Adapter可以在两种上下文中使用,既可以作为Target,也可以作为Adaptee。从技术上讲,我们创建了一个双向适配器,在某些情况下会非常方便。

2.3、好处和权衡

类适配器最适合在Target和Adaptee方法之间进行一对一映射。这样,我们就可以使用委托,而无需在适配器中进行额外的实现。但是,如果Target接口更复杂,则此方法可能需要在适配器中进行额外的工作。不过,我们可以通过委托来解决这个问题:

在这里,我们仅将 request() 方法委托给Adaptee。其余部分取自 ConcreteTarget。我们可以使用组合将这些接口方法委托给实现,以避免代码重复。同时,如果我们不需要双向适配器,我们可以使用对象适配器,这样结构就会简单很多:


因此,实现此模式的方法在很大程度上取决于代码库的初始状态,是否可以使用接口,以及是否需要提供适配器在两种上下文中工作的能力。

3、适配器模式例子

Java 有一个关于适配器模式的优秀示例,我们可以在这里回顾一下。Enumeration和Iterator是两个相关的接口,它们是适配器-被适配者关系的很好的例子。

3.1、Enumeration

这两个接口都非常简单,但让我们从Enumeration开始:
public interface Enumeration<E> {
    
    boolean hasMoreElements();

    E nextElement();

    default Iterator<E> asIterator() {
        return new Iterator<>() {
            @Override public boolean hasNext() {
                return hasMoreElements();
            }
            @Override public E next() {
                return nextElement();
            }
        };
    }
    
}

3.2、Iterator

Iterator接口的描述如下:
An iterator over a collection. Iterator takes the place of Enumeration in the Java Collections Framework. Iterators differ from enumerations in two ways:
Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.
Method names have been improved.
从技术上讲,Enumeration具有相同的接口,唯一的区别是方法名称:
public interface Iterator<E> {

    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
    
}

3.3. Adapter实现

正如我们所看到的,这些接口是相似的并且具有相同的目标。默认的 asIterator() 方法是在 Java 9 中添加的,并且包含使用匿名类的 Adapter 模式的实现:
 
default Iterator<E> asIterator() {
    return new Iterator<>() {
        @Override public boolean hasNext() {
            return hasMoreElements();
        }
        @Override public E next() {
            return nextElement();
        }
    };
}
这个例子使用了组合,但在这种情况下并不明确。我们不将 Enumeration 实例传递给 Iterator,因为我们在 Enumeration 的上下文中创建 Iterator。这样,我们就可以直接访问Enumeration方法。这是一种非常强大的技术,它允许隐藏接口的一部分并使用委托给私有方法。前面的类适配器和对象适配器示例需要公共 API 来进行委托。
然而,只有当我们能够控制Adapter和Adaptee时,这种使用匿名类实现适配器模式的方法才是可能的,而这在大多数情况下是不可能的。让我们想象一下在 Java 9 之前我们如何实现相同的功能:
public class IteratorAdapter<E> implements Iterator<E> {
    
    private Enumeration<E> enumeration;
    
    public IteratorAdapter(Enumeration<E> enumeration) {
        this.enumeration = enumeration;
    }
    
    @Override
    public boolean hasNext() {
        return enumeration.hasMoreElements();
    }
    
    @Override
    public E next() {
        return enumeration.nextElement();
    }

}
此示例与上面的对象适配器示例相同。让我们使用类适配器实现相同的功能。我们将在本示例中使用 StringTokenizer,因为它实现了 Enumeration 接口:
public class StringTokenizerIteratorAdapter extends StringTokenizer implements Iterator<String> {

    public StringTokenizerIteratorAdapter(final String str, final String delim, final boolean returnDelims) {
        super(str, delim, returnDelims);
    }

    public StringTokenizerIteratorAdapter(final String str, final String delim) {
        super(str, delim);
    }

    public StringTokenizerIteratorAdapter(final String str) {
        super(str);
    }

    @Override
    public boolean hasNext() {
        return hasMoreTokens();
    }

    @Override
    public String next() {
        return nextToken();
    }
}
我们创建了一个双向适配器,可以用作 Iterator 和 StringTokenizer。 Iterator 方法不直接委托给 Enumerator 中的方法,而是委托给 StringTokenizer 中更具体的方法。

4、结论

在本文中,我们研究了 Java 中的适配器设计模式。这是管理代码库复杂性和使用遗留系统的最重要的模式之一。此外,它还允许重用第三方库,而无需更改应用程序,并且始终能够轻松更改实现。
 
文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0