工厂方法反if实现

发布于 2024-09-14 16:14:59 字数 735 浏览 3 评论 0原文

我正在我的 C++ 项目中应用工厂设计模式,下面您可以看到我是如何做到的。我尝试通过遵循“反 if”活动来改进我的代码,因此想删除我所拥有的 if 语句。知道我该怎么做吗?

typedef std::map<std::string, Chip*> ChipList;

Chip* ChipFactory::createChip(const std::string& type) {
    MCList::iterator existing = Chips.find(type);
    if (existing != Chips.end()) {
        return (existing->second);
    }
    if (type == "R500") {
        return Chips[type] = new ChipR500();
    }
    if (type == "PIC32F42") {
        return Chips[type] = new ChipPIC32F42();
    }
    if (type == "34HC22") {
        return Chips[type] = new Chip34HC22();
    }
    return 0;
}

我想象创建一个地图,以字符串为键,以及构造函数(或创建对象的东西)。之后,我可以使用类型(类型是字符串)从映射中获取构造函数并创建我的对象,而无需任何 if 。 (我知道我有点偏执,但我想知道是否可以做到。)

I'm applying the Factory design pattern in my C++ project, and below you can see how I am doing it. I try to improve my code by following the "anti-if" campaign, thus want to remove the if statements that I am having. Any idea how can I do it?

typedef std::map<std::string, Chip*> ChipList;

Chip* ChipFactory::createChip(const std::string& type) {
    MCList::iterator existing = Chips.find(type);
    if (existing != Chips.end()) {
        return (existing->second);
    }
    if (type == "R500") {
        return Chips[type] = new ChipR500();
    }
    if (type == "PIC32F42") {
        return Chips[type] = new ChipPIC32F42();
    }
    if (type == "34HC22") {
        return Chips[type] = new Chip34HC22();
    }
    return 0;
}

