如何使Factory的Header不依赖于它创建的模板化对象?

Lim*_*ans 5 c++ dependencies templates design-patterns factory

我有一个像这样的抽象基类:

class AbstractBaseClass
{}; 
Run Code Online (Sandbox Code Playgroud)

从中衍生出来的模板化具体类:

template<class T>
class ConcreteClass : public AbstractBaseClass
{
public:
   ConcreteClass(T input) : data(input) {}
private:
    T data;
};
Run Code Online (Sandbox Code Playgroud)

我有一个创建AbstractBaseClasses的工厂类

class MyFactory
{
public:
   boost::shared_ptr<AbstractBaseClass> CreateBlah();
   boost::shared_ptr<AbstractBaseClass> CreateFoo();

   template<class T>
   boost::shared_ptr<AbstractBaseClass> Create(T input)
   {
      return boost::shared_ptr<AbstractBaseClass>(new ConcreteClass<T>(input));
   }
};
Run Code Online (Sandbox Code Playgroud)

这个问题是,现在使用MyFactory的一切都必须将整个实现包含在ConcreteClass中.理想情况下,除了MyFactory之外我什么都不想了解ConcreteClass.

有没有办法设计这个来实现这个目标?(除了在MyFactory中为我想要的每种类型手动创建一个新的Create函数,而不是模板化它).

jus*_*tin 5

你需要将工厂实现放入实现文件中(你提到你想要避免它,但除非接口很小,和/或你的项目很小,否则它是较小的邪恶).

当然,还有其他一些方法可以解决这个问题,例如将实现放入基类,制作派生基工厂,或者使用其他一些非常奇怪的模板语法来减少依赖翻译中的实例化.这实际上归结为您项目的便利性和规模.如果您正在处理一个或多个大型项目,那么实例化的完全抽象将长期满足您的需求(假设您需要动态多态性和内存).

您也可以尝试其他方法(例如重载)以通过使用类型安全来减少错误.

简短的回答是,您真的需要将接口/实例化抽象为一个或多个实现文件,以删除标头依赖关系 - 非常常见的习惯用法,以及解决它的许多方法.你可以在工程中进一步划分和使用多态性.

您还可以使用模板转发声明来最小化编译单元的集合.提供:

/** in MyIntermediateFactory.hpp */
class MyIntermediateFactory {
public:
    static template<class T> boost::shared_ptr<T> Create(float);
};

/** in Concrete.hpp */
template<Concrete>
boost::shared_ptr<T> MyIntermediateFactory::Create<Concrete>(float arg) {
    /* … */
}
Run Code Online (Sandbox Code Playgroud)

使用它,您可以选择库中需要的程序/接口部分,然后将它们全部包装在一个真正的工厂中(用于手头的构建).如果您实际尝试请求不可见的创建,则链接器/实例化应该一直失败.

有很多选择,真的 - 你需要弄清楚你的规模有多大,以确定抽象(或不抽象).实例化需要接口,要删除标头依赖项,您必须在某处抽象实例化.


小智 1

我意识到我是在五年后回答这个问题。也许从那时起,这种语言已经发展了一点。如果我正确理解了这个问题,我想提供一些看起来正确的东西,如果没有其他目的,只是为了帮助其他可能发现这个问题并想知道他们能做什么的人。


工厂.hpp

#include "base.hpp"

namespace tvr
{
    namespace test
    {
        class factory
        {
        public:
            typedef base::ptr Ptr;

            enum eSpecial
            {
                eDerived
            };

            template<typename Type>
            Ptr create()
            {
                Ptr result;
                result.reset(new Type());
                return result;
            }

            template<typename Type, typename DataType>
            Ptr create(const DataType& data)
            {
                Ptr result;
                result.reset(new Type(data));
                return result;
            }

