使用条件编译实现产品的工厂模式

时间:2015-08-03 21:31:35

标签: c++ oop design-patterns factory-pattern c++98

我想以一种允许我编译代码而不引入类型依赖的方式实现工厂(或其他模式)。

enum CarType
{
 BMW,
 PORSCHE,
 MERC
};

class CarFactory
{
  public:
 static Car* create(CarType type)
 {
  switch(type)
  {
    case BMW : return new BMWCar();
    case PORSCHE : return new PorscheCar();
    default : return new MercCar();
  }
 }
};

当我编译CarFactory时,我需要将BMWCar,PorscheCar和MercCar作为我的编译/链接单元的一部分。

我的代码库设置方式,我们可能只想运送BMWCar,或者两个或全部三个。所以,我无法使create()依赖于类型。

我如何调整工厂模式?另外,我想避免使用ifdef,因为这只是我问题的一个例子。真正的代码库是巨大的,并不是ifdef代码的实用解决方案。

更新: 另外,我不允许使用:

  • 模板
  • 必须符合c ++ 98标准
  • 无法使用boost

这主要是由于客户构建工具链限制。我无法改变这些。

6 个答案:

答案 0 :(得分:2)

我通常会做类似的事情:

class CarFactory
{
public:
     static void RegisterCar(CarType t, std::function<Car*()> f)
     {
          getMap().emplace(t, f);
     }
     static Car* create(CarType type)
     {
          return getMap().at(type)();
     }
private:
     static std::unorderd_map<CarType, std::function<Car*()> >& getMap()
     {
         static std::unorderd_map<CarType, std::function<Car*()> > m;
         return m;
     }
};

在每个班级实施中:

 class BMWCar : public Car
 { 
     struct Init
     {
         Init() 
         {
             CarFactory::RegisterCar(BMW, [](){return new BMWCar(); });
         }
     };
     static Init initializeBmwCar;
     /** .. */ 
 };

 /*** BMWCar.cpp ***/
 BMWCar::Init BMWCar::initializeBmwCar;

这是有效的,因为每个类型在静态初始化期间使用static Init对象初始化自己的工厂。

这段代码的巨大痛苦是为了避免初始化顺序惨败:一个简单的实现只会在CarFactory中使用静态映射。遗憾的是,无法保证BMWCar::initializeBmwCar构造函数在<{1}}中地图之后运行。有时一些编译器可能会工作,有时它可能会崩溃。 因此,我们的想法是使用静态函数(CarFactory)和静态变量(getMap),保证在第一次调用m时进行初始化。

我知道getMap / clang使用此模式注册优化传递。

另一个更复杂但更灵活的解决方案是设计一个插件系统,其中每个DLL实现一个llvm类型并导出一个car函数。

然后,您可以在初始化期间通过动态加载库并调用CreateCar / CreateCar来收集所有这些GetProcAddress

在Windows上实现这两个解决方案可能都很棘手,因为(除非dlsym是抽象的)基础Car实现需要进入自己的库,每个插件dll需要链接到该库

答案 1 :(得分:1)

这是一个完整的例子,C ++ 98风格。我假设在编译时不知道可能的汽车类型列表,所以我将枚举更改为字符串。

cartype.hh:

#include <map>
#include <string>
#include <vector>

struct Car {
  virtual std::string type() = 0;
  virtual ~Car() {}
};

// Factory
class CarFactory
{
  typedef std::map<std::string, Car *(*)()> Registry;
  static Registry &registry();
public:
  static std::vector<std::string> getRegisteredTypes();
  static void registerCarType(std::string type, Car *(*creator)());
  static Car* create(std::string type);
};

cartype.cc:

#include <map>
#include <string>
#include <vector>

#include "cartype.hh"

// Factory
CarFactory::Registry &CarFactory::registry()
{
  static std::map<std::string, Car *(*)()> r;
  return r;
}

std::vector<std::string> CarFactory::getRegisteredTypes()
{
  static const Registry &reg = registry();
  std::vector<std::string> types;
  types.reserve(reg.size());
  Registry::const_iterator end = reg.end();
  for(Registry::const_iterator it = reg.begin(); it != end; ++it)
    types.push_back(it->first);
  return types;
}

void CarFactory::registerCarType(std::string type, Car *(*creator)())
{
  registry()[type] = creator;
}