I would imagine creating a map, with string as the key, and the constructor (or something to create the object). After that, I can just get the constructor from the map using the type (type are strings) and create my object without any if. (I know I'm being a bit paranoid, but I want to know if it can be done or not.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

不羁少年 2024-09-21 16:14:59

你是对的,你应该使用从键到创建函数的映射。
在您的情况下,将为

typedef Chip* tCreationFunc();
std::map<std::string, tCreationFunc*> microcontrollers;

每个新的芯片驱动类 ChipXXX 添加一个静态函数:

static Chip* CreateInstance()
{
    return new ChipXXX();
}

并将该函数注册到映射中。

您的工厂函数应该是这样的:

Chip* ChipFactory::createChip(std::string& type)
{
    ChipList::iterator existing = microcontrollers.find(type);    
    if (existing != microcontrollers.end())
        return existing->second();

    return NULL;
}

请注意,不需要复制构造函数,如您的示例中所示。

You are right, you should use a map from key to creation-function.
In your case it would be

typedef Chip* tCreationFunc();
std::map<std::string, tCreationFunc*> microcontrollers;

for each new chip-drived class ChipXXX add a static function:

static Chip* CreateInstance()
{
    return new ChipXXX();
}

and also register this function into the map.

Your factory function should be somethink like this:

Chip* ChipFactory::createChip(std::string& type)
{
    ChipList::iterator existing = microcontrollers.find(type);    
    if (existing != microcontrollers.end())
        return existing->second();

    return NULL;
}

Note that copy constructor is not needed, as in your example.

一梦浮鱼 2024-09-21 16:14:59

工厂的目的不是摆脱 ifs,而是将它们放在实际业务逻辑代码的单独位置,而不是污染它。这只是关注点分离。

The point of the factory is not to get rid of the ifs, but to put them in a separate place of your real business logic code and not to pollute it. It is just a separation of concerns.

寂寞笑我太脆弱 2024-09-21 16:14:59

如果您绝望,您可以编写一个跳转表/clone() 组合来完成这项工作,而无需 if 语句。

class Factory {
    struct ChipFunctorBase {
        virtual Chip* Create();
    };
    template<typename T> struct CreateChipFunctor : ChipFunctorBase {
        Chip* Create() { return new T; }
    };
    std::unordered_map<std::string, std::unique_ptr<ChipFunctorBase>> jumptable;
    Factory() {
        jumptable["R500"] = new CreateChipFunctor<ChipR500>();
        jumptable["PIC32F42"] = new CreateChipFunctor<ChipPIC32F42>();
        jumptable["34HC22"] = new CreateChipFunctor<Chip34HC22>();
    }
    Chip* CreateNewChip(const std::string& type) {
        if(jumptable[type].get())
            return jumptable[type]->Create();
        else
            return null;
    }
};

然而,只有当您拥有大量不同的芯片类型时,这种方法才变得有价值。对于少数情况,只写几个 if 会更有用。

快速说明:我使用了 std::unordered_map 和 std::unique_ptr,它们可能不是您的 STL 的一部分,具体取决于您的编译器的新程度。替换为 std::map/boost::unordered_map 和 std::/boost::shared_ptr。

If you're desperate, you could write a jump table/clone() combo that would do this job with no if statements.

class Factory {
    struct ChipFunctorBase {
        virtual Chip* Create();
    };
    template<typename T> struct CreateChipFunctor : ChipFunctorBase {
        Chip* Create() { return new T; }
    };
    std::unordered_map<std::string, std::unique_ptr<ChipFunctorBase>> jumptable;
    Factory() {
        jumptable["R500"] = new CreateChipFunctor<ChipR500>();
        jumptable["PIC32F42"] = new CreateChipFunctor<ChipPIC32F42>();
        jumptable["34HC22"] = new CreateChipFunctor<Chip34HC22>();
    }
    Chip* CreateNewChip(const std::string& type) {
        if(jumptable[type].get())
            return jumptable[type]->Create();
        else
            return null;
    }
};

However, this kind of approach only becomes valuable when you have large numbers of different Chip types. For just a few, it's more useful just to write a couple of ifs.

Quick note: I've used std::unordered_map and std::unique_ptr, which may not be part of your STL, depending on how new your compiler is. Replace with std::map/boost::unordered_map, and std::/boost::shared_ptr.

像你 2024-09-21 16:14:59

不,你无法摆脱“如果”。 createChip 方法根据您作为参数传递的常量(类型名称)创建一个新实例。
但是您可以稍微优化您的代码,从 if 语句中删除这两行。

 microcontrollers[type] = newController;
 return microcontrollers[type];

No you cannot get rid of the ifs. the createChip method creats a new instance depending on constant (type name )you pass as argument.
but you may optimaze yuor code a little removing those 2 line out of if statment.

 microcontrollers[type] = newController;
 return microcontrollers[type];
流心雨 2024-09-21 16:14:59

回答你的问题:是的,你应该创建一个工厂,其中包含构造所需对象的函数的映射。构造的对象应该向工厂本身提供并注册该函数。

在其他几个 SO 问题中也有一些关于这个主题的阅读,所以我会让你阅读它,而不是在这里全部解释。

C++ 中的通用工厂

有没有办法从保存类名的字符串中实例化对象?

To answer your question: Yes, you should make a factory with a map to functions that construct the objects you want. The objects constructed should supply and register that function with the factory themselves.

There is some reading on the subject in several other SO questions as well, so I'll let you read that instead of explaining it all here.

Generic factory in C++

Is there a way to instantiate objects from a string holding their class name?

三生路 2024-09-21 16:14:59

您可以在工厂中使用 if - 只是不要让它们散布在您的代码中。

You can have ifs in a factory - just don't have them littered throughout your code.

梦里梦着梦中梦 2024-09-21 16:14:59
struct Chip{
};

struct ChipR500 : Chip{};

struct PIC32F42 : Chip{};

struct ChipCreator{
   virtual Chip *make() = 0;
};

struct ChipR500Creator : ChipCreator{
   Chip *make(){return new ChipR500();}
};

struct PIC32F42Creator : ChipCreator{
   Chip *make(){return new PIC32F42();}
};

int main(){
   ChipR500Creator m;  // client code knows only the factory method interface, not the actuall concrete products
   Chip *p = m.make();
}
struct Chip{
};

struct ChipR500 : Chip{};

struct PIC32F42 : Chip{};

struct ChipCreator{
   virtual Chip *make() = 0;
};

struct ChipR500Creator : ChipCreator{
   Chip *make(){return new ChipR500();}
};

struct PIC32F42Creator : ChipCreator{
   Chip *make(){return new PIC32F42();}
};

int main(){
   ChipR500Creator m;  // client code knows only the factory method interface, not the actuall concrete products
   Chip *p = m.make();
}
满地尘埃落定 2024-09-21 16:14:59

从本质上讲,您所要求的称为虚拟构造,即构建仅在运行时知道其类型的对象的能力。

当然,C++ 不允许构造函数是虚拟的,因此这需要一些技巧。常见的面向对象方法是使用 Prototype 模式:

class Chip
{
public:
  virtual Chip* clone() const = 0;
};

class ChipA: public Chip
{
public:
  virtual ChipA* clone() const { return new ChipA(*this); }
};

然后实例化这些原型的映射并使用它来构建对象 (std::map)。通常,地图被实例化为单例。

到目前为止已经说明的另一种方法是类似的,并且包括直接注册方法而不是对象。它可能是也可能不是您的个人偏好,但它通常会稍微快一些(不多,您只是避免了虚拟调度)并且内存更容易处理(您不必在函数指针)。

然而,您应该注意的是内存管理方面。您不想发生泄漏,因此请确保使用 RAII 习惯用法。

What you are asking for, essentially, is called Virtual Construction, ie the ability the build an object whose type is only known at runtime.

Of course C++ doesn't allow constructors to be virtual, so this requires a bit of trickery. The common OO-approach is to use the Prototype pattern:

class Chip
{
public:
  virtual Chip* clone() const = 0;
};

class ChipA: public Chip
{
public:
  virtual ChipA* clone() const { return new ChipA(*this); }
};

And then instantiate a map of these prototypes and use it to build your objects (std::map<std::string,Chip*>). Typically, the map is instantiated as a singleton.

The other approach, as has been illustrated so far, is similar and consists in registering directly methods rather than an object. It might or might not be your personal preference, but it's generally slightly faster (not much, you just avoid a virtual dispatch) and the memory is easier to handle (you don't have to do delete on pointers to functions).

What you should pay attention however is the memory management aspect. You don't want to go leaking so make sure to use RAII idioms.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文