不受派生类控制的线程安全工厂模式

时间:2018-07-26 17:33:12

标签: c++ c++11 refactoring

说我有一个'Car'类,带有一个复杂的子目录树。每个类都有一个唯一的ID。我有一个管理类,该类跟踪所有汽车的指针,并可以基于id查找它们。

class Car {
public:
    using CarId = size_t;
    Car* getCar(CarId id) const { return sMap[id]; }
    Car(){
        sMutex.lock();
        mId = ++sLastId;
        sMap.insert(pair(mId,this));
        sMutex.unlock();
    }
private:
    static map<CarId,Car*> sMap;
    static CarId sLastId;
    static mutex sMutex;
    CarId mId;
}

我想对此进行重构,以便Car::getCar返回weak_ptr<Car>,因为返回原始指针会带来很多麻烦。

但是,要以一种幼稚的方式从构造函数向sMap中添加指向其自身的弱指针,则需要创建一个共享指针,该指针会在对象超出范围时立即破坏该对象:

sMutex.lock();
mId = ++sLastId;
sMap.insert(
    pair(mId,
         weak_ptr<Car>(shared_ptr(this))
    )
);
sMutex.unlock();

该死,我想我需要使用一些工厂:

class CarFactory {
public:
    shared_ptr<Car> createCar(){
        sMutex.lock();
        mId = ++sLastId;
        auto res = shared_ptr<Car>(this);
        sMap.insert(
            pair(mId,
                 weak_ptr<Car>(res)
            )
        );
        sMutex.unlock();
        return result;
    }

private:
    map<CarId,Car*> sMap;
    CarId sLastId;
    mutex sMutex;
}

很好,但是模块中有很多派生类,很多时候我无法从此处包括这些类,因此我无法为此处定义的每种可能的Car派生类型提供一个工厂方法,这是非常不切实际的。但是,我需要跟踪每辆汽车,我不允许任何方式创建不通过工厂方法的派生汽车。

到目前为止我提出的最好的解决方案:

class CarFactory {
...
template<class DT, class Tp>
shared_ptr<DT> create(Tp params...)
{
    sMutex.lock();
    mId = ++sLastId;
    auto res = shared_ptr<Car>(new DT(forward(params)...) );
    sMap.insert(
        pair(mId,
             weak_ptr<Car>(res)
        )
    );
    sMutex.unlock();
    return result;
}
...
}

允许私有化派生类的构造函数:

class VolksWagenGolf : public VolksWagen {
    friend class CarFactory;
private:
    VolksWagen(bool enableEmissionCheat);
}

因此,我只能按以下方式创建它:

shared_ptr<Car> myCar = CarFactory::create<VolksWagenGolf>(true);

对,但是我仍然不喜欢它,因为:

  1. 这是一个模板,出于各种原因,最好不要使用模板。
  2. 任何时候,不知道这种机制的同事都会来,并像以前一样经常用公共构造函数声明一个新的Car类型。但是现在,当他使用该公共构造函数时,将不会跟踪他的汽车。

因此,如果您有任何想法如何实现这种模式,即必须通过集中簿记机制创建每个派生类,同时使用弱指针进行跟踪,请告诉我。

1 个答案:

答案 0 :(得分:1)

第1步:创建受保护的汽车构造函数。它需要由您的工厂功能拥有的令牌:只有工厂功能才有权创建该令牌。

现在,没有该令牌,没有试图创建派生汽车的代码。

class CarFactory {
private:
  struct CarConstructionPermission {
    explicit CarConstructionPermission(int) {};
  };
  friend class Car;
public:
  template<class D, class...Args>
  std::shared_ptr<D> create(Args&&...args) {
    // blah blah
    auto retval = std::make_shared<D>( CarConstructionPermission(0), std::forward<Args>(args)... );
    // blah
    return retval;
  }
};
class Car {
protected:
  explicit Car(CarConstructionPermission) {}
};

第2步:使模板主体部分隐藏。

您需要的是一个从()到shared_ptr的函数。

private:
  bool create_internal(std::function< std::shared_ptr<Car>() > producer);

public:
  template<class D, class...Args>
  std::shared_ptr<D> create(Args&&...args) {
    std::shared_ptr<D> retval;
    if (create_internal([&]{
      retval = std::make_shared<D>( CarConstructionPermission(0), std::forward<Args>(args)... );
      return retval;
    }) {
      return retval;
    }
    return {}; // failure
  }

现在所有互斥对象都在create_internal内部;我们输入橡皮擦创建汽车并将其传递。

create_internal现在看起来很像您的create,但它不是模板,返回bool,并接受1个参数。 auto result = producer();替换了shared_ptr<Car>行。