它真的是适配器模式吗?
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
(它依赖于 CachedRowSetImpl
是 Serializable
的事实),所以技术上 它是 一个适配器。可以找到为什么需要这样做的原因 here.
至于这是否是个好主意,那是另一回事(结果集可能很大,使用不同的格式序列化更有意义,例如 json、protobuf 等)。
是的,它仍然是 适配器 模式。
一个适配器使两个不兼容的接口能够协同工作。
在java世界里,我们习惯于看到Adapters
(例如MouseAdapter
和MouseListener
)实际上并不是真正意义上的适配器(所以请请注意这一点)。
然而,在您的示例中,适配器似乎有意将 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
接口并且 可能 在其构造函数中包装一个对象 Adaptee
(CachedRowSet
是硬编码的)。有人可能会说 Serializable
是 Target
接口,但这意味着它会适应那些 request()
方法。 RowSetAdaptor
不会覆盖 Serializable
中我能看到的任何内容。
最后,如果真的是 GoF Adapter 模式,我希望 Adapter 不止一个。通常,Target
接口的设计是因为有一些共同的功能是由不同的适配器实现的(只有一种实现的接口意义不大)。
另一个值得注意的点是 Client
不应该知道它正在使用 RowSetAdaptor
; Client
只能看到 Target
类型的对象。如果客户端只是要直接访问实现,则很难证明与客户端一起使用接口是合理的。
也许您可以检查客户端 class 或其他适配器的代码。
我的结论是,根据您提供的代码,它不是 GoF 适配器模式的令人信服的示例。
在
查看更多真实的适配器示例
在一个项目中,我看到了一些前雇员编写的代码。该人将其命名为适配器模式的实现,但我不确定。这是代码:
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
(它依赖于 CachedRowSetImpl
是 Serializable
的事实),所以技术上 它是 一个适配器。可以找到为什么需要这样做的原因 here.
至于这是否是个好主意,那是另一回事(结果集可能很大,使用不同的格式序列化更有意义,例如 json、protobuf 等)。
是的,它仍然是 适配器 模式。
一个适配器使两个不兼容的接口能够协同工作。
在java世界里,我们习惯于看到Adapters
(例如MouseAdapter
和MouseListener
)实际上并不是真正意义上的适配器(所以请请注意这一点)。
然而,在您的示例中,适配器似乎有意将 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
接口并且 可能 在其构造函数中包装一个对象 Adaptee
(CachedRowSet
是硬编码的)。有人可能会说 Serializable
是 Target
接口,但这意味着它会适应那些 request()
方法。 RowSetAdaptor
不会覆盖 Serializable
中我能看到的任何内容。
最后,如果真的是 GoF Adapter 模式,我希望 Adapter 不止一个。通常,Target
接口的设计是因为有一些共同的功能是由不同的适配器实现的(只有一种实现的接口意义不大)。
另一个值得注意的点是 Client
不应该知道它正在使用 RowSetAdaptor
; Client
只能看到 Target
类型的对象。如果客户端只是要直接访问实现,则很难证明与客户端一起使用接口是合理的。
也许您可以检查客户端 class 或其他适配器的代码。
我的结论是,根据您提供的代码,它不是 GoF 适配器模式的令人信服的示例。
在
查看更多真实的适配器示例