views:

141

answers:

4

I see myself regularly confronted with the following problem. I have some kind of Marker Interface (for simplicity let's use java.io.Serializable) and several wrappers (Adapter, Decorator, Proxy, ...). But when you wrap a Serializable instance in another instance (which is not serializable) you loose functionality. The same problem occurs with java.util.RandomAccess which can be implemented by List implementations. Is there a nice OOP way to handle it?

+1  A: 

For the likes of RandomAccess there is not much you can do. You can, of course, do an instanceof check and create an instance of the relevant class. The number of classes grows exponentially with markers (although you could use java.lang.reflect.Proxy) and your creation method needs to know about all markers ever.

Serializable isn't so bad. If the indirection class implements Serializable then the whole will be serialisable if the target class is Serializable and not if it isn't.

Tom Hawtin - tackline
Ok, `Serializable` was a *bad* example. Do you know how other languages/concepts (different than java) handle this?
Willi
+1  A: 

There are a few alternatives, although none are very nice

  1. Make the wrapper implement the interface, if it's known at compile time if the wrapped object also implements the interface. A factory method can be used to create the wrapper if it is not known until runtime if the wrapped object will implement the interface. This means you then have separate wrapper classes for the possible combinations of implemented interfaces. (With one interface, you need 2 wrappers, one with and one without. For 2 interfaces, 4 wrappers and so on.)

  2. Expose the wrapped objects from the wrapper, so that clients can walk the chain and test each object in the chain for the interface using instanceof. This breaks encapsulation.

  3. Have a dedicated method to retrieve the interface, implemented by both the wrapper and the wrapped object. E.g. asSomeInterface(). The wrapper delegates to the wrapped object, or creates a proxy around the wrapped object to preserve encapsulation.

  4. Create one wrapper class for each interface - the wrapper is implemented as usual - it implements the interface and delegates to another implementation of that interface. A wrapped object may implement several interfaces so several wrapper instances are combined into one logical instance by using a dynamic proxy to delegate the interface methods implemented by the proxy to the appropriate wrapper instance. It's necessary that the set of interfaces implemented by the proxy do not have any method signatures in common.

Microsoft baked aggregation (Wikipedia) into their Component Object Model (COM). It appears to be unused by the majority yet results in considerable complexity for COM object implementors, since there are rules that every object must adhere to. Wrapped objects are encapsulated by having wrapped objects know about they're wrappers, having to maintain a pointer to the wrapper, which is used when implementing QueryInterface (loosely instanceof) for the exposed public interfaces - the wrapped object returns the interface implemented on the wrapper rather than it's own implementation.

I've not seen a clean, easy to understand/implement and correctly encapsulated solution to this. COM aggregation works and provides complete encapsulation, but it's a cost you pay for every single object you implement, even if it is never used in an aggregate.

mdma
+4  A: 

If the interfaces you're interested in are all marker interfaces, you could have all your wrapper classes implement an interface

public interface Wrapper {
    boolean isWrapperFor(Class<?> iface);
}

whose implementation would look like this:

public boolean isWrapperFor(Class<?> cls) {
    if (wrappedObj instanceof Wrapper) {
        return ((Wrapper)wrappedObj).isWrapperFor(cls);
    }
    return cls.isInstance(wrappedObj);
}

This is how it's done in java.sql.Wrapper. If the interface is not just a marker, but actually has some functionality, you can add a method to unwrap:

<T> T unwrap(java.lang.Class<T> cls)
mkadunc
This works, consider the following natural mistake: Given a marker interface X, and an (wrapped) object o, the inclination for a client is to write the conventional `o instanceof X`, which would silently return false, instead of somehow grabbing the attention of the programmer to the error. I think it is dangerous/counter-intuitive to have **both** marker interfaces, and make meaningless the instanceof test. (cont'd)
Dimitris Andreou
In my answer, I replace instanceof with a method call, similarly to yours, avoiding though combining this with marker interfaces. These can be replaced with any value, like MyEnum.CAPABILITY_ONE, so that the user can't possibly misunderstand and do `x instanceof MyEnum.CAPABILITY_ONE`. /Hope it's useful to compare notes :)
Dimitris Andreou
It's always useful to compare notes, of course :) I actually liked your answer a lot (gave you a +1); but it has a few drawbacks - the obvious one is that you'd need to define all your capabilities in advance (so no way for someone to create a new kind of list without modifying the Capability enum);
mkadunc
Hey, you should make that comment under the other answer then, as I did here! :) Anyway, good point, it wasn't my attention to make it look as rigid - any set of values will do, no need to be values of a particular enum only (though a narrow common supertype helps). I think the real drawback comes from the lack of a type. Both to express what capabilities are required in an argument and have that checked at compile time, and (to a lesser effect, since we talk about *marker* interfaces) to have a natural place to put methods related to that capability.
Dimitris Andreou
+5  A: 

