在制作GUI时清理OOP设计
Clean OOP design when making a GUI
假设我有两个主要类,Application
和ApplicationGUI
。Application
做了很多事情,可以在不知道ApplicationGUI
存在的情况下快乐地运行。ApplicationGUI
和Application
有很多联系,它可能有50或100个不同的旋钮可以改变Application
的行为。
ApplicationGUI
是一种分层结构,因此它具有许多ControlGroup
的实例,每个实例包含任意数量的Button
s和Knob
s,或者甚至另一个ControlGroup
。
当前设计:在实例化ApplicationGUI
时(Application
已经使用一些默认参数运行),我将Application
的参数指针传递到GUI的各个组件。例如:
my_gui.sound_controlgroup.knob.link_to_param(&(my_application.volume));
如果我需要做一些更复杂的事情,比如调用Application
、my_application.update_something()
的成员函数,这是如何做到的?
简单的答案是将指向my_application的指针传递给my_gui.sound_controlgroup.knob
,但如果我只需要调用my_applications的一个函数,那么我似乎给了我的旋钮一个选项,可以更改它应该知道的所有事情(例如my_application.update_something_unrelated()
)。在这种情况下,最干净的做法是什么?
此外,这需要将ApplicationGUI
的所有子组件公开,或者在层次结构的每个阶段都有一个函数将指针转发到底层。这导致了相当多的功能。这是一个有很多旋钮的用户界面的必然结果吗?
快速简短回答
为了实现非GUI相关的Application
对象和GUIApplication
对象之间的交互,我建议应用"属性和方法以及事件处理程序"范式。
扩展复杂答案
G.U.I.的发展是O.O.p.理论最实际的实施之一。
什么是"属性和方法以及事件处理程序"范式?
这意味着构建,无论是非GUI类还是GUI类,都应该具有:
- 属性
- 方法
- 事件处理程序
"事件"(处理程序)也称为"信号",用函数指针实现。不确定,但是,我认为你的"旋钮"就像事件处理程序。
这是一种应用问题中的my_application.update_something_unrelated()
的技术。
由于C++和Java一样没有属性语法,所以可以使用"getter"answers"setter"方法,或者使用"property"模板。
例如,如果您的应用程序具有Close
方法,则可以声明类似以下示例的内容。
注意:它们不是完整的程序,只是一个想法:
// Applications.hpp
public class BaseApplicationClass
{
// ...
};
public class BaseApplicationClientClass
{
// ...
};
typedef
void (BaseApplicationClientClass::*CloseFunctor)
(BaseApplicationClass App);
public class ApplicationClass: public BaseApplicationClass
{
// ...
public:
Vector<BaseApplicationClientClass::CloseFunctor>
BeforeCloseEventHandlers;
Vector<BaseApplicationClientClass::CloseFunctor>
AfterCloseEventHandlers;
protected:
void ConfirmedClose();
public:
virtual void Close();
} Application;
// Applications.cpp
void ApplicationClass::ConfirmedClose()
{
// do close app. without releasing from memory yet.
} // void ApplicationClass::ConfirmedClose()
void ApplicationClass::Close()
{
// Execute all handlers in "BeforeCloseEventaHandlers"
this.ConfirmedClose();
// Execute all handlers in "AfterCloseEventaHandlers"
} // void ApplicationClass::Close()
// AppShells.cpp
public class AppShell: public BaseApplicationClientClass
{
// ...
};
void AppShell::CloseHandler(ApplicationClass App)
{
// close GUI
} // void AppShell.CloseHandler(ApplicationClass App)
void AppShell::setApp(ApplicationClass App)
{
App->BeforeCloseEventHandlers->add(&this.CloseHandler);
} // void AppShell.setApp(ApplicationClass App)
void main (...)
{
ApplicationClass* AppKernel = new ApplicationClass();
ApplicationGUIClass* AppShell = new ApplicationGUIClass();
AppShell.setApp(App);
// this executes "App->Run();"
AppShell->Run();
free AppShell();
free AppKernel();
}
UPDATE:修复了从全局函数指针(也称为"全局函子")到对象函数指针(又称为"方法函子")的类型声明。
干杯。
你知道模型-视图-控制器(MVC)范式吗?将Application
类视为模型,将GUI控件的整个层次视为视图,将ApplicationGUI
类视为控制器。您不希望Application
了解控件,也不希望控件了解Application
;它们都应当仅与控制器ApplicationGUI
进行通信。
使用ApplicationGUI
作为控件和Application
之间通信的管道意味着您可以通过用mock对象替换Application
或控件来测试另一个。更重要的是,您可以在不影响其他控件的情况下更改控件或Application
。单个控件不需要知道任何关于Application
的信息——它们只需要知道当值发生变化时将其发送到哪里。Application
不应该关心输入是来自旋钮、滑块还是文本字段。将这两个领域分开将简化每一个领域。
此外,这要么需要将ApplicationGUI公共或在层次结构,将指针转发到底层。这导致相当多的功能。这是带有很多旋钮?
给定的控件不应该关心它管理的值。它不需要知道这个值是决定屏幕上外星入侵者的数量还是决定核反应堆中的冷却剂液位。它确实需要知道诸如最小值和最大值、要显示的标签、要使用的比例(线性、日志等)以及其他直接影响控件工作方式的事情。它还需要知道当事情发生变化时该告诉谁,并且可能需要某种方式来识别自己。
考虑到这一点,ApplicationGUI
不需要为Application
的每个可能的参数公开访问者。相反,它应该有一个通用方法,允许控件向它发送更新。当控件发生更改时,它应该向ApplicationGUI
发送一条包含新值及其标识符的消息,ApplicationGUI
负责将该标识符映射到Application
的某个特定参数。控件的标识符可以是给定给它的某个标识号,也可以只是指向该控件的指针。
当然,有时沟通也必须走另一条路。。。GUI通常既有输入又有输出,因此ApplicationGUI
需要一些方法来从Application
获取更新并更新GUI的状态。出于与上述相同的原因,Application
应该将这些更新发送到ApplicationGUI
,并让后者找到需要更改的实际UI组件。
- OOP设计:一个对象依赖于其所有依赖项的存在
- 我是否违反了OOP设计准则?两个有趣的设计泡菜
- 在制作GUI时清理OOP设计
- 使用 OOP 进行状态模式设计
- 在进行 OOP 状态机设计时如何避免单例
- OOP:没有静态成员或完全重新设计的类
- OOP设计-受保护的成员不在自己的类中使用,但仅在子类中使用
- OOP 设计 - 将类成员变量作为类中的方法参数传递
- 需要有关在类之间共享变量的 OOP 设计的帮助,这些变量单独使用计时器运行
- OOP 设计 - 处理整个 Foo 对象序列
- 使用 C++ 的 OOP 设计模式
- 逻辑矛盾是否在类设计中背叛了OOP
- 数据库的最佳OOP设计
- 用c++ OOP设计更新总价值
- c++ - OOP设计-多重继承和纯虚方法
- 正确的oop设计,如果我想调用const引用的非const函数
- OOP设计——从泛型容器继承
- OOP:设计一个树,在Node类和tree类之间划分功能
- c++ OOP 设计 - 将数据成员传递给其他类 - 是否合理
- OOP 设计问题:包含对象或容器中的责任