设计模式:部分支持的派生类的统一接口

时间:2010-11-20 03:19:08

标签: c++ design-patterns virtual architecture

我正在为我的项目设计一个界面,并且好奇这个想法是否可以实现。 这是情况, 在运行时,我想使用一个基类指针数组来向不同的派生对象发出命令。不同的派生对象获得了不同的实现(虚函数)。我的问题是:如果这些对象得到不同的接口支持级别,我怎么能避免写一个空函数?

例如,(我当前的代码)

Class Base { //this is the main interface
public:
   virtual void basicFun() = 0;
   virtual void funA(int input1) {return;}
   virtual void funB(int input2) {return;}
   virtual void funC(int input3) {return;}
}

Class Derived1 : public Base { //this class support only funA()
public:
   void basicFun() {//....}
   void funA(int input1) {//do something}
}

Class Derived2 : public Base { //this class support both funA() funB()
public:
   void basicFun() {//....}
   void funA(int input1) {//do something}
   void funB(int input2) {//do something}
}

Class Derived3 : public Base { //this class support all
public:
   void basicFun() {//....}
   void funA(int input1) {//do something}
   void funB(int input2) {//do something}
   void funC(int input3) {//do something}
}

假设:对于某个对象,永远不会调用不支持的函数。如果basePtr指向的对象是Derived1或Derived2,则永远不会调用BasePtr-> funC()。 问题是:

  1. 如果需要统一的界面,我必须在Base或Derived中定义一个空函数
  2. 如果像上面那样定义了空函数,编译器会一直警告我未引用的参数(input1~input3)。当然我可以把它关掉,但就是不喜欢这样。
  3. 那么,是否有任何模式可用于实现统一接口而无需定义空函数?我已经考虑了几天了。这似乎不可能。因为funA()funB()和funC()必须在接口中,所以我可以使用Base指针数组来控制所有对象,这意味着在Derived1中,必须以某种方式定义funB()和funC()。

    谢谢,感恩节快乐,感谢您分享您的想法。

    索尔蒂

3 个答案:

答案 0 :(得分:1)

统一接口是一件好事。这意味着您必须在接口中实现所有方法,即使这意味着您将拥有空方法。这个问题没有设计模式,因为它首先不是一个真正的问题。

暂时考虑一下:假设您有Car界面,方法Accelerate()Brake()。派生自Car的类必须实现所有方法。您是否希望从Car派生的对象实现Accelerate()方法而不是Brake()方法?这将是一个非常不安全的Car

OOP上下文中的接口必须具有双方都遵守的明确定义的合同。在C ++中,通过要求在派生类中实现所有纯虚拟,在某种程度上强制执行此操作。尝试使用未实现的虚方法实例化一个类会导致编译错误,假设有人不使用愚蠢的技巧来解决它。

您反对创建空方法,因为它们会导致编译器警告。在您的情况下,只需省略参数名称:

void funC(int) // Note lack of parameter name
{
}

或者说出这个名字:

void funC(int /*input3*/)
{
}

甚至是by using templates!

template<class T> void ignore( const T& ) { }

//...

void funC(int input3)
{
    ignore(input3);
}

答案 1 :(得分:0)

这就是我要做的事情:使用所有纯虚方法的空默认实现创建一个帮助器基类。然后,您可以从此基类而不是主接口派生,然后选择要覆盖的方法。

// main interface, everything pure virtual
struct IBase
{
   virtual ~IBase() {}
   virtual void basicFun() = 0;
   virtual void funA(int input1) = 0;
   virtual void funB(int input2) = 0;
   virtual void funC(int input3) = 0;
};

// helper base class with default implementations (empty)
class Base : public IBase
{
   void basicFun() {}
   void funA(int input1) {}
   void funB(int input2) {}
   void funC(int input3) {}
};

class Derived1 : public Base { //this class support only funA()
   void funA(int input1) {//do something}
};

class Derived2 : public Base { //this class support both funA() funB()
   void funA(int input1) {//do something}
   void funB(int input2) {//do something}
};

class Derived3 : public IBase { //this class support all
   void basicFun() {//....}
   void funA(int input1) {//do something}
   void funB(int input2) {//do something}
   void funC(int input3) {//do something}
};

int main()
{
   // I always program to the interface
   IBase& b1 = Derived1(); b1.basicFun(); b1.funA(); b1.funB(); b1.funC();
   IBase& b2 = Derived2(); b2.basicFun(); b2.funA(); b2.funB(); b2.funC();
   IBase& b3 = Derived3(); b3.basicFun(); b3.funA(); b3.funB(); b3.funC();
}

答案 2 :(得分:0)

如果你真的需要非统一的界面(以前考虑一下),也许访客模式值得一试:

struct Visitor;

struct Base
{
    virtual ~Base() {}
    virtual void accept(Visitor& v) { v.visit(*this); }
};

struct InterfaceA : Base
{
    void accept(Visitor& v) { v.visit(*this); }
    virtual void MethodA() = 0;
};

struct InterfaceB : Base
{
    void accept(Visitor& v) { v.visit(*this); }
    virtual void MethodB() = 0;
};

struct InterfaceA2 : InterfaceA
{
    void accept(Visitor& v) { v.visit(*this); }
    void MethodA(); // Override, eg. in terms of MethodC
    virtual void MethodC() = 0;
};

// Provide sensible default behavior. Note that the visitor class must be
// aware of the whole hierarchy of interfaces
struct Visitor
{
    virtual ~Visitor() {}
    virtual void visit(Base& b) { throw "not implemented"; }
    virtual void visit(InterfaceA& x) { this->visit(static_cast<Base&>(x)); }
    virtual void visit(InterfaceA2& x) { this->visit(static_cast<InterfaceA&>(x)); }
    virtual void visit(InterfaceB& x) { this->visit(static_cast<Base&>(x)); }
};


// Concrete visitor: you don't have to override all the functions. The unimplemented
// ones will default to what you expect.
struct MyAction : Visitor
{
    void visit(InterfaceA& x)
    {
        x.MethodA();
    }

    void visit(InterfaceB& x)
    {
        x.methodB();
    }
};

用法:

Base* x = getSomeConcreteObject();
MyAction f;
x->accept(f);

这将调用MethodAMethodB,具体取决于x的运行时类型。实现的访问者允许您不重载许多函数,如果未实现该行为,则回退到基类的操作。最后,如果您未能在某个类的访问者中提供操作,则默认为throw "not implemented"

Const正确性可能会迫使您区分VisitorConstVisitor,对于他们来说,所有accept方法都是const。