如何根据类型做不同的事情但不使用覆盖?

时间:2015-12-07 02:14:33

标签: c++ oop design-patterns override generic-programming

例如,我有一些Shape,每个形状都返回不同类型的按钮:

#include <stdio.h>
struct Button{
    Button(){
        printf("Button\n");
    }
};

struct CircleButton : public Button{
    CircleButton(){
        printf("CircleButton\n");
    }
};

struct SquareButton : public Button{
    SquareButton(){
        printf("SquareButton\n");
    }
};

struct Shape{
    virtual Button* getNewButton()=0;
};

struct Circle : public Shape{
    Button* getNewButton(){
        return new CircleButton();
    }
};

struct Square : public Shape{
    Button* getNewButton(){
        return new CircleButton();
    }
};

通过使用覆盖,我可以编写一些通用代码:

int main(){
    Shape* s=new Circle();
    Button* b=s->getNewButton();
    return 0;
}

但现在形状是数据传输对象,它不应该有任何方法:

struct Shape{
};

struct Circle : public Shape{
};

struct Square : public Shape{
};

我想保留我的通用代码,我试过了:

struct Helper{
    static Button* getNewButton(Shape* s){
        return new Button();
    }

    static Button* getNewButton(Circle* s){
        return new CircleButton();
    }

    static Button* getNewButton(Square* s){
        return new SquareButton();
    }
};

int main(){
    Shape* s=new Circle();
    Button* b=Helper::getNewButton(s);
    return 0;
}

但是这次我不能得到一个新的CircleButton,如何修改代码,或者可以应用什么样的设计模式,这样我就可以根据不同的形状获得不同类型的按钮,但是没有在Shape,Circle和方?

4 个答案:

答案 0 :(得分:0)

Factory Design Pattern是解决问题的最有效方法。

您可以使用RTTI来检查按钮的类型(使Button虚拟化的析构函数允许您使用dynamic_cast)并且仅当按钮已经是使用以下方法的正确类型时才允许捕获:

struct Shape {
    // Store the allocated button
    unique_ptr<Button> ptr;

    Shape()
    {
       ptr.reset(nullptr);
    }
    Shape(Button* otherButton)
    {
       ptr.reset(otherButton); 
    }

    virtual bool containsValidButton()
    {
        return ptr != nullptr;
    }
};

struct Circle : public Shape {
    Circle(Button* otherButton)
    {
        ptr.reset(dynamic_cast<Circle*>(otherButton) ? otherButton : nullptr);
    }
};

这会保留一个通用接口,但如果用户将不正确的按钮类型传递给构造函数,则不会存储按钮。

您可以使用的工厂方法将涉及创建枚举参数(如上一个链接中的示例所示),以向您的工厂方法表示要创建的Button类型:

enum ButtonType
{
    SQUARE,
    CIRCLE
};

static unique_ptr<Button> GetNewButton(ButtonType bType)
{
    unique_ptr<Button> ptr;
    switch (bType)
    {
    case SQUARE:
        ptr.reset(new SquareButton());
        break;
    case CIRCLE:
        ptr.reset(new CircleButton());
        break;
    }

    return ptr;
}

可以在Square或Circle的构造函数中使用(再次使用RTTI)的类似方法:

static unique_ptr<Button> GetNewButton(Shape* shape)
{
    unique_ptr<Button> ptr;
    if (dynamic_cast<Circle*>(shape))
    {
        ptr.reset(new CircleButton());
    }
    if (dynamic_cast<Square*>(shape))
    {
        ptr.reset(new SquareButton());
    }
    return ptr;
}

struct Square : public Shape {
    Square()
    {
        ptr = GetNewButton(this);
    }
};

如果您对上述案例中dynamic_cast如何发挥其魔力感到好奇,请参阅RTTI上的文章。

注意:您可能必须使用前向声明(如果不包括头文件中的声明)来编译上述代码段。

答案 1 :(得分:0)

如果您不允许在课程中使用任何方法,我看不到任何解决此问题的设计解决方案。只有类ID,Enum或instanceOf等类型的输入机制才能让你弄清楚你在运行时处理的对象的类型。显然,建议不要特别使用instanceOf,因为它违反了DIP

但我很好奇为什么你禁止任何内部方法?你是否意识到这是封装的全部本质?

如果你没有说出每个说法的任何方法,而是指内部结构,那么在这种情况下Visitor模式是一个很好的解决方案。

答案 2 :(得分:0)

boost::variant可能有帮助

using ShapeVariant = boost::variant<Circle, Square>;

class to_button : public boost::static_visitor<std::unique_ptr<Button>>
{
public:
    std::unique_ptr<Button> operator ()(const Circle&) const
    {
        return std::make_unique<CircleButton>();
    }

    std::unique_ptr<Button> operator ()(const Square&) const
    {
        return std::make_unique<SquareButton>();
    }
};

用法:

ShapeVariant shape = GetShape();
boost::apply_visitor(to_button(), shape);

Demo

答案 3 :(得分:0)

如果你打算采用OOP方法,你几乎会遇到这种或那种RTTI。但是,如果您真的想要像标签所暗示的那样进行“泛型编程”,那么请完全通用!非成员函数专业化可以帮助您,例如:

#include <vector>
#include <stdio.h>
// Shapes
struct Circle { };
struct Square { };

// Buttons
struct Button {
    virtual void click() { printf("Button clicked\n"); }
};

struct CircleButton : Button {
    void click() override { printf("CircleButton clicked\n"); }
};

struct SquareButton : Button {
    void click() override { printf("SquareButton clicked\n"); }
};

// Button creation

template<typename TShape>
auto getNewButton(const TShape& shape)
{
    static_assert(false, "Don't know how to create a button for this type.");
}

template<>
auto getNewButton(const Circle& shape)
{
    return new CircleButton();
}

template<>
auto getNewButton(const Square& shape)
{
    return new SquareButton();
}


int main()
{
    Circle circle;
    Square square;

    std::vector<Button*> buttons;
    buttons.push_back(getNewButton(circle));
    buttons.push_back(getNewButton(square));

    for (auto pButton : buttons)
        pButton->click();

    return 0;
}