赞
踩
UI设计的3大原则:
直到现在各个UI系统,包括题主所提到的MFC、WPF、Qt,也包括其它,诸如Android SDK、Cocoa的构建仍旧建立在这3大原则的基础上。
要提到MFC,就不得不先提到Windows SDK,后者是随Windows 1.0所提供的操作系统API。Windows 1.0在1985年发售,尽管在此之前已有施乐的Star、苹果的Lisa和Mac OS这样的图形界面操作系统,但Windows 1.0毕竟是第一个大规模发行的图形操作系统,需要直面各样的开发者与普通用户的问题,也算是“第一个吃螃蟹的”。既然是第一个,当然是不成熟的。这种不成熟即有当时技术条件的限制,也有经验上的匮乏,我用一个例子简要说明一下。例如,我需要显示一个窗口,Windows SDK看上去是这样的:
- int WinMain() {
-
- //1 register a window class
- WNDCLASS wndclass;
-
- wndclass.style = CS_HREDRAW | CS_VREDRAW;
- wndclass.lpfnWndProc = WndProc;
- wndclass.cbClsExtra = 0;
- wndclass.cbWndExtra = 0;
- wndclass.hInstance = NULL;
- wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
- wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
- wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
- wndclass.lpszMenuName = NULL;
- wndclass.lpszClassName = "MainWndClass";
-
- RegisterClass(&wndclass);
-
- //2 create a winow
- HWND hwnd = CreateWindow(
- "MainWndClass",
- "Window Title",
- WS_OVERLAPPEDWINDOW,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- NULL,
- NULL,
- hInstance,
- NULL);
-
- //3 message loop
- MSG msg;
- while (GetMessage(&msg, NULL, 0, 0)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- return 0;
- }
对,你没有看错,那个叫CreateWindow的函数的有11个参数!按照面向对象的观念与简洁性的做法,它不应该是这样的吗(一个Gtk的例子):
- GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-
- gtk_window_set_title(window, "window title");
- gtk_window_set_position(window, GTK_WIN_POS_CENTER_ALWAYS);
- gtk_window_set_default_size(window, 400, 300);
- gtk_window_set_resizable(window, TRUE);
原因是当时的机器内存太小,一个函数的链接符号要占有一个字长的地址空间,所以能省就省。这也是当时诸多系统API设计的惯例,如之后苹果的Carbon库,参数多到惨不忍睹的函数也是比比皆是。这就是所谓的“技术限制”的一例。
此外,上例中第一步“注册窗口类”和第三步“消息循环”有必要强制要求用户来指明各个参数项吗。从后来的实践看,答案是没有必要。但作为“第一个吃螃蟹的”,怎么会知道。这就是所谓的“经验上的匮乏”一例。
作为后起之秀的Qt,同样是显示一个窗口,用户代码就简洁得多:
- int main(int argc, char *argv[]){
-
- //1 create application instance
- QApplication app(argc, argv);
-
- //2 create a window
- QWidget *window = new QWidget;
- window->setWindowTitle("title");
- window->show();
-
- //3 message loop
- return app.exec();
- }
正由于Windows SDK的不如意,于是就出现的MFC。
这里我想插一个题外话:为什么用户终端系统(包括Windows、mac OS这样的PC端,Android、iOS这样的移动平台,也包括Play Station、xbox这样的家用主机),所提供的的API和程序库接口,编程语言都是类C(C、C++、ObjC、Java等)的。这主要是技术积累的缘故。早期探索用户终端系统的开拓者,使用的C语言,先驱们在这一语言基础上构建起各种基础设施,包括教育环境与人才储备,项目管理的流程与经验,开发、分析、测试工具,建议做法与禁忌。作为后来者当然不会舍弃这些基础设施而另起炉灶从零开始,于是使用类C语言就成了传统。可以预见:下一代的用户终端与的程序库接口,也是类C的编程语言。
我们回到MFC的话题。MFC只是对Windows SDK的封装,按后世的看法,MFC不按“套路”出牌!包括作为同时代竞争产品的Borland公司的VCL,还有稍晚的Java awt,也是不按“套路”出招!这里我需要解释下图形库构建的“套路”是什么,读者才能理解什么叫“不走套路”。
对于一个图形界面的程序,大致可以分为3个层:
- +----------------------+
- | user application |
- +----------------------+
- | ui framework |
- +----------------------+
- | operation system api |
- +----------------------+
最下面的是操作系统API,无论是UI库还是用户程序,要实现某功能最终都是要依赖于操作系统API的。关键点在于,操作系统需要提供多少数量的API函数,才足以构建一个图形界面程序。答案是:很少。
首先,操作系统需要为UI框架和用户程序提供一块屏幕区域:
struct window *window_create();
其次,这块区域需要能够接收用户事件(这里以鼠标点击为例):
- enum mouse_event {
- mouse_event_down,
- mouse_event_move,
- mouse_event_up
- };
-
- typedef void (*mouse_handler)(struct window *, mouse_event event, int x, int y);
-
- void window_set_mouse_handler(struct window *, mouse_handler handler);
最后,UI框架与用户程序需要在这块区域绘制各种控件(下例是矩形):
- struct context *context_get_form_window(struct window *);
-
- void context_set_color(struct context *, int rgba);
- void context_fill_rect(struct context *, int x, int y, int width, int height);
只要有了这3类API,一个图形界面程序就可以构建出来了,即使如今复杂的3D图像程序也是如此。这些作为基础API的函数数量很少,往大的说不到一百个,往小的说,十多个也能成事。庞大的UI框架,诸如Qt、Xamarin、WinForm,仅仅是以这几十个操作系统API函数作为自己的基础;对操作系统极小的依赖,也是可移植的保障;也是UI框架构建的“套路”。
所以说微软的MFC、Borland的VCL不走“套路”,因为它们都是对Windows SDK的封装,而不是选择少量Windows SDK核心函数并在其上构建自己的工作逻辑。因为Windows SDK使用繁琐,于是有了MFC,但MFC是对Windows SDK的直接封装,虽然提升了易用性,但终究要沿用Windows SDK所定义的工作流程。当然这种做法也是有好处的,例如工作量更小、用户可以更方便地直接调用系统API。
说穿了,MFC(当然也包括VCL)需要解决的是“有无”的问题。Windows 1.0这样的图形界面操作系统已经开始在大众领域普及,开发者也需要一套框架来开发图形界面程序。MFC在上世纪90年代取得了巨大的商业成功,但以后世程序员的眼光看,MFC还是太稚气了。我曾经接触过一些学习Windows程序开发的新人,他们对MFC充满鄙夷,认为MFC是上个世纪的遗留产物,它概念多、晦涩难懂、代码量大、界面也丑,他们更倾向于Qt、WPF这样的新秀。他们的结论是对的,MFC确实是上个世纪的遗留产物;但推导过程不对,MFC与Qt、WPF本质上是一样的东西,在MFC上遇到的问题,在使用Qt、WPF时极可能也会遇到,不过是早还是晚。只能说“MFC对新人极不友好”。
在MFC同时代,也正是上世纪80、90年代,出现了对后世UI框架设计有极大影响的技术方案:用web浏览器作为图形界面程序的容器。由于篇幅的限制,我这里直接说结论:作为一个整体性的技术方案,它失败了。失败的原因可以大致归为3点:
尽管在新世纪的第二个10年以web前端为技术基础的UI框架,如electron、reactive native,取得了喜人的成果,但与上世纪的雏形相比,它们所在的大环境与技术本质已有了根本的区别。
但失败的方案并不代表没有借鉴意义。本世纪初出现了一种被称为“direct ui”的思潮,其中一大观点就是程序启动时加载xml文件作为UI。眼亮的同仁一定会说:这不就是资源编辑器莫。对的,这就是现代意义的资源编辑器的雏形。另外也多有程序会在自身嵌入一个web容器来显示特定内容的UI,至今Android、iOS App上这样的做法也相当常见。出于篇幅的限制这里的技术因果就不展开了。
上世纪90年代末到本世纪初是UI库百花齐放的年代,如今在PC领域,为人熟知的Qt、Gtk、wxWidgets都出现在这个时间段,这是与大环境有关。
Qt是传统PC领域程序框架集大成者,除了UI,它还提供数据库、多媒体等功能,已经不是单纯的UI框架,而是一套完整的程序开发解决方案;wxWidgets就要轻量级一些;WPF则是微软官方作为取代曾经的MFC、后来的WinForm的替代方案。
我这里谈一下跨平台开发的一些经验。问:使用跨平台的程序框架开发程序,相比于每个目标平台单独组建一个开发组,前者有更高的性价比?回答是:不一定。开发程序不仅仅是程序员的事,还涉及到产品策划、美术、市场运营、第三方技术供应商等方方面面,具体问题要具体分析。事实上资金与人力充足的团队,更倾向于不同目标平台组建各自的开发组。
新世纪的第二个10年,以iOS、Android主导的移动平台兴起。Qt推出了移动平台版本,但终究没有形成气候;微软推出UWP,WPF还是被舍弃了。这又是另一个话题了。
作者:wzsayiie
链接:https://www.zhihu.com/question/23480014/answer/658825482
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。