进行如此复杂的C ++类设计的目的是什么?

时间:2018-11-21 14:06:34

标签: c++ design-patterns

我遇到了一个开源C ++代码,我很好奇,为什么人们用这种方式设计类?

首先,这是Abstract类:

class BaseMapServer
    {
    public:
        virtual ~BaseMapServer(){}

        virtual void LoadMapInfoFromFile(const std::string &file_name) = 0;
        virtual void LoadMapFromFile(const std::string &map_name) = 0;
        virtual void PublishMap() = 0;
        virtual void SetMap() = 0;
        virtual void ConnectROS() = 0;
    };

这里没有什么特别的,拥有一个抽象类可以有几个众所周知的原因。因此,从这一点出发,我认为也许作者希望在其他课程之间共享共同的特征。因此,这里是下一个类,它是一个单独的类,但实际上包含上述类型抽象类的指针(实际的cpp文件,另外两个类是头文件):

class MapFactory
{
    BaseMapServer *CreateMap(
            const std::string &map_type,
            rclcpp::Node::SharedPtr node, const std::string &file_name)
        {
            if (map_type == "occupancy") return new OccGridServer(node, file_name);
            else
            {
                RCLCPP_ERROR(node->get_logger(), "map_factory.cpp 15: Cannot load map %s of type %s", file_name.c_str(), map_type.c_str());
                throw std::runtime_error("Map type not supported")
            }
        }
};

现在有趣的事情来了,这是抽象类的子类:

class OccGridServer : public BaseMapServer
    {
    public:
        explicit OccGridServer(rclcpp::Node::SharedPtr node) : node_(node) {}
        OccGridServer(rclcpp::Node::SharedPtr node, std::string file_name);
        OccGridServer(){}
        ~OccGridServer(){}

        virtual void LoadMapInfoFromFile(const std::string &file_name);
        virtual void LoadMapFromFile(const std::string &map_name);
        virtual void PublishMap();
        virtual void SetMap();
        virtual void ConnectROS();

    protected:
        enum MapMode { TRINARY, SCALE, RAW };

        // Info got from the YAML file
        double origin_[3];
        int negate_;
        double occ_th_;
        double free_th_;
        double res_;
        MapMode mode_ = TRINARY;
        std::string frame_id_ = "map";
        std::string map_name_;

        // In order to do ROS2 stuff like creating a service we need a node:
        rclcpp::Node::SharedPtr node_;

        // A service to provide the occupancy grid map and the message with response:
        rclcpp::Service<nav_msgs::srv::GetMap>::SharedPtr occ_service_;
        nav_msgs::msg::OccupancyGrid map_msg_;

        // Publish map periodically for the ROS1 via bridge:
        rclcpp::TimerBase::SharedPtr timer_;
    };

那么MapFactory类的目的是什么?

更具体地说-创建一个类,该类具有一个抽象类BaseMapServer类型的指针,这是一个构造函数(我相信),而这个怪异的构造函数为名为{的新对象创建了一个内存{1}}并返回?只写这个让我很困惑。我真的想成为一个更好的C ++编码器,并且我非常想知道这些代码设计背后的秘密。

3 个答案:

答案 0 :(得分:5)

MapFactory类用于根据传递给它的参数创建BaseMapServer的正确子类实例。

在这种特殊情况下,只有一个子类实例,但是也许有计划添加更多子类实例。然后,当添加更多时,factory方法将如下所示:

BaseMapServer *CreateMap(
        const std::string &map_type,
        rclcpp::Node::SharedPtr node, const std::string &file_name)
    {
        if (map_type == "occupancy") return new OccGridServer(node, file_name);
        // create Type2Server
        else if (map_type == "type2") return new Type2Server(node, file_name);   
        // create Type3Server
        else if (map_type == "type3") return new Type3Server(node, file_name);
        else
        {
            RCLCPP_ERROR(node->get_logger(), 
                         "map_factory.cpp 15: Cannot load map %s of type %s", 
                         file_name.c_str(), map_type.c_str());
            throw std::runtime_error("Map type not supported")
        }
    }

这具有一个优点,即调用者不需要知道正在使用的确切子类,并且实际上,潜在的子类可能会更改,甚至可以在后台进行替换,而无需修改调用代码。工厂方法会为您内部化此逻辑。

答案 1 :(得分:0)

它是工厂模式。参见https://en.wikipedia.org/wiki/Factory_method_pattern。看起来当前代码仅支持一种实现(OccGridServer),但将来可能会添加更多实现。相反,如果只可能有一个具体的实现,那就是过度设计。

答案 2 :(得分:0)

这是工厂设计模式的示例。用例是这样的:有几种类型的非常相似的类将在代码中使用。在这种情况下,OccGridServer是实际显示的唯一一个,但是一般性的解释可能引用假设的DogCatOtter等类。由于它们的相似性,因此需要一些多态性:如果它们都从基类Animal继承,则它们可以共享诸如::genus::species等的虚拟类方法,而派生类可以用基类指针/引用指向或引用。就您而言,OccGridServer继承自BaseMapServer;大概还有其他派生类,以及指针/引用。

如果您知道在编译时需要哪个派生类,则通常只需调用其构造函数即可。当特定派生类直到运行时才知道时,工厂设计模式的重点是简化派生类的选择。想象一下,用户通过选择按钮或输入名称来选择自己喜欢的动物。这通常意味着在某处存在一个大的if / else块,该块将某种I / O歧义消除器(字符串,枚举等)映射到特定的派生类类型,并调用其构造函数。将其封装在工厂模式中很有用,它可以像命名构造函数一样使用该歧义变量作为“构造函数”参数,并找到要构造的正确派生类。

通常,CreateMap将是BaseMapServer的静态方法。我不明白为什么在这种情况下为什么需要为工厂函数使用单独的类。

相关问题