tags:

views:

87

answers:

2

I need to add some new functionality to an existing interface. There are already a lot of classes in the project implementing it but a few of them wouldn't need the new set of features. My first approach was to just add the new functions to the existing interface and implementing it everywhere, adding do-nothing functions where applicable and the such. But now I wonder if there's a better way to do this.

As an example:

// Everything able to produce a waveform must implement this interface.
interface IWaveformResource
{
    int Collect( Stream _target, int _sampleCount );
}

// A waveform stored in a file
class FileWaveform : IWaveformResource
{
    public int Collect( Stream _target, int _sampleCount )
    {
        // ...
    }
}

// A sine waveform.
class SineWaveform : IWaveformResource
{
    public int Collect( Stream _target, int _sampleCount )
    {
        // ...
    }
}

// Added feature, we want to be able to specify the read position
interface IWaveformResource
{
    int Collect( Stream _target, int _sampleCount );
    int ReadOffset { get; set; }
}

class FileWaveform : IWaveformResource
{
    public int Collect( Stream _target, int _sampleCount )
    {
        // ...
    }

    // Moves the associated file pointer accordingly.
    int ReadOffset { get; set; }
}

class SineWaveform : IWaveformResource
{
    public int Collect( Stream _target, int _sampleCount )
    {
        // ...
    }

    // There's no point in setting or retrieving a sine wave's read position.
    int ReadOffset { get; set; }
}

Another option would be to create a new interface which will only be implemented by positionable waveform streams, eg. FileWaveform :

interface IPositionableWaveform
{
    int ReadOffset { get; set; }
}

// A waveform stored in a file
class FileWaveform : IWaveformResource, IPositionableWaveform
{
    public int Collect( Stream _target, int _sampleCount )
    {
        // ...
    }
}

and use it like this:

private List<IWaveformResource> mResources;
public int ReadOffset
{
    set
    {
        foreach( IWaveformResource resource in mResources )
        {
            if( resource is IPositionableWaveform )
            {
                ((IPositionableWaveform)resource).ReadOffset = value;
            }
        }
    }
}

Note that in this approach I'm not forcing a IPositionableWaveform to be also a IWaveformResource.

I would like to know if there's a more elegant solution than this, thanks in advance.

+2  A: 

No. Unfortunately, changing an existing interface is a breaking change.

This is, by the way, one of the stronger arguments for favoring abstract classes to interfaces - with an abstract base class, you can add members (with a default implementation) without breaking an API.

Introducing a second interface (inheriting from the first) is probably your best option. The main change I'd make, from what you're describing, would be to do:

public interface IWaveformResource : IAudioResource { ... }

This way, it's obvious that a Waveform Resource is also an audio resource.

Reed Copsey
A: 

If not all classes implementing the interface will need the new functionality, there is a good chance it should have never belonged to that interface anyway.

NotDan