具有许多输入小部件的接口的设计方法

时间:2011-05-12 15:45:56

标签: c++ design-patterns refactoring

我的界面有~16个输入字段框。它们都被声明为类中的公共指针,已初始化等等。但是,随着我的代码越来越多地使用私有函数进行数据库提交,错误检查,临时存储例程等,如果字段必须已经变得非常痛苦删除或添加一个新的我必须深入研究所有这些私有函数并显式删除/添加字段;并且总是关于现场订购。

是一种更简单的方法!

这是我的想法,我希望任何人都可以拍下来或以此为基础:

我的想法是存储指针数组中所有输入字段的指针,然后所有这些私有辅助函数遍历数组;然而,其中一些私有函数是静态的,有些是非静态函数;因此需要一些更多的指针魔法,或者我应该有两个这样的指针数组函数:一个用于静态函数,一个用于非静态函数?

为了使事情更复杂,在小部件上调用的方法取决于私有函数正在做什么...有些人可能会调用“ - > value(foo)”,有些人可能会调用“ - > location(1), - >位置(2),“按窗口小部件的顺序递增。有没有办法传递调用的方法和要传递给包含输​​入字段指针数组的新辅助函数的参数?

深思熟虑: 也许我想通过在每次需要进行更改时保存自己在代码中滚动的负担来试图过于花哨?也许这会增加所有额外指针间接开销的开销?受苦更好吗?

谢谢,任何帮助表示赞赏。如果真的需要一些代码示例,我可以制作一些。

代码示例(这不会编译并且徒手打字作为示例)

class Foo
{
public:
   InputBox * in1;
   InputBox * in2;
   InputBox * in3;
   ExternalDataSource * exds; // Pretend this object you can retrieve values out of
private:
   static void clearFieldsFunc1(void * v); // callback bound to a button
   static void loadFieldFunc2(void * v);  // callback bound to a button
   void printFieldsFunc3(); // not a callback, just called from various functions
}

Foo::Foo()
{
   in1= new InputBox (0,0,10,10);  // Box x,y,w,h
   in2= new InputBox (15,0,10,10);
   in3= new InputBox (30,0,10,10);
   exds = new ExernalDataSource("US Intelligence Agency");
}

// Clears the fields
void Foo::clearFieldsFunc1(void * v)
{
   Foo * fptr = ((Foo*)v);
   fptr->in1->clear();
   fptr->in2->clear();
   fptr->in3->clear();
}

// Loads the fields
void Foo::loadFieldFunc2(void * v)
{
   Foo * fptr = ((Foo*)v);
   fptr->in1->value(fptr->exds->getValue(1));
   fptr->in2->value(fptr->exds->getValue(2));
   fptr->in3->value(fptr->exds->getValue(3));
}

// Prints the fields
void Foo::printFieldsFunc3()
{
   printf("%s\n",this->in1->value());
   printf("%s\n",this->in2->value());
   printf("%s\n",this->in3->value());
}

4 个答案:

答案 0 :(得分:1)

您可以将InputBox的容器作为成员添加到Foo并迭代它以使生活更简单。

#include <vector>

    class Foo
    {
    private:
       static void clearFieldsFunc1(void * v); // callback bound to a button
       static void loadFieldFunc2(void * v);  // callback bound to a button
       void printFieldsFunc3(); // not a callback, just called from various functions

       std::vector<InputBox> m_inputBoxes;
       typedef std::vector<InputBox>::iterator InputItr;
    };

    Foo::Foo()
    {
       m_inputBoxes.push_back(InputBox(0, 0, 10, 10));
       m_inputBoxes.push_back(InputBox(15, 0, 10, 10));
       m_inputBoxes.push_back(InputBox(30, 0, 10, 10));
    }

    // Clears the fields
    void Foo::clearFieldsFunc1(void * v)
    {
       for(InputItr itr(m_inputBoxes.begin()); itr != m_inputBoxes.end(); ++itr)
           itr->clear(); // calls clear for each InputBox
    }

    // etc

答案 1 :(得分:1)

我对你所说的看法是你的代码变得越来越复杂。我看到了这个问题,因为它是一个难以理解的大块。因此,响应应该是将其分解为更小的更易于管理的块。

