Suppose that I have an input stream interface, which defines a method for reading data. I also have a seekable interface which defines a method for seeking. A natural way of defining a input file is then to implement both input stream and seekable.

I want to construct a data decoder from the input stream interface so I can read data from a file or from another stream. The problem is that I also want to implement seek functionality to the data decoder, since I want to be able to step individual records not raw bytes. This is not possible if I only provide an input stream, which does not have the bytewise seek method.

Should I skip the seekable interface and add the seek method to input stream instead and force all streams to at least leave it as a nop.

EDIT: The decoder does not need to seek if the client does not request a seek operation. Also the stream is associated with the decoder and cannot be changed after initialization

  • 1
    think of client code that is expected to use the API you are designing (or better yet, create unit tests to see yourself how it will work). How would it handle nops for streams that don't support seek? If you find it easy and convenient, go for nop option; otherwise, drop this idea – gnat Dec 13 '12 at 18:38
  • @gnat letting the method return false if seeking is not supported can work very well. I can just probe for seek support using foo.seek(0,SEEK_CURRENT) ie seek 0 bytes from current position. – user877329 Dec 13 '12 at 18:46
  • "can work very well" - you better double check assumptions like that. I for one have seen it work pretty well in one kind context and being major headache in another. I found it really safer to learn about how my API will be used with unit tests, this helps to lower uncertainty in cases like this. – gnat Dec 13 '12 at 18:50

The way the .NET guys solved this problem was to introduce a property IsSeekable that allow to know if a Stream can be seeked. If not and you call Seek() anyway, it throws an exception. It's not very polymorphic but it's simple and works reasonably well in practice. It allows Stream to be passed around freely and each method can optimise for seeking if it want. The downside is that any method that want to use Seek() need to check the property before.

A more object oriented approach would be to create the interface SeekableInputStream that derives from both Seekable and InputStream. That way a class that needs to seek like your data adapter can require that specific interface and be guaranteed that seek will work. Better yet, the compiler will warn you if you try to send an invalid InputStream to such method. Other method can accept any class that implements InputStream, including those that implement SeekableInputStream. The downside to this method is that if you pass your SeekableInputStream around a lot then once its downcasted to InputStream the information about its seekability is lost and you need to cast it to SeekableInputStream back again if you really to optimise when you can seek.

  • An advantage of the IsSeekable approach is that it reduces the need for typecasting in cases where a method can benefit from certain abilities without requiring them. It also makes it possible for wrapper objects to expose abilities that the wrapped objects may or may not support as abilities which the wrapper may or may not support, without having to define separate wrappers types for objects that have the abilities in question and for objects which do not. Having a type for things which are known to be seekable is helpful, but having methods exist... – supercat Oct 8 '14 at 16:41
  • ...even if they're not known to be supported makes it possible to use abilities in cases where they exist, even if they may not always exist. For example, if a collection interface had a CanGetCountQuickly property, it might initially return false for a lazily-generated collection, but change to true after the collection had been completely read through once. If the ability to count were an attribute of the collection type, such enhancing of abilities would not be possible. – supercat Oct 8 '14 at 16:46

I think this kind of problem can be easily fixed by decorating the object. In your case, you're going to have your streams, using your stream interface, and a seeker stream decorator, which adds the seeking functionality you require. You can then decorate your streams with the seeking functionality whenever needed.

  • It seems somewhat overkill – user877329 Dec 13 '12 at 18:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.