Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 是否有合成和标记接口的变通方法?_Java_Oop_Decorator_Composition_Marker Interfaces - Fatal编程技术网

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
,则整个类将是可序列化的,如果不是,则整个类将是不可序列化的。

有几个备选方案,尽管没有一个很好

  • 如果在编译时知道包装对象是否也实现了接口,则让包装器实现接口。如果直到运行时才知道包装对象是否将实现接口,则可以使用工厂方法创建包装。这意味着您可以为实现的接口的可能组合使用单独的包装器类。(对于一个接口,您需要两个包装器,一个带,一个不带。对于2个接口,需要4个包装器,依此类推。)

  • 从包装器中公开包装的对象,以便客户端可以遍历链并使用
    instanceof
    测试链中接口的每个对象。这破坏了封装

  • 有一个专用的方法来检索接口,由包装器和包装对象实现。例如,
    asSomeInterface()
    。包装将委托给包装对象,或围绕包装对象创建一个代理以保留封装

  • 为每个接口创建一个包装器类-包装器照常实现-它实现接口并委托给该接口的另一个实现。一个包装对象可以实现多个接口,因此通过使用动态代理将多个包装实例组合成一个逻辑实例,以将代理实现的接口方法委托给相应的包装实例。代理实现的接口集必须没有任何共同的方法签名

  • Microsoft将()烘焙到其组件对象模型(COM)中。它似乎没有被大多数人使用,但却给COM对象实现者带来了相当大的复杂性,因为每个对象都必须遵守一些规则。包装对象的封装方式是让包装对象知道它们是包装器,必须维护指向包装器的指针,这是在实现QueryInterface时使用的(松散地
    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));
      ...
    }