它真的是适配器模式吗?

Is it really an Adaptor pattern?

在一个项目中,我看到了一些前雇员编写的代码。该人将其命名为适配器模式的实现,但我不确定。这是代码:

public class RowSetAdaptor implements java.io.Serializable {
    private javax.sql.rowset.CachedRowSet cachedRowSet;

    public RowSetAdaptor() throw SQLException {
        cachedRowSet = new com.sun.rowset.CachedRowSetImpl();
    }

    public void populate(ResultSet resultSet) throw SQLException {
        cachedRowSet.populate(resultSet);
    }

    public boolean next() throw SQLException {
        cachedRowSet.next();
    }
    
    .... // different methods all using cachedRowSet
} 

我认为 class RowSetAdaptor 限制访问 CachedRowSet 接口,因为并非 CachedRowSet 接口的所有方法都在 RowSetAdaptor 中可用class。它真的是适配器模式吗?如果不是,那么这里使用的是哪种设计模式?

更新 [2015 年 2 月 24 日]

感谢@JB Nizet、@Fuhrmanator、@Günther Franke、@vikingsteve 和@Giovanni Botta 的回答。

如果我进行以下修改使其成为 Adapter 模式会怎样?

public interface RowSetI {
    public boolean next() throws SQLException;
    ...
}

public class CachedRowSetAdapter implements RowSetI {
    private javax.sql.rowset.CachedRowSet cachedRowSet;

    public CachedRowSetAdapter() throw SQLException {
        cachedRowSet = new com.sun.rowset.CachedRowSetImpl();
    }

    public void populate(ResultSet resultSet) throw SQLException {
        cachedRowSet.populate(resultSet);
    }

    public boolean next() throw SQLException {
        cachedRowSet.next();
    }
    ...
}

public class JdbcRowSetAdapter implements RowSetI {
    private javax.sql.rowset.JdbcRowSet jdbcRowSet;

    public JdbcRowSetAdapter() throw SQLException {
        jdbcRowSet = new com.sun.rowset.JdbcRowSetImpl();
    }

    public void populate(ResultSet resultSet) throw SQLException {
        jdbcRowSet.populate(resultSet);
    }

    public boolean next() throw SQLException {
        jdbcRowSet.next();
    }
    ...
}

TIA

好吧,class 确实实现了 Serializable(它依赖于 CachedRowSetImplSerializable 的事实),所以技术上 它是 一个适配器。可以找到为什么需要这样做的原因 here.

至于这是否是个好主意,那是另一回事(结果集可能很大,使用不同的格式序列化更有意义,例如 json、protobuf 等)。

是的,它仍然是 适配器 模式。

一个适配器使两个不兼容的接口能够协同工作。

在java世界里,我们习惯于看到Adapters(例如MouseAdapterMouseListener)实际上并不是真正意义上的适配器(所以请请注意这一点)。

然而,在您的示例中,适配器似乎有意将 CachedRowSet 接口的大小和复杂性降低到一个名为 RowSetAdapter 的新接口中,该新接口也是 Serializable .

该示例使用组合而不是继承这一事实并不排除它是 Adapter 模式,我认为它实际上是一个很好的 Adapter 示例,尽管您也可以争论它也代表 Proxy 模式。

如果 class RowSetAdaptor 以任何方式适配 CachedRowSet 接口,那么 RowSetAdaptor 可以被视为 Adapter 设计模式(对象适配器)。

但是在您的示例列表中我看不到任何改编 - 操作只是转发到 cachedRowSet 对象 - 这样客户端就可以访问 CachedRowSet直接接口。

RowSetAdaptor 引入了一个额外的间接级别,它 使设计复杂化并降低性能。 仅当客户端不能或不应直接访问 CachedRowSet 接口时才应使用它。

"A design pattern should only be applied when the flexibility it affords is actually needed."
[GoF book, page 31]

注意: Adapter 设计模式(对象适配器)建议客户端引用接口(Target)以使它们独立于具体实现 class(适配器)。
在您的示例中,客户端引用(并依赖于)具体 RowSetAdaptor class。

有关进一步讨论,请参阅位于 http://w3sdesign.com 的 GoF 设计模式内存/适配器设计模式。

查看某物是否是模式实现的最佳方法是将 GoF 定义中的 roles/methods 映射到实现。

GoF 适配器有两个变体(class 和对象适配器),它们具有不同的结构。 Class 适配器使用多重继承:

您有一个 class RowSetAdaptor,这将使它成为此图中 Adapter class 的候选对象。然而,RowSetAdaptor只实现了Serializable,所以我认为它不可能是这种形式的适配器模式。

第二种变体是对象适配器:

同样,RowSetAdaptor 将是此图中的 Adapter class。看起来 javax.sql.rowset.CachedRowSet 确实是适应者,因为它在其某些方法中使用了 class。然而,最纯粹的 GoF Adapter 必须实现一些 Target 接口并且 可能 在其构造函数中包装一个对象 AdapteeCachedRowSet 是硬编码的)。有人可能会说 SerializableTarget 接口,但这意味着它会适应那些 request() 方法。 RowSetAdaptor 不会覆盖 Serializable 中我能看到的任何内容。

最后,如果真的是 GoF Adapter 模式,我希望 Adapter 不止一个。通常,Target接口的设计是因为有一些共同的功能是由不同的适配器实现的(只有一种实现的接口意义不大)。

另一个值得注意的点是 Client 不应该知道它正在使用 RowSetAdaptorClient 只能看到 Target 类型的对象。如果客户端只是要直接访问实现,则很难证明与客户端一起使用接口是合理的。

也许您可以检查客户端 class 或其他适配器的代码。

我的结论是,根据您提供的代码,它不是 GoF 适配器模式的令人信服的示例。

查看更多真实的适配器示例