Here is a recent discussion on Guava mailing list - my answer touches upon this, rather fundamental issue.

http://groups.google.com/group/guava-discuss/browse_thread/thread/2d422600e7f87367/1e6c6a7b41c87aac

The gist of it is this: Don't use marker interfaces when you expect your objects to be wrapped. (Well, that's pretty general - how do you know that your object isn't going to be wrapped by a client?)

For example, an ArrayList. It implements RandomAccess, obviously. Then you decide to create a wrapper for List objects. Oops! Now when you wrap, you have to check the wrapped object, and if it is RandomAccess, the wrapper you create should also implement RandomAccess!

This works "fine"...if you only have a single marker interface! But what if the wrapped object can be Serializable? What if it is, say, "Immutable" (assuming you have a type to denote that)? Or synchronous? (With the same assumption).

As I also note in my answer to the mailing list, this design deficiency also manifest itself in the good old java.io package. Say you have a method accepting an InputStream. Will you read directly from it? What if it is a costly stream, and nobody cared to wrap it in a BufferedInputStream for you? Oh, that's easy! You just check stream instanceof BufferedInputStream, and if not, you wrap it yourself! But no. The stream might have buffering somewhere down the chain, but you may get a wrapper of it, that is not an instance of BufferedInputStream. Thus, the information that "this stream is buffered" is lost (and you have to pessimistically waste memory to buffer it again, perhaps).

If you want to do things properly, just model the capabilities as objects. Consider:

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!
}

Edit: It should be noted that I use an enum just for convenience. There could by an interface Capability and an open-ended set of objects implementing it (perhaps multiple enums).

So when you wrap an object of these, you get a Set of capabilities, and you can easily decide which capabilities to retain, which to remove, which to add.

This does, obviously, have its shortcomings, so it is to be used only in cases where you really feel the pain of wrappers hiding capabilities expressed as marker interfaces. For example, say you write a piece of code that takes a List, but it has to be RandomAccess AND Serializable. With the usual approach, this is easy to express:

<T extends List<Integer> & RandomAccess & Serializable> void method(T list) { ... }

But in the approach I describe, all you can do is:

void method(YourType object) {
  Preconditions.checkArgument(object.getCapabilities().contains(SERIALIZABLE));
  Preconditions.checkArgument(object.getCapabilities().contains(RANDOM_ACCESS));
  ...
}

I really wish there were a more satisfying approach than either, but from the outlook, it seems not doable (without, at least, causing a combinatorial type explosion).

Edit: Another shortcoming is that, without an explicit type per capability, we don't have the natural place to put methods that express what this capability offers. This is not too important in this discussion since we talk about marker interfaces, i.e. capabilities that are not expressed through additional methods, but I mention it for completeness.

PS: by the way, if you skim through Guava's collections code, you can really feel the pain that this problem is causing. Yes, some good people are trying to hide it behind nice abstractions, but the underlying issue is painful nonetheless.

Dimitris Andreou
I was hoping for a nice solution but i already expected "no, but..."-answers. I like your's most.
Willi