Java 是否有合成和标记接口的变通方法?
我发现自己经常遇到以下问题。我有一些标记接口(为了简单起见,让我们使用Java 是否有合成和标记接口的变通方法?,java,oop,decorator,composition,marker-interfaces,Java,Oop,Decorator,Composition,Marker Interfaces,我发现自己经常遇到以下问题。我有一些标记接口(为了简单起见,让我们使用java.io.Serializable)和一些包装器(适配器、装饰器、代理等等)。但是,当您将一个可序列化实例包装到另一个实例(不可序列化)中时,就会失去功能。同样的问题也发生在java.util.RandomAccess上,它可以通过列表实现来实现。有没有一种很好的面向对象的方法来处理它呢?对于像RandomAccess这样的人来说,你无能为力。当然,您可以执行instanceof检查并创建相关类的实例。随着标记的增加,类
java.io.Serializable
)和一些包装器(适配器、装饰器、代理等等)。但是,当您将一个可序列化实例包装到另一个实例(不可序列化)中时,就会失去功能。同样的问题也发生在java.util.RandomAccess上,它可以通过列表实现来实现。有没有一种很好的面向对象的方法来处理它呢?对于像RandomAccess
这样的人来说,你无能为力。当然,您可以执行instanceof
检查并创建相关类的实例。随着标记的增加,类的数量呈指数增长(尽管您可以使用java.lang.reflect.Proxy
),您的创建方法需要了解所有标记
Serializable
还不错。如果间接类实现了Serializable
,那么如果目标类是Serializable
,则整个类将是可序列化的,如果不是,则整个类将是不可序列化的。有几个备选方案,尽管没有一个很好
instanceof
测试链中接口的每个对象。这破坏了封装asSomeInterface()
。包装将委托给包装对象,或围绕包装对象创建一个代理以保留封装instanceof
)对于公开的公共接口-包装对象返回包装器上实现的接口,而不是它自己的实现
我还没有看到一个干净、易于理解/实现且正确封装的解决方案。COM聚合可以工作并提供完整的封装,但这是您为实现的每个对象支付的成本,即使它从未在聚合中使用。如果您感兴趣的接口都是标记接口,您可以让所有包装器类实现一个接口
public interface Wrapper {
boolean isWrapperFor(Class<?> iface);
}
这是最近关于番石榴邮件列表的一次讨论——我的回答涉及到这个相当基本的问题 其要点如下: 当您希望包装对象时,不要使用标记接口。(好吧,这很一般——你怎么知道你的对象不会被客户机包装?) 例如,
ArrayList
。显然,它实现了随机访问。然后决定为List
对象创建包装器。哎呀!现在,当您包装时,您必须检查包装的对象,如果它是RandomAccess,那么您创建的包装也应该实现RandomAccess
如果你只有一个标记接口的话,这就“很好”了!但是如果包装的对象可以序列化呢?如果它是,比如说,“不可变的”(假设您有一个类型来表示它)?还是同步的?(假设相同)
正如我在对邮件列表的回答中所指出的,这种设计缺陷也表现在良好的java.io
包中。假设您有一个接受InputStream
的方法。你会直接读吗?如果它是一个昂贵的流,并且没有人愿意为您将其包装在一个BufferedInputStream
中,该怎么办?哦,那很容易!您只需检查BufferedInputStream的流实例,如果不检查,您可以自己包装它!但是没有。流可能在链的某个地方有缓冲,但您可能会得到它的包装,它不是BufferedInputStream的实例。因此,“此流已缓冲”的信息丢失(您可能不得不悲观地浪费内存来再次缓冲它)
如果您想正确地执行操作,只需将功能建模为对象即可。考虑:
interface YourType {
Set<Capability> myCapabilities();
}
enum Capability {
SERIALIAZABLE,
SYNCHRONOUS,
IMMUTABLE,
BUFFERED //whatever - hey, this is just an example,
//don't throw everything in of course!
}
但在我描述的方法中,您所能做的就是:
void method(YourType object) {
Preconditions.checkArgument(object.getCapabilities().contains(SERIALIZABLE));
Preconditions.checkArgument(object.getCapabilities().contains(RANDOM_ACCESS));
...
}
我真的希望有一种比这两种方法都更令人满意的方法,但从前景来看,这似乎是不可行的(至少在不引起组合类型爆炸的情况下)
Edit:另一个缺点是,如果没有每个功能的显式类型,我们就没有放置表示此功能所提供内容的方法的自然位置。这在本次讨论中并不太重要,因为我们讨论的是标记接口,即功能t
interface YourType {
Set<Capability> myCapabilities();
}
enum Capability {
SERIALIAZABLE,
SYNCHRONOUS,
IMMUTABLE,
BUFFERED //whatever - hey, this is just an example,
//don't throw everything in of course!
}
<T extends List<Integer> & RandomAccess & Serializable> void method(T list) { ... }
void method(YourType object) {
Preconditions.checkArgument(object.getCapabilities().contains(SERIALIZABLE));
Preconditions.checkArgument(object.getCapabilities().contains(RANDOM_ACCESS));
...
}