C++ 共享对象工厂的设计模式

C++ 共享对象工厂的设计模式,c++,design-patterns,C++,Design Patterns,我有一个系统,其中一个单例对象负责在其他非单例类之间创建和共享对象。例如: // One instance is shared between multiple instances // of Widget that have the same ID. class Lease {}; // For each unique Widget instance that has the same // value for `m_id`, the Controller will either // cr

我有一个系统,其中一个单例对象负责在其他非单例类之间创建和共享对象。例如:

// One instance is shared between multiple instances
// of Widget that have the same ID.
class Lease {};

// For each unique Widget instance that has the same
// value for `m_id`, the Controller will either
// create a new Lease (if one has not already been
// created) or it will return an existing one.
class Widget
{
private:
    unsigned m_id{};
    std::shared_ptr<Lease> m_lease;
};

// Singleton object that is used by all instances of
// Widget to obtain shared instances to Lease
class Controller
{
public:
    std::shared_ptr<Lease> CreateLease(unsigned id);

private:
    std::map<unsigned, std::weak_ptr<Lease>> m_leases;
};
//一个实例在多个实例之间共享
//具有相同ID的小部件的。
类租约{};
//对于具有相同
//值,则控制器将
//创建新租约(如果尚未创建)
//或者它将返回一个现有的。
类小部件
{
私人:
无符号m_id{};
std::共享ptr m_租约;
};
//的所有实例所使用的单例对象
//获取要租赁的共享实例的小部件
类控制器
{
公众:
std::shared_ptr CreateLease(未签名的id);
私人:
标准::地图m_租赁;
};
这就是我目前的要点。控制器所做的是,如果给定ID的映射中不存在新租约,则创建新租约。如果确实存在,则返回现有的共享对象

我有一个计时器,可以定期检查
m\u租约
映射中的“过期”弱指针,如果是,将删除它们。这意味着,在某个时候,多个小部件被销毁,随后也释放了它们的租约

既然您有了一些背景知识,我想我这里有一个比普通工厂更聪明的工厂(控制器):它跟踪实例的创建,并且只根据某些业务规则创建新的实例(特别是,如果在映射中找到匹配的ID)。我不确定这是否是我尝试做的最好的设计(也就是说:在Widget的唯一实例之间共享租约实例的某种机制)。此解决方案有几点我不喜欢:

  • 小部件实例需要一个singleton作为获取租约的接触点
  • 计时器用于管理地图:我想不出任何基于事件的方法来管理从地图中删除过期租约
  • 第二步:因为我在地图中使用计时器来管理租约到期,所以地图中始终有一个小窗口,租约在到期后仍保留在地图中,这意味着
    CreateLease()
    在返回之前,还必须检查
    弱ptr
    是否过期,如果找到了现有的ID映射

  • 我觉得这个逻辑不对。我需要更多的眼光来看待这个想法,最好是推荐更好的模式来解决这个问题。

    我会在析构函数中广播租约-观察者模式

    这将允许控制者注册一名观察员,以便删除租约并将其删除。这消除了对计时器的需求,引入了事件的概念;满足2和3

    至于控制器是一个单体,根据您发布的内容,它可以嵌套到小部件中并成为私有的(或者移动到小部件的cpp文件中)。虽然它最终仍然是一个单例,但由于它的范围有限,它的控制性更强,可以很容易地在以后被替换

    如何将其组合在一起的示例:

    class Lease {
    public:
        struct listener {
            virtual void leaseGone(int id) = 0;
        }
    
        void addListener(listener* l) {
            listeners.push_back(l);
        }
    
        ~Lease() {
            for (auto x&: listeners)
                x->leaseGone(myId);
        }
     }
    
    
    class Controller : Lease::listener {
        void leaseGone(int id) {
            m_leases.erase(id);
        }
    }
    

    我喜欢这个想法,但可能有未指定的行为:我的编辑显示了它如何不会击中这个UB,因为它根本不测试弱点有效性-它知道它无效;所以不需要测试我刚才看到了你的例子。链接SO仅因为我的租约地图使用弱ptr才相关。在
    租约
    的析构函数中,在检查
    弱ptr::expired()
    之前,共享ptr的
    使用计数()
    必须为0,这是我计划在
    removeLeaseFromMap()
    实现内部执行的额外检查。但是仔细想想,这可能是不必要的,因为调用了Lease析构函数这一事实意味着ref count为0,否则它不会被析构函数。确切地说,removeLeaseFromMap()不需要做任何检查,因为它已经消失了。完美。回答得很好。感谢您提供的示例代码。为了总结讨论,我建议您更新代码示例,以显示
    removeLeaseFromMap
    的实现,以显示仅
    m_租赁。擦除(id),并且不检查
    弱\u ptr::expired()
    。这是为了你的答案的未来观众的利益。再次感谢!!“我有一个计时器,可以定期检查m_租约映射中的“过期”弱指针,如果是,将删除它们。这意味着在某个时刻,多个小部件被销毁”:当您在
    映射中存储
    弱_ptr
    时,您的映射清理(删除
    null ptr
    弱_ptr
    )不会释放小部件。这只是对
    映射的优化。