peetonn
peetonn

Reputation: 3052

Design pattern: inheritance and encapsulated inheritance

I got problems formulating it precisely so I left more general description in the title (if you have more precise description of the problem, please comment, I'll edit the title).

The problem: Two classes AudioStream and VideoStream are derived from base class MediaStream which has some common for audio and video stream methods, but is not intended to be used as-is. Consequently, there are two classes AudioStreamSettings and VideoStreamSettings which are derived from MediaStreamSettings and passed to the constructors of their corresponding stream classes. MediaStreamSettings stores settings common for audio and video, and base class MediaStream accesses this data. The question is: what would be the best way to design this hierarchical relationship between base classes of streams and settings?

I can think of a quick solution like the following:

class MediaStream {
public:
  MediaStream(const MediaStreamSettings& settings){
    // do nothing, let derived classes initialize settings_
    // note: I feel guilty for doing this...
  }
  virtual ~MediaStream(){}
protected:
  std::shared_ptr<MediaStreamSettings> settings_;
};

class VideoStream : public MediaStream {
public:
  VideoStream(const VideoStreamSettings& settings):
    MediaStream(settings)
  {
    settings_ = std::make_shared<VideoStreamSettings>(settings);
  }

  void doSomething(){
    int s1 = std::dynamic_pointer_cast<VideoStream, MediaStream>(settings_)->getVideoStreamSetting1();
    ...
  }
};

class AudioStream : public MediaStream {
public:
  AudioStream(const AudioStreamSettings& settings):
    MediaStream(settings)
  {
    settings_ = std::make_shared<AudioStreamSettings>(settings);
  }
}

To summarize I'm not comfortable with two things in this approach:

  1. not initializing settings_ in base class (should I make it abstract to calm myself?)
  2. using dynamic_pointer_cast every time I need to access settings in derived classes (should I make a method wrapper for this?)

Upvotes: 0

Views: 246

Answers (2)

Dafang Cao
Dafang Cao

Reputation: 907

Since MediaStream should not be used as-is, making it an abstract class should be acceptable (and desirable). Thus providing implementation (which includes class members) is pointless.

class IMediaStream {
public:
    virtual ~IMediaStream() {}
    virtual void play() = 0;
    virtual std::shared_ptr<MediaSettings> getSettings() = 0;
private:
    IMediaStream() {}
};

template<Setting>
class MediaStream : public IMediaStream {
public:
    MediaStream(const Setting& settings){
        settings_ = std::make_shared<Setting>(settings);
    }
    virtual ~MediaStream() {}
    virtual void play() override {
        // Implementation here
    }
    virtual std::shared_ptr<MediaSettings> getSettings() override {
        return std::dynamic_pointer_cast<Setting, MediaSettings>();
    }
private:
    std::shared_ptr<Setting> settings_;
}

// Alternatively you can inherit or specialize
// the template to add your implementation
typedef MediaStream<VideoStreamSettings> VideoStream;
typedef MediaStream<AudioStreamSettings> AudioStream;

Upvotes: 1

Jarod42
Jarod42

Reputation: 217750

One solution is to not store data in MediaStream and add a virtual method

virtual const MediaStreamSettings& GetMediaStreamSettings() const = 0;

Upvotes: 1

Related Questions