Car* CarFactory::create(std::string type)
{
  static const Registry &reg = registry();
  Registry::const_iterator result = reg.find(type);
  if(result != reg.end())
    return result->second();
  throw "Unregistered car type";
}

bmw.cc(porsche.cc和merc.cc相似但未显示):

#include <string>

#include "cartype.hh"

// BMW
class BMWCar : public Car
{
  static const bool registered;
  static Car *create() { return new BMWCar; }
public:
  virtual std::string type() { return "BMW"; }
};
const bool BMWCar::registered =
  (CarFactory::registerCarType("BMW", BMWCar::create),
   true);

check.cc:

#include <iostream>
#include <memory>
#include <ostream>
#include <string>
#include <vector>

#include "cartype.hh"

int main()
{
  // all car types should be registered when we enter main
  std::vector<std::string> types = CarFactory::getRegisteredTypes();
  for(std::size_t i = 0; i < types.size(); ++i)
  {
    std::auto_ptr<Car> car(CarFactory::create(types[i]));
    std::cout << "Wanted: " << types[i] << ", Got: " << car->type() << std::endl;
  }
}

编译并运行:

-*- mode: compilation; default-directory: "/tmp/" -*-
Compilation started at Tue Aug  4 01:24:51

set -ex; g++ -std=c++98 -g -O3 -Wall check.cc cartype.cc bmw.cc porsche.cc -o check; ./check
+ g++ -std=c++98 -g -O3 -Wall check.cc cartype.cc bmw.cc porsche.cc -o check
+ ./check
Wanted: BMW, Got: BMW
Wanted: PORSCHE, Got: Porsche

Compilation finished at Tue Aug  4 01:24:54

注1:您不能假设所有类都在main启动之前注册,即在您可能正在进行的其他静态初始化中。

注2:如果Car实现位于自己的共享对象库(.so)中,那么(实际上大多数解决方案)可能无法自行运行。除非二进制文件需要来自该.so的符号,否则链接器根本不会将该依赖项放在完整的二进制文件中。因此,您需要特殊的链接器选项来强制链接器执行此操作。对于使--as-needed成为默认值的发行版来说,这主要是一个问题(我正在看着你,Ubuntu)。使用--no-as-needed-Wl,--no-as-needed将其关闭,至少对于包含汽车实施的库而言。

静态库(.a)存在类似问题。 .a文件只是几个.o文件的集合,链接器将只包含.a文件中包含以前未定义的符号的那些.o文件。可以强制链接器考虑使用-u symbol_name未定义的符号。但那是受损的符号名称,因此很难猜测。在我的示例中,为此目的起作用的一个符号是_ZN6BMWCar10registeredE,a.k.a BMW::registered以未拼写的形式出现。但是使用C链接定义函数可能更好,因此您不需要猜测错位变量名称:

extern "C" void bmw_mark() { }

然后您不必猜测符号名称,只需使用-u bmw_mark即可。 这必须在与BMWCar的其他定义相同的编译单元中完成,因此它们最终位于相同的.o文件中。

答案 2 :(得分:0)

您可以使用模板和专业化,而不是使用switch。 以下示例提供了BMWCarMercCar的实现,但不包括PorscheCar

enum CarType
{
    BMW,
    PORSCHE,
    MERC
};

struct Car {};
struct BMWCar:public Car{};

// DO NOT SHIP 
// struct PorscheCar:public Car{};

struct MercCar:public Car{};

template <CarType type>
struct CarFactory;


template <>
struct CarFactory<BMW>
{
static Car* create()
{
    return new BMWCar();
}
};

/* 
// DO NOT SHIP
template <>
struct CarFactory<PORSCHE>
{
static Car* create()
{
    return new PorscheCar();
}
};
*/

template <>
struct CarFactory<MERC>
{
static Car* create()
{
    return new MercCar();
}
};

int main()
{
    Car* m = CarFactory<MERC>::create();
}

答案 3 :(得分:0)

您可以将类型动态注册到数组。我想到的第一个解决方案就是(你可能想要一个更好的设计):

{{1}}

答案 4 :(得分:0)

我的解决方案:

