我有一个 ClientInterface 类,它使用策略模式来组织两个分别符合接口 Abase 和 Bbase 的复杂算法。ClientInterface 聚合(通过组合)算法运行的数据,这些数据需要符合 Data 接口。
我试图做的是拥有一个 ClientInterface 类,它能够在运行时选择不同的策略和数据实现。使用从输入文件中读取字符串并在 ClientInterface 构造函数中选择算法和数据实现的工厂方法来选择算法和数据实现。下面的代码模型中没有提供数据和算法的运行时选择。
Data 实现可以基于 map、list、unordered_map 等来测试两种复杂算法(Abase 和 Bbase 实现的 Strategies)的效率如何随着用于 Data 的不同容器而变化。
此外,Data 聚合了不同的元素(ElementBase 实现)。不同的元素实现也会对客户端接口的效率产生重大影响,但元素实际上是不相交的类型,实现来自不同的库。我知道这是一个事实,因为对现有应用程序的分析表明 Element 操作是瓶颈之一。
我知道,如果我对容器使用多态性,就会有“boost/ptr_container”,但数据将存储数十万甚至数百万个元素。在这种情况下,对 Elements 使用多态性会在 ClientInterface 上产生很大的开销,但如果我选择将数据作为 Element 类型的类 Template,我最终将静态定义 ClientInterface 类,这意味着为每个 Element 生成一个客户端应用程序至少键入。
我是否可以假设对于相同数量的 Elements 和在运行时获得的 ClientInterface 配置,对 Element 类型使用多态性引起的开销将对 Data 和 Algorithm 实现的所有配置产生相同的影响?在这种情况下,我可以运行自动化测试,决定 Data 实现和 Element 实现的配置,并定义一个静态配置的 EfficientClientInterface 以在生产代码中使用?
目标:我准备了一个测试工具,我想做的是自动化测试用例系列的测试,因为在运行时更改算法和元素允许我在循环中使用单个应用程序,它是在运行时配置的,它的输出是为了效率而测量的。在实际实现中,我至少要处理 6 个算法接口,3-4 个 Data 实现,我估计至少 3 个 Element 实现。
所以,我的问题是:
1)当重载不适用于返回类型时,元素如何支持不同的操作?如果我将操作设为模板,则需要在编译时定义它,这会与我的自动化测试过程相混淆。
2)如何更好地设计此代码以实现目标?
3)这个问题有更好的整体方法吗?
这是代码模型:
#include <iostream>
#include <memory>
class ElementOpResultFirst
{};
class ElementOpResultSecond
{};
class ElementBase
{
public:
// Overloading does not allow different representation of the solution for the element operation.
virtual ElementOpResultFirst elementOperation() = 0;
//virtual ElementOpResultSecond elementOperation() = 0;
};
class InterestingElement
:
public ElementBase
{
public:
ElementOpResultFirst elementOperation()
{
// Implementation dependant operation code.
return ElementOpResultFirst();
}
//ElementOpResultSecond elementOperation()
//{
//// Implementation dependant operation code.
//return ElementOpResultSecond();
//}
};
class EfficientElement
:
public ElementBase
{
public:
ElementOpResultFirst elementOperation()
{
// Implementation dependant operation code.
return ElementOpResultFirst();
}
//ElementOpResultSecond elementOperation()
//{
//// Implementation dependant operation code.
//return ElementOpResultSecond();
//}
};
class Data
{
public:
virtual void insertElement(const ElementBase&) = 0;
virtual const ElementBase& getElement(int key) = 0;
};
class DataConcreteMap
:
public Data
{
// Map implementation
public:
void insertElement(const ElementBase&)
{
// Insert element into the Map implementation.
}
const ElementBase& getElement(int key)
{
// Get element from the Map implementation.
}
};
class DataConcreteVector
:
public Data
{
// Vector implementation
public:
void insertElement(const ElementBase&)
{
// Insert element into the vector implementation.
}
const ElementBase& getElement(int key)
{
// Get element from the Vector implementation
}
};
class Abase
{
public:
virtual void aFunction() = 0;
};
class Aconcrete
:
public Abase
{
public:
virtual void aFunction()
{
std::cout << "Aconcrete::function() " << std::endl;
}
};
class Bbase
{
public:
virtual void bFunction(Data& data) = 0;
};
class Bconcrete
:
public Bbase
{
public:
virtual void bFunction(Data& data)
{
data.getElement(0);
std::cout << "Bconcrete::function() " << std::endl;
}
};
// Add a static abstract factory for algorithm and data generation.
class ClientInterface
{
std::unique_ptr<Data> data_;
std::unique_ptr<Abase> algorithmA_;
std::unique_ptr<Bbase> algorithmB_;
public:
ClientInterface()
:
// A Factory Method is defined for Data, Abase and Bbase that
// produces the concrete type based on an entry in a text-file.
data_ (std::unique_ptr<Data> (new DataConcreteMap())),
algorithmA_(std::unique_ptr<Abase> (new Aconcrete())),
algorithmB_(std::unique_ptr<Bbase> (new Bconcrete()))
{}
void aFunction()
{
return algorithmA_->aFunction();
}
void bFunction()
{
return algorithmB_->bFunction(*data_);
}
};
// Single client code: both for testing and final version.
int main()
{
ClientInterface cli;
cli.aFunction();
cli.bFunction();
return 0;
};