例如,我有一些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和方?
答案 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);
答案 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;
}