class CarCreator
    {
        public:
        virtual Car* operator(int otherArgs) = 0;
    };

    class BMWCarCreator : pure CarCreator
    {
        public:
        Car* operator(int otherArgs) { return new BMWCar(otherArgs); }
    };

    // in BMWCar.cpp
    class BMWCar : public Car
    {
     // SOME WAY TO STATICALLY REGISTER BMWCarCreator to BMWCarType 
     BMWCar( int otherArgs ) { }
    };

    class CarFactory
    {
    public:
      // associates the type-creator_FnObj
      void registerCreator(CarType type, CarCreator* creator); 

      // Creates the car based on associated CarCreator*
      Car* create(CarType type, int otherArgs)
      {
       CarCreator* creator = this->findCreatorAssociation(type);
       if (!creator)
        throw exception;
       return creator(otherArgs);
      }
    }

我仍然需要弄明白:

  • 所有Car派生类都应该注册CarFactory,正确的TypeCarCreator
  • 为每个Car派生类静态调用registerCreator

答案 5 :(得分:0)

我在这件事上并不是一个好的程序员。但是,试着给出我最好的答案。

#include <iostream>

using namespace std;

class Car { virtual void SomeMethods() {} /* Your Code */ };
class BMWCar : public Car { /* Your Code */ };
class PorscheCar : public Car { /* Your Code */ };
class MercCar : public Car { /* Your Code */ };

enum CarType
{
    BMW,
    PORSCHE,
    MERC,
    BMW_N_PORSCHE,
    BMW_N_MERC,
    PORSCHE_N_MERC,
    ALL
};

class FinalCar
{
private:
    Car* m_car[3];
    size_t m_size;
    CarType m_CarType;
public:
    FinalCar(Car* car1, CarType carType)
    {
        m_car[0] = car1;
        m_car[1] = nullptr;
        m_car[2] = nullptr;
        m_size = 1;
        m_CarType = carType;
    }
    FinalCar(Car* car1, Car* car2, CarType carType)
    {
        m_car[0] = car1;
        m_car[1] = car2;
        m_car[2] = nullptr;
        m_size = 2;
        m_CarType = carType;
    }
    FinalCar(Car* car1, Car* car2, Car* car3, CarType carType)
    {
        m_car[0] = car1;
        m_car[1] = car2;
        m_car[2] = car3;
        m_size = 3;
        m_CarType = carType;
    }

    size_t GetSize()
    {
        return m_size;
    }

    CarType GetCarType()
    {
        return m_CarType;
    }

    Car* GetCar(size_t n)
    {
        return m_car[n];
    }

    ~FinalCar()
    {
        if (m_car[0] != nullptr)
        {
            delete m_car[0];
            m_car[0] = nullptr;
        }
        if (m_car[1] != nullptr)
        {
            delete m_car[1];
            m_car[1] = nullptr;
        }
        if (m_car[2] != nullptr)
        {
            delete m_car[2];
            m_car[2] = nullptr;
        }
    }
};

class CarFactory
{
public:
    static FinalCar create(CarType type)
    {
        switch (type)
        {
        case BMW:
            return FinalCar(new BMWCar(), BMW);
            break;
        case PORSCHE:
            return FinalCar(new PorscheCar(), PORSCHE);
            break;
        case MERC:
            return FinalCar(new MercCar(), MERC);
            break;
        case BMW_N_PORSCHE:
            return FinalCar(new BMWCar(), new PorscheCar(), BMW_N_PORSCHE);
            break;
        case BMW_N_MERC:
            return FinalCar(new BMWCar(), new MercCar(), BMW_N_MERC);
            break;
        case PORSCHE_N_MERC:
            return FinalCar(new PorscheCar(), new MercCar(), PORSCHE_N_MERC);
            break;
        default:
            return FinalCar(new BMWCar(), new PorscheCar(), new MercCar(), ALL);
            break;
        }
    }
 };

int main()
{
    FinalCar myCar = CarFactory::create(PORSCHE_N_MERC);
    for (int i = 0; i < myCar.GetSize(); i++)
    {
        Car* tmpCar = myCar.GetCar(i);
        if (dynamic_cast<BMWCar*>(tmpCar))
        {
            cout << "BMWCar*" << endl;
        }
        else if (dynamic_cast<PorscheCar*>(tmpCar))
        {
            cout << "PorscheCar*" << endl;
        }
        else if (dynamic_cast<MercCar*>(tmpCar))
        {
            cout << "MercCar*" << endl;
        }
    }
}