这很长,但我会尽量清楚。
我有一个界面,我们称之为IFoo
class IFoo
{
public:
virtual void reset(const Bar* bar) = 0;
virtual int calculate(int i) const = 0;
};
基本实现
class FooBasic : public IFoo
{
private:
int _value;
public:
FooBasic(int value) : _value(value) {}
virtual void reset(const Bar* bar) override {}
virtual int calculate(int i) const override { return _value; }
};
我有一个接口的基础装饰器
class FooDecorator : public IFoo
{
protected:
IFoo* _foo;
public:
FooDecorator(IFoo* foo) : _foo(foo) {}
virtual void reset(const Bar* bar) override { _foo->reset(bar); }
virtual int calculate(int i) const override { return _foo->calculate(i); }
};
我有一大堆装饰器的实现,它采用了几乎相同的形式
class FooInt : public FooDecorator
{
private:
int _y;
int _z;
public:
FooInt(IFoo* foo) : FooDecorator(foo), _y(0), _z(0) {}
virtual void reset(const Bar* bar) override
{
_foo->reset(bar);
_y = bar->expensiveIntFunction(15);
_z = bar->expensiveIntFunction(10);
}
virtual int calculate(int i) const override { return _y*_foo->calculate(i) + _z; }
};
class FooIntStar : public FooDecorator
{
private:
const int* _z;
public:
FooIntStar(IFoo* foo) : FooDecorator(foo), _z(nullptr) {}
virtual void reset(const Bar* bar) override
{
_foo->reset(bar);
_z = bar->expensiveIntStarFunction();
}
virtual int calculate(int i) const override { return _foo->calculate(i)*_z[i]; }
};
这里的想法是IFoo
接口的客户端将首先调用传递其reset()
对象的Bar
方法。然后,他们将使用不同的整数参数调用calculate()
方法数百万次。 IFoo
实现将在调用reset()
时从Bar
对象中提取一些信息并使用它来设置一些内部成员,然后他们将使用这些内部成员来装饰底层IFoo
的结果。 1}}对象。他们从bar对象中提取的内容取决于实现,可能是不同的类型/值。
只要有一个客户端使用IFoo
实现,这一切都可以正常工作。我现在正在尝试引入一些线程,我有不同的客户端同时使用IFoo
实现。每个客户端都有自己的Bar
对象,但我希望它们使用IFoo
对象的相同实例(如果可能)。在当前状态下,这不起作用,因为调用reset()
设置成员变量,即reset()
方法不是const
。
我计划创建一个工作空间对象,然后传递它。每个客户端将拥有自己的工作空间实例,以避免多个客户端使用同一对象时发生冲突。在重置调用时,IFoo
实现将在工作空间中设置临时数据,而不使用内部成员。但是,由于不同实现所需的数据类型不同,因此提前设计此工作空间非常棘手。
我想出的一个想法是使这个工作区抽象化并让实现扩展它。所以我正在考虑将界面改为
// Totally undefined object
struct IFooWorkspace
{
virtual ~IFooWorkspace(){}
};
class IFoo
{
public:
virtual IFooWorkspace* createWorkspace() const = 0;
virtual void reset(IFooWorkspace* workspace, const Bar* bar) const = 0;
virtual int calculate(IFooWorkspace* workspace, int i) const = 0;
};
,例如FooInt
实现
class FooInt : public FooDecorator
{
private:
// Struct definition instead of member variables
struct WorkSpace : IFooWorkspace
{
WorkSpace() : _y(0), _z(0), _ws(nullptr) {}
virtual ~WorkSpace(){ delete _ws; }
int _y;
int _z;
IFooWorkspace* _ws;
};
public:
FooInt(IFoo* foo) : FooDecorator(foo) {}
virtual IFooWorkspace* createWorkspace() const override
{
WorkSpace* ws = new WorkSpace;
ws->_ws = _foo->createWorkspace();
return ws;
}
virtual void reset(IFooWorkspace* workspace, const Bar* bar) const override
{
WorkSpace& ws = dynamic_cast<WorkSpace&>(*workspace);
ws._y = bar->expensiveIntFunction(15);
ws._z = bar->expensiveIntFunction(10);
_foo->reset(ws._ws, bar);
}
virtual int calculate(IFooWorkspace* workspace, int i) const override
{
const WorkSpace& ws = dynamic_cast<WorkSpace&>(*workspace);
return ws._y*_foo->calculate(ws._ws, i) + ws._z;
}
};
所以新计划是客户首先调用createWorkspace()
并拥有返回的IFooWorkspace
对象。然后,对reset()
和calculate()
的后续调用应该通过此工作区。
我的问题是,这一切看起来都很复杂,我不确定dynamic_cast
在安全性和性能方面。所以问题是:这里有更简单,更简洁的方法来实现线程安全吗?理想情况下,我想避免为IFoo对象创建新实例。
答案 0 :(得分:2)
理想情况下,我想避免为IFoo对象创建新实例。
但是你创建工作空间的新实例没有问题,所以我可以假设foo对象的构造在某种程度上是痛苦的。
我认为工厂模式适合。
class IFooFactory
{
public:
virtual IFoo create(const Bar* bar) = 0;
};
class IFoo
{
public:
virtual int calculate(int i) const = 0;
};
Foo
然后变得像你的Workspace
对象,即使它们很复杂,工厂也会封装这个工作。 reset
已成为create
方法。
工厂可以安全地在线程之间共享,因为它们没有可变状态。
示例工厂:
class FooIntFactory : public IFooFactory
{
public:
virtual IFoo create(const Bar* bar) const override
{
return new FooInt(bar);
}
};
答案 1 :(得分:0)
如果您需要规划的不同数据类型是相对可互换的(例如int
vs double
),那么您可以尝试模板IFoo
。
实际上,只有在子类之间存在大量共享状态时才引入IFooWorkspace
才有意义 - 即。如果IFooWorkspace
的实际实现有其中的内容。否则为什么要打扰?你没有实现Workspace
和FooInt
之间的脱钩 - 前者只能为后者建立,后者只有没有前者才能实现。您也可以将_y
,_z
作为FooInt
的一部分。但是如果有一个有意义的工作区子类层次结构,那么我认为你的设计是可以的,除了这些工作空间将被调用的地方的问题,但我想调用代码将处理它。