代码之家  ›  专栏  ›  技术社区  ›  phunehehe

工厂方法反if实现

  •  1
  • phunehehe  · 技术社区  · 15 年前

    我在我的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。(我知道我有点偏执,但我想知道是否能做到。)

    8 回复  |  直到 10 年前
        1
  •  7
  •   Lior Kogan    15 年前

    你是对的,你应该使用一个从键到创建函数的映射。

    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;
    }
    

        2
  •  5
  •   Petar Minchev    15 年前

    工厂的意义不在于摆脱ifs,而是将它们放在真正业务逻辑代码的一个单独的地方,而不是污染它。这只是一种关注点的分离。

        3
  •  2
  •   Puppy    15 年前

    如果你很绝望,你可以编写一个jump table/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\u map和std::unique\u ptr,这可能不是STL的一部分,具体取决于编译器的新版本。替换为std::map/boost::unordered\u map和std::/boost::shared\u ptr。

        4
  •  1
  •   Arseny    15 年前

    但是你可以优化你的代码或者从if语句中删除这两行代码。

     microcontrollers[type] = newController;
     return microcontrollers[type];
    
        5
  •  1
  •   Community Mohan Dere    9 年前

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

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

    Generic factory in C++

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

        6
  •  0
  •   Skilldrick    15 年前

    你可以有 if 它在一个工厂里-只是不要把它们乱扔在你的代码里。

        7
  •  0
  •   Chubsdad    15 年前
    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();
    }
    
        8
  •  0
  •   Matthieu M.    15 年前

    Virtual Construction ,即生成类型仅在运行时已知的对象的能力。

    当然C++不允许构造函数是虚拟的,所以这需要一点诡计。常用的OO方法是使用 Prototype 图案:

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

    然后实例化这些原型的映射并使用它来构建对象( std::map<std::string,Chip*>

    另一种方法,如到目前为止所说明的,是类似的,包括直接注册方法而不是对象。这可能是你的个人喜好,也可能不是,但它通常会稍微快一点(不是很多,你只是避免了虚拟调度),而且内存更容易处理(你不必这样做) delete

    但是,您应该注意的是内存管理方面。你不想泄密,所以一定要使用RAII习语。