您可以考虑采用类似M.V.C. Pattern的内容。基本思路是将代码分为三个部分。 “模型”部分处理所有数据库活动。 “视图”部分处理所有GUI交互。 “Controller”部分处理实现逻辑。这将有助于使巨大的oump-o-code™更容易维护。

答案 2 :(得分:0)

将窗口小部件包装到能够在将其添加到父窗口类时自行“自我初始化”的类中的可能性怎么样?换句话说,您可以在parent_window类中使用类似add_widget(widget_type* widget)的方法,其中class widget_type将是具有标准接口的抽象基类对象。主接口组件可以是构造函数,也可以是指向parent_window类的指针,即widget_type::widget_type(parent_window* window)。既然窗口小部件有一个指向父窗口的指针,它可以调用parent_window类中所需的任何方法进行初始化。此外,parent_window类将获得传递指针的所有权widget_type,以便在销毁父节点时可以正确销毁小部件。

所以我在一个非常抽象的层面上思考,你的代码可能看起来像:

class parent_window
{
    private:
        widget_type** widget_ptr_array;
        //... more data elements, private methods, etc.

    public:
        parent_window();            
        //... more public data, methods, etc.

        void add_widget(widget_type* widget) 
        { 
            widget->initialize(this);
            widget_ptr_array[CURRENT_EMPTY_SLOT] = widget;
        }

        void clear_fields()
        {
            for (int i=0; i < MAX_WIDGETS; i++)
            {
                widget_ptr_array[i]->clear_field();
            }
        }

        //for simplicity I'm assuming the size of 
        //the array "value" points to is appropriate in length
        void load_fields(some_type* value)
        {
            for (int i=0; i < MAX_WIDGETS; i++)
            {
                widget_ptr_array[i]->load_field(value[i]);
            }
        }

        ~parent_window()
        {
            for (int i=0; i < MAX_WIDGETS; i++)
            {
                widget_ptr_array[i]->destroy(this);
                delete widget_ptr_array[i];
            }

            delete [] widget_ptr_array;
        }
};

//somewhere else ...
class widget_type
{
    private:
        //...private data, methods, etc.
    public:
        widget_type();
        virtual void initialize(parent_window* window) = 0;
        virtual void destroy(parent_window* window) = 0;

        virtual void clear_field() = 0;
        virtual void load_field(some_type value) = 0;
};

class derived_widget_type: public widget_type { /*...*/ };
class another_widget_type: public widget_type { /*...*/ };

然后你会拨打电话:

parent_window window;
window.add_widget(new derived_widget_type(optional_arg_val));
window.add_widget(new another_widget_type(optional_arg_val));

现在,您不必担心每次要添加新的窗口小部件类型时都要更改parent_window类。最重要的是确保小部件可以访问父级中正确初始化和销毁​​自身所需的所有方法和数据结构。

编辑:对上面的代码进行了一些更改,以更好地反映OP新代码添加中的一些方法

答案 3 :(得分:0)

  • 你不会太花哨。 GUI交互的重复代码是一个真正的问题。

通过guiElements向量迭代是一个很好的解决方案。您需要一个支持clear(),load()和print()作为虚函数的基本guiElement类。您可以处理差异,例如清除零或清除子类中的空白。

即便如此,您仍然会在每个字段中对代码中的多个位置产生影响。当您添加其他对话框时,您也可能会发现自己编写非常相似的代码以执行相同的操作。

对于具有超过1000个参数字段的EDA应用程序,我使用了一种组合方法 - 迭代器和代码/文档生成。生成器编写了具有迭代器的代码,并处理了特殊情况。为了允许自定义数据验证字段标识符出现在头文件中,我不能仅使用迭代器方法。我还必须生成文档来生成内存映射和来自字段信息的帮助文档 - 因此添加代码生成不是一个很大的额外成本。我在一个地方保存了关于每个字段的源信息,一个YAML文件。规范的变化,我可以通过YAML文件中的更改进行一次我需要的更改。

我用C ++编写了自定义代码/文档生成器。再做一次我会调整cog code generator,它非常简单,适合这种半重复的代码。将cog视为类固醇上的宏,其优点是可以获得真实的代码,以便您可以单步调试。

总和

  • 使用迭代器,遍历guiElements。很有可能它足以满足您的需求。
  • 如果您需要更多内容,请查看使用cog生成代码,以便将所有字段信息放在一个位置。