0

我有一个 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; 
};
4

1 回答 1

1

我试图做的是拥有一个 ClientInterface 类,它能够在运行时选择不同的策略和数据实现。使用从输入文件中读取字符串并在 ClientInterface 构造函数中选择算法和数据实现的工厂方法来选择算法和数据实现。下面的代码模型中没有提供数据和算法的运行时选择。

听起来你在这里有一些基础:要么只生成一组文件进行测试,然后生成正确的不同输入集。或者重构工厂函数,以便文件和字符串的读取是分开的,因此您可以使用代码中的字符串调用工厂函数 [internals]。

我是否可以假设对于相同数量的 Elements 和在运行时获得的 ClientInterface 配置,对 Element 类型使用多态性引起的开销将对 Data 和 Algorithm 实现的所有配置产生相同的影响?在这种情况下,我可以运行自动化测试,决定 Data 实现和 Element 实现的配置,并定义一个静态配置的 EfficientClientInterface 以在生产代码中使用?

我认为你不能做出这样的假设。不同的实现可能对算法有不同的影响——例如,复制一个 100 字节的字符串比复制一个 4 字节的整数要困难得多。所以数据是什么,以及它的组织方式会对你所做的工作产生一些影响。当然,由于您没有详细描述您的 Elements 实际包含的内容,因此这都是猜测。

1)当重载不适用于返回类型时,元素如何支持不同的操作?如果我将操作设为模板,则需要在编译时定义它,这会与我的自动化测试过程相混淆。

创建一个返回 ElementBase 引用或指针的工厂类?这是我对这个问题的直接反应,但同样,你问题中的细节非常模糊,很难确定。

在实际应用中,这是如何工作的?它是通过模板完成的,那么您最好通过模板实现测试代码,并用您认为真实系统可能会做的事情的实际变化来填写它。

2)如何更好地设计此代码以实现目标?

尝试重用生产代码?

3)这个问题有更好的整体方法吗?

还不确定。

于 2013-02-09T14:20:59.243 回答