            template<typename Type, typename DataType>
            Ptr create(const DataType& data, eSpecial tag)
            {
                Ptr result;
                result.reset(new Type());
                static_cast<Type*>(result.get())->set_item(data);
                return result;
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

基础.hpp

#include <memory>

namespace tvr
{
    namespace test
    {
        class base
        {
        public:
            typedef std::shared_ptr<base> ptr;

        public:
            base() {}
            virtual ~base() {}
            virtual void do_something() = 0;
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

一些_class.hpp

#include <ostream>

namespace tvr
{
    namespace test
    {
        struct some_class
        {
        };
    }
}

std::ostream& operator<<(std::ostream& out, const tvr::test::some_class& item)
{
    out << "This is just some class.";
    return out;
}
Run Code Online (Sandbox Code Playgroud)

模板派生.hpp

#include <iostream>

#include "base.hpp"

namespace tvr
{
    namespace test
    {
        template<typename Type>
        class template_derived : public base
        {
        public:
            template_derived(){}
            virtual ~template_derived(){}
            virtual void do_something()
            {
                std::cout << "Doing something, like printing _item as \"" << _item << "\"." << std::endl;
            }

            void set_item(const Type data)
            {
                _item = data;
            }
        private:
            Type _item;
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

最后是 main.cpp

#include <vector>

#include "base.hpp"
#include "factory.hpp"

namespace tvr
{
    namespace test
    {
        typedef std::vector<tvr::test::base::ptr> ptr_collection;

        struct iterate_collection
        {
            void operator()(const ptr_collection& col)
            {
                for (ptr_collection::const_iterator iter = col.begin();
                    iter != col.end();
                    ++iter)
                {
                    iter->get()->do_something();
                }
            }
        };
    }
}

#include "template_derived.hpp"
#include "some_class.hpp"

namespace tvr
{
    namespace test
    {
        inline int test()
        {
            ptr_collection items;

            tvr::test::factory Factory;

            typedef template_derived<unsigned int> UIntConcrete;
            typedef template_derived<double> DoubleConcrete;
            typedef template_derived<std::string> StringConcrete;
            typedef template_derived<some_class> SomeClassConcrete;

            items.push_back(Factory.create<SomeClassConcrete>(some_class(), tvr::test::factory::eDerived));
            for (unsigned int i = 5; i < 7; ++i)
            {
                items.push_back(Factory.create<UIntConcrete>(i, tvr::test::factory::eDerived));
            }
            items.push_back(Factory.create<DoubleConcrete>(4.5, tvr::test::factory::eDerived));
            items.push_back(Factory.create<StringConcrete>(std::string("Hi there!"), tvr::test::factory::eDerived));

            iterate_collection DoThem;
            DoThem(items);

            return 0;
        }
    }
}

int main(int argc, const char* argv[])
{
    tvr::test::test();
}
Run Code Online (Sandbox Code Playgroud)

输出

Doing something, like printing _item as "This is just some class.".
Doing something, like printing _item as "5".
Doing something, like printing _item as "6". 
Doing something, like printing _item as "4.5".
Doing something, like printing _item as "Hi there!".
Run Code Online (Sandbox Code Playgroud)

这使用模板、函数重载和通过枚举进行标记的组合来帮助创建一个灵活的工厂类,该工厂类不需要太多了解它实例化的各个类,以包含OP询问的模板化具体类。

“eDerived”标签(以枚举的形式)告诉编译器使用工厂的 create 函数的版本,该函数采用类似于 template_衍生类的类,该类具有一个允许我将数据分配给其成员之一的函数。从我在 main.cpp 中订购标头的方式可以看出,工厂对 template_衍生一无所知。调用基类的虚函数(do_something)的函数也不会。我认为这就是OP想要的,但不必在该工厂可能生成的每个类中添加各种创建函数。

我还展示了如何不必为工厂应创建的每个类显式创建函数。工厂的重载创建函数可以创建从基类派生的任何与适当签名匹配的内容。

我没有对这段代码进行广泛的性能分析,但我做了足够多的工作,发现大部分工作都发生在流操作符中。在我的 3.30Ghz 四核机器上编译大约需要 1 秒。您可能需要尝试更健壮的代码,看看它会使编译器陷入困境的程度如何(如果有的话)。

我已经在 VC++ 2015 中测试了这段代码,尽管它可能很容易在其他编译器中工作。如果您想复制此内容,则需要添加自己的保护标头。无论如何,我希望这有用。