这是关于给定问题的C ++中特定OO实现的问题:
算法有几种变体,比如两种,两者都有一些通用的部分。这些算法(它们的实现,AlgImpl)接收启动选项。他们还会收到他们一步一步处理的输入数据。可以通过不同的方式提供启动选项(来自文件,来自网络,来自用户输入),以及接收数据的方式有多种(来自文件,来自设备)。对于不同的数据源,有一个通用的API,类似于选项。
该架构应允许将任何可用的AlgImpl与任何可用的选项和数据源一起使用。它还应该允许添加新的AlgImpl以及可能的新类型的选项和数据源,只需对原始代码进行最少的更改或不进行任何更改(仅使用两个AlgImpl'两个选项源类型和两个数据)来源类型)。
以下是我在C ++方面的看法,使用继承,聚合和指针。由于所有AlgImpl都有一些共同的部分,因此很自然地将它们组织在一个基本的抽象类中,所以(省略非必要的数据类型)我们有:
class BaseAlgorithm
{
... //Abstract class with common code
};
class SimpleAlgorithmImpl: public BaseAlgorithm
{
...
};
class OptimalAlgorithmImpl: public BaseAlgorithm
{
...
};
现在,选项和数据源分别具有相同的接口:
class BaseOptionsSource
{
public:
//Interface
virtual void GetOptions(Options& opts) = 0;
};
class FileOptionsSource: public BaseOptionsSource
{
void GetOptions(Options& opts);
...
};
class NetworkOptionsSource: public BaseOptionsSource
{
void GetOptions(Options& opts);
...
};
class BaseDataSource
{
public:
//Interface
virtual void GetDataChunk(DataChunk& chunk) = 0;
};
class FileDataSource: public BaseDataSource
{
void GetDataChunk(DataChunk& chunk);
...
};
class DeviceDataSource: public BaseDataSource
{
void GetDataChunk(DataChunk& chunk);
...
};
然后,选项和数据源成为BaseAlgorithm的成员:
class BaseAlgorithm
{
public:
BaseAlgorithm(BaseDataSource* pDataSrc, BaseOptionsSource* pOptsSrc);
BaseDataSource* _pDataSrc;
BaseOptionsSource* _pOptsSrc;
};
BaseAlgorithm::BaseAlgorithm(BaseDataSource* pDataSrc, BaseOptionsSource* pOptsSrc):
_pDataSrc(pDataSrc), _pOptsSrc(pOptsSrc)
{
}
然后可以按如下方式创建一个算法算法对象:
DeviceDataSource dataSrc;
NetworkOptionsSource optsSrc;
SimpleAlgorithmImpl simpleAlg(&dataSrc, &optsSrc);
对于新的AlgImpl或新类型的源,其类应该实现继承的方法。当然,必须有一个" if / else if /..."代码,它可以在创建AlgImpl对象之前显式选择已使用的AlgImpl和源集。
在这种情况下,也可以重用源对象,前提是AlgImpl对象不管理传递给它的源对象的分配/释放。
您认为这是在C ++中使用它的正确方法吗?或者也许存在一些其他的,更简单的,更灵活的或更少的无问题的"这种可互换的子功能实现的模式?在这种情况下使用指针是不可避免的吗?
答案 0 :(得分:0)
对我来说很合理。如果你能确信数据源和选项源在算法的生命周期中是活的,我认为指针很好。我不认为指针是不可避免的。这里有一些其他选项(其中一些是C ++ 11):
<强>参考强>
最好是const引用。如果你在构造函数中传递它们并且你不需要它们可以为空,那么可能比指针更安全。
BaseAlgorithm(BaseDataSource& dataSrc,
BaseOptionsSource& optsSrc) :
dataSrc_(dataSrc),
optsSrc_(optsSrc) {}
共享智能指针
如果您无法确信数据源或选项源在算法的生命周期内是活动的,请考虑传入std::shared_ptr
或std::weak_ptr
。如果你想让它们保持活着,请使用std::shared_ptr
std::weak_ptr
如果你想知道它们是否还活着。
BaseAlgorithm(std::weak_ptr<BaseDataSource> dataSrc,
std::weak_ptr<BaseOptionsSource> optsSrc) :
dataSrc_(dataSrc),
optsSrc(optsSrc) {}
<强>水槽强>
这取决于如何创建和使用这些数据源,选项源和算法,但如果算法取得数据源和/或选项源的所有权并通过unique_ptr传递,它可能会使所有权变得更简单。
BaseAlgorithm(std::unique_ptr<BaseDataSource> dataSrc,
std::unique_ptr<BaseOptionsSource> optsSrc) :
dataSrc_(std::move(dataSrc)),
optsSrc(std::move(optsSrc)){}
一些小问题:我怀疑out参数是否是获取选项和数据块的最佳方式。不要害怕按值返回,即使对于像数据块这样大的东西,副本也会被任何现代编译器优化掉,它使调用代码更容易读写:
Options opts = optsSrc_->GetOptions(opts);
DataChunk data = dataSrc_->GetDataChunk(data);
您可能错过了它们来简化帖子,但我想知道GetOptions
和/或GetDataChunk
是const
吗?应该打电话给他们更改源?如果它们可以是const
,则意味着算法只需要一个const指针/对源的引用,这可能会使查找错误更容易一些。您谈到重用源对象,如果算法可以对源对象进行更改,这可能并不明智。例如,如果你反复调用GetDataChunk会发生什么,你每次都得到一个不同的块吗?如果您可以在不同的算法中使用相同的数据源,则可能会产生意外后果。
最后,请确保_pOptsSrc和_pDataSrc protected
或private
不公开。