设计模式:继承与封装继承

Posted

技术标签:

【中文标题】设计模式:继承与封装继承【英文标题】:Design pattern: inheritance and encapsulated inheritance 【发布时间】:2016-04-08 23:34:39 【问题描述】:

我在精确表述时遇到问题,所以我在标题中留下了更一般的描述(如果您对问题有更准确的描述,请发表评论,我会编辑标题)。

问题:AudiostreamVideoStream 两个类派生自基类 MediaStream,它对音频和视频流方法有一些共同点,但不打算按原样使用。因此,有两个类 AudioStreamSettingsVideoStreamSettings 派生自 MediaStreamSettings 并传递给其相应流类的构造函数。 MediaStreamSettings 存储音频和视频的通用设置,基类MediaStream 访问此数据。问题是:在流和设置的基类之间设计这种层次关系的最佳方法是什么?

我可以想到一个快速的解决方案,如下所示:

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);
  

总而言之,我对这种方法有两点不满意:

    未在基类中初始化 settings_(我是否应该将其抽象化以使自己平静下来?) 每次我需要访问派生类中的设置时都使用dynamic_pointer_cast(我应该为此创建一个方法包装器吗?)

【问题讨论】:

由于基类提供了settings_ 数据成员,它应该初始化该成员。然后,它的构造函数应该接受一个参数,该参数要么直接提供完整的设置对象,要么用作生产它的工厂。但是,这里的问题是通过复制创建的多个对象之间可能共享设置。最好让MediaStream 不可复制。然后unique_ptr 会更合适。 @Cheersandhth.-Alf 谢谢!起初,我实际上尝试使用unique_ptr,但后来被dynamic_pointer_cast 卡住了。我同意,shared_ptr 在这里不合适。我想将unique_ptr 和@Jarod42 答案描述的抽象方法结合起来会是一个更好的设计 【参考方案1】:

一种解决方案是不在MediaStream中存储数据并添加虚拟方法

virtual const MediaStreamSettings& GetMediaStreamSettings() const = 0;

【讨论】:

谢谢!我喜欢这种方法。另外,我会尝试使用unique_ptr 而不是shared_ptr 来代替settings_。我会暂时保留问题,让人们提供更多选择。【参考方案2】:

由于 MediaStream 不应按原样使用,因此将其设为抽象类应该是可以接受的(并且是可取的)。 因此提供实现(包括类成员)是没有意义的。

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;

【讨论】:

谢谢!我想我需要继承 VideoStreamAudioStream 必须执行一些特定于其类型的操作。但是在这里使用模板是个好主意!我会考虑的

以上是关于设计模式:继承与封装继承的主要内容,如果未能解决你的问题,请参考以下文章

面向对象程序设计-继承与多态

Javascript设计模式与开发实践——面向对象的JavaScript(多态封装继承)

面向对象的程序设计

大话设计模式封装 继承 多态

继承和类设计

设计模式之美——封装,继承,多态的意义