0

在开发我当前的代码库时,我正在尽我最大的努力遵守一些严格的设计模式,因为我希望我会在很长一段时间内致力于它,并且我希望它尽可能灵活和干净。然而,在尝试结合所有这些设计模式来解决我当前面临的问题时,我遇到了一些问题,我希望有人能给我一些建议。

我正在开发一些基本的自制 GUI 小部件,以提供一些通用的单击/拖动/复制行为。从表面上看,用户单击小部件,然后将其拖动到某处。将它拖得足够远后,小部件将“出现”克隆自身并让用户拖动这个新副本。

原型设计模式显然进入了尝试,使这种复制功能可推广到许多类型的小部件。对于大多数对象,故事到此结束。Prototype 对象实际上是用户最终拖动的复制版本的相同副本。

但是,我要复制的对象之一附加了一些相当大的资源,因此在用户真正决定单击并拖动并随后复制该特定对象之前,我不想加载它们。进入延迟初始化。但这给我带来了一个难题。我不能只让原型对象克隆自己,因为它需要在用户复制虚拟原型版本之前加载大量资源。我也不热衷于将一些逻辑放入对象中,这些逻辑在被克隆/复制时会检查正在发生的事情并决定它是否应该加载到这些资源中。相反,一个乐于助人的人建议我创建一种 shell 对象,当它被克隆时,它会返回这个更派生的版本,其中包含允许我使用 RAII 和延迟初始化的资源。

但是我在实现这一点时遇到了一些麻烦,我开始怀疑我是否可以按照我认为应该做的方式去做。现在它看起来像这样:

class widgetSpawner : public widget {
public:
    widgetSpawner();
    ~widgetSpawner();

private:
    widget* mPrototypeWidget;       // Blueprint with which to spawn new elements
};

class widgetAudioShell : public widget {
public:

    widgetAudioShell(std::string pathToAudioFile);
    widgetAudioShell( const widgetAudioShell& other );
    ~widgetAudioShell();

    virtual widgetAudio* clone() const { return new widgetAudio(*this); };  

private:
    std::string mPathToAudioFile;
};


class widgetAudio : public widgetAudioShell {
public:

    widgetAudio(AudioEngineAudioTrack &aTrack);
    widgetAudio( const widgetAudio& other );
    widgetAudio( const widgetAudioShell& other );

    ~widgetAudio();

    virtual widgetAudio* clone() const { return new widgetAudio(*this); };  

private:

    AudioEngineAudioTrack &mATrack;
};

显然,这是行不通的,因为 shell 不知道使用它来派生新类的对象。所以它不能通过克隆函数返回它。但是,如果我保持这两个继承方式独立(因为它们都从小部件继承),那么编译器会抱怨缺乏我认为有意义的协方差?或者也许是因为我再次遇到了在另一个之前正确定义一个的麻烦。

本质上,widgetAudioShell 需要了解 widgetAudio,以便它可以返回一个“新”副本。widgetAudio 需要了解 widgetAudioShell 以便在创建/克隆时读取它的成员函数。

如果我没记错的话,这种循环依赖的存在是因为我喜欢使用引用而不是指针,如果我必须使用指针,那么突然间我所有的其他小部件都需要做同样的事情,我会觉得这很糟糕。我希望有过类似经历的人可以提供一些智慧?

4

0 回答 0