65.9K
CodeProject 正在变化。 阅读更多。
Home

一个用户界面系统

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (7投票s)

2009 年 9 月 1 日

CPOL

5分钟阅读

viewsIcon

32746

downloadIcon

389

一个用户界面基础设施,可以快速附加到您的应用程序,并附带一个简单的、基于文本的、平台无关的用户界面,该界面建立在基础设施之上。

引言

几乎每个程序都需要某种方式与用户沟通。那些不沟通的程序(驱动程序、系统进程等)仍然可以从与开发人员沟通中受益,从而使调试更容易一些。

可以采取多种方法进行信息交换,从初始化配置文件和命令行开关,到运行时信号以及使用 shell 实际提示输入。实现这些功能通常需要程序员的特别关注,给他带来繁琐、冗长的工作。

但是这项工作可以大大减少。毕竟,用户所做的唯一事情就是更改变量和调用函数。因此,最佳解决方案是只告诉机器哪些变量和函数可以向用户公开(发布)。然后,用户通信就可以导出到其他标准的一次性实现的代码,从而节省程序员的工作。换句话说,程序员只需遵循基础设施定义的(非常少的)函数,而不必担心用户界面的实现。

这种方法还允许不同的用户界面实现,提供了极大的灵活性。这些实现不仅可以区分基于文本和图形,还可以区分用户提示、配置文件、命令行开关、网络协议等。

可以在文章末尾找到一系列(不)优点。

注意:虽然这里提出的概念是通用的,但代码仅适用于面向对象而非模块化编程。文件UI.hpp描述了为了使基础设施适合模块化编程需要进行的更改。

基础设施

本文提出的基础设施的基础是一个名为ui_export的单一抽象类。

class ui_export
{   ...
    virtual void addoptions()=0; 
    void postoption(string name, string desc, xxx);
    ...
}

想要向用户发布某些成员变量的每个类都需要继承自该类。这样做,它需要实现addoptions函数。addoptions的实现应该只包含对postoption函数的调用,该函数也继承自ui_export

class yourClass : public ui_export
{   void addoptions()
    { postoption(...);
      postoption(...);
      ...
    }
}

对于要发布的每个选项,都必须在addoptions中调用postoption函数。它有 14 种不同的实现,允许发布不同的数据类型和函数。它们是

  • ui_export派生的类
  • void postoption(string name, string desc, class ui_export* post)
  • 类型为void f()的简单函数
  • template<class X, class Y>
    void postoption(string name, string desc, X* Object, void (Y::* F)() )
  • 类型为boolintdoublecharchar*string的变量
  • void postoption(string name, string desc, xxx* post)
  • 用于类型:boolintdoublecharchar*string的获取和设置函数
  • template<class X, class Y>
    void postoption(string name, string desc, X* Object, xxx (Y::* getF)(), 
                                                        void (Y::* setF)(xxx) )

以下示例说明了对上述函数的调用会是什么样子

class car : public ui_export
{   ...
    class engine eng;
    void start() {};
    int weight;
    bool _lights;
    bool getlights() {return(_lights);}
    void setlights(bool par) {_lights=par; repaint();}
    ...
    void addoptions()
    {   postoption("eng", "manipulate the engine object", &eng);
        postoption("start", "start the car", this, &car::start);
        postoption("weight", "weight of the car in kg", &weight);
        postoption("lights", "headlights of the car", this, &car::getlights, 
                                                            &car::setlights);
    }
};

完成此操作后,用户界面就可以使用该类了。

用户界面

本代码提供的用户界面是基于文本的。它只使用 C++ 标准库,这使得它成为平台无关的。由于基础设施的面向类的方法,它具有菜单驱动的感觉。

同样,整个用户界面的基础是一个名为user_interface的单一类。要使用它,需要创建一个该类的实例。作为ui_export的子类,user_interface实现了所有postoption函数。这些用于初始化用户界面。operator()函数启动用户界面。

user_interface ui;
ui.postoption("o", "manipulate obj", &obj);
ui.postoption(...);
...
ui();

就是这样。

示例

以下是 'Demo1YourApp.cpp' 的简化版本。它演示了使用用户界面的简单程序是什么样的

#include"UI.hpp"

class yourClass : public ui_export 
{   public:
        bool b; int i;
        void addoptions()
        {
            postoption("b", "mess with a boolean", &b);
            postoption("i", "mess with an integer", &i);
        }
};

int main(int argc, char* argv)
{
    yourClass c;
    user_interface ui;
    ui.postoption("yourClass", "mess around with yourClass", &c);
    ui();
}

保存功能

用户界面具有简单的“保存状态”机制。它可以用于初始化您的应用程序,但并非旨在实际保存复杂应用程序的状态(启动会花费大量时间)。当用户界面退出时,它会询问您是(Q)uit(不保存)还是(S)ave(然后退出)。

它的工作方式是简单地记住所有用户输入并将其保存在文件中。启动时,用户界面会打开文件(默认文件名是 'input')并执行所有之前保存的输入。加载和保存文件的文件名可以通过用户界面对象访问

ui.loadfilename="init";
ui.savefilename="save";

在用户界面执行期间也可以更改保存文件的名称。这是通过使用默认发布的SaveFile选项来完成的。

您还可以手动编辑包含已保存输入的保存文件。

C++ 标准库

该基础设施概念带来的另一个巨大优势是,像 C++ 标准库这样的常用库可以提前使其支持 UI。这再次减少了程序员的工作量,并确保了不同程序行为的统一性,这对于用户来说非常有用。

已经开始。文件stdUI.hpp旨在通过使用 C++ 标准库类在stdUI命名空间中构建支持 UI 的类。以下类已可用

  • stdUI::vector
  • stdUI::ios_base
  • stdUI::basic_ios
  • stdUI::basic_istream
  • stdUI::basic_ostream
  • stdUI::basic_iostream
  • stdUI::basic_stringstream
  • stdUI::basic_fstream

非基本版本也可用。

示例

在文件 'Demo2StreamLib.cpp' 中可以找到对stdUI::iostreamstdUI::stringstreamstdUI::fstream类以及stdUI::coutstdUI::cin对象 UI 功能的小型演示。

结论

优点

  • 不再需要完全实现用户交互机制
  • 程序员只需了解(非常少的)基础设施命令
  • 允许多种用户界面实现(基于文本、图形、配置文件解析、命令行开关)
  • 简单的初始化或状态保存功能
  • 不同程序的统一用户体验
  • (几乎)过多的程序参数可编辑性(有利于调试,不利于用户)
  • 像 C++ 标准库这样的常用库可以提前发布,进一步减少工作量

缺点

  • 代码非常面向类(参见关于模块化编程的注释)
  • 没有信息隐藏/一旦发布选项就无法隐藏
  • (导致大型项目变得有些“选项臃肿”)

延伸阅读

许多更专业的技术文档可以在源代码中找到。主题是

  • 模块化编程的修改
  • 为什么选择代理
  • 通过使用MVI构建 C++ 标准库类的修改版本
© . All rights reserved.