So I have starting to learn Qt 4.5 and found the Signal/Slot mechanism to be of help. However, now I find myself to be considering two types of architecture.
所以我开始学习qt4.5,发现信号/插槽机制对我有帮助。然而,现在我发现自己正在考虑两种类型的架构。
This is the one I would use
这是我要用的。
class IDataBlock
{
public:
virtual void updateBlock(std::string& someData) = 0;
}
class Updater
{
private:
void updateData(IDataBlock &someblock)
{
....
someblock.updateBlock(data);
....
}
}
Note: code inlined for brevity.
注:代码为简短。
Now with signals I could just
有了信号,我可以
void Updater::updateData()
{
...
emit updatedData(data);
}
This is cleaner, reduces the need of an interface, but should I do it just because I could? The first block of code requires more typing and more classes, but it shows a relationship. With the second block of code, everything is more "formless". Which one is more desirable, and if it is a case-by-case basis, what are the guidelines?
这更简洁,减少了对接口的需要,但我是否应该仅仅因为我能做到就去做呢?第一个代码块需要更多的输入和更多的类,但是它显示了一种关系。对于第二个代码块,一切都更“无格式”。哪一种更可取,如果是个案基础,指导方针是什么?
3 个解决方案
#1
9
Emmitting a signal costs few switches and some additional function calls (depending on what and how is connected), but overhead should be minimal.
授予一个信号需要花费很少的开关和一些额外的函数调用(取决于连接的内容和方式),但是开销应该是最小的。
Provider of a signal has no control over who its clients are and even if they all actually got the signal by the time emit returns.
信号的提供者无法控制它的客户是谁,即使他们在发出返回时都得到了信号。
This is very convenient and allows complete decoupling, but can also lead to problems when order of execution matters or when you want to return something.
这非常方便,并且允许完全解耦,但是当执行顺序有问题时,或者当您想返回一些东西时,也会导致问题。
Never pass in pointers to temporary data (unless you know exactly what you are doing and even then...). If you must, pass address of your member variable -- Qt provides a way to delay destruction of object untill after all events for it are processed.
永远不要将指针传递给临时数据(除非您确切地知道您正在做什么,即使是这样…)。如果必须,则传递成员变量的地址——Qt提供了一种方法,可以将对象的销毁延迟到处理完所有事件之后。
Signals also might requre event loop to be running (unless connection is direct I think).
信号也可能重新启动事件循环以运行(除非连接是直接的)。
Overall they make a lot of sense in event driven applications (actually it quickly becomes very annoying without them).
总的来说,它们在事件驱动的应用程序中很有意义(实际上,没有它们很快就会变得非常烦人)。
If you already using Qt in a project, definitely use them. If dependency on Qt is unacceptable, boost has a similar mechanism.
如果您已经在项目中使用了Qt,一定要使用它们。如果对Qt的依赖是不可接受的,boost也有类似的机制。
#2
3
There is another difference. #1 is hard coupled to the IDataBlock interface, and the Updater class needs to know about "someblock". #2 can be late-coupled via a connect call (or several, including disconnects), which leads to a more dynamic approach. #2 acts like a message (think Smalltalk/ObjC) and not a call (think C/C++). Messages can also be subject to multiple dispatch, which requires hand implementing that feature in #1.
还有另一个不同之处。#1很难与IDataBlock接口耦合,Updater类需要了解“someblock”。#2可以通过一个连接调用(或多个连接,包括断开连接)延迟耦合,这将导致更动态的方法。#2的行为就像一条消息(想想Smalltalk/ObjC),而不是一个调用(想想C/ c++)。消息还可能受到多个分派的影响,这需要手工实现#1中的那个特性。
My preference would be to utilize signals/slots due to their flexibility, unless code performance or the need for immediate return data does not allow for it (or the dependence on Qt is not desirable).
由于信号/插槽的灵活性,我倾向于使用它们,除非代码性能或即时返回数据的需求不允许使用它们(或者不希望依赖Qt)。
#3
2
The two forms may appear to be similar. Functionally, that is true. In practice, you are solving a larger problem. In those cases, external circumstances will cause these two soltuions to be not equivalent.
这两种形式似乎是相似的。功能,这是真的。实际上,你正在解决一个更大的问题。在这种情况下,外部环境会导致这两个结果不相等。
A common case is figuring out the relation between source and sink. Do they even know each other? In your first example, updateData() needs to have the sink passed in. But what if the trigger is a GUI button [Update Data] ? Pushbuttons are generic components and shouldn't know about IDataBlock.
一个常见的情况是找出源和汇之间的关系。他们甚至认识彼此吗?在第一个示例中,updateData()需要传入sink。但是如果触发器是GUI按钮[更新数据]呢?按钮是通用组件,不应该知道IDataBlock。
A solution is of course to add a m_someblock member to Updater. The pushbutton would now update whatever member is in Updater. But is this really what you intended?
解决方案当然是向Updater添加一个m_someblock成员。按钮现在将更新更新更新器中的任何成员。但这真的是你想要的吗?
#1
9
Emmitting a signal costs few switches and some additional function calls (depending on what and how is connected), but overhead should be minimal.
授予一个信号需要花费很少的开关和一些额外的函数调用(取决于连接的内容和方式),但是开销应该是最小的。
Provider of a signal has no control over who its clients are and even if they all actually got the signal by the time emit returns.
信号的提供者无法控制它的客户是谁,即使他们在发出返回时都得到了信号。
This is very convenient and allows complete decoupling, but can also lead to problems when order of execution matters or when you want to return something.
这非常方便,并且允许完全解耦,但是当执行顺序有问题时,或者当您想返回一些东西时,也会导致问题。
Never pass in pointers to temporary data (unless you know exactly what you are doing and even then...). If you must, pass address of your member variable -- Qt provides a way to delay destruction of object untill after all events for it are processed.
永远不要将指针传递给临时数据(除非您确切地知道您正在做什么,即使是这样…)。如果必须,则传递成员变量的地址——Qt提供了一种方法,可以将对象的销毁延迟到处理完所有事件之后。
Signals also might requre event loop to be running (unless connection is direct I think).
信号也可能重新启动事件循环以运行(除非连接是直接的)。
Overall they make a lot of sense in event driven applications (actually it quickly becomes very annoying without them).
总的来说,它们在事件驱动的应用程序中很有意义(实际上,没有它们很快就会变得非常烦人)。
If you already using Qt in a project, definitely use them. If dependency on Qt is unacceptable, boost has a similar mechanism.
如果您已经在项目中使用了Qt,一定要使用它们。如果对Qt的依赖是不可接受的,boost也有类似的机制。
#2
3
There is another difference. #1 is hard coupled to the IDataBlock interface, and the Updater class needs to know about "someblock". #2 can be late-coupled via a connect call (or several, including disconnects), which leads to a more dynamic approach. #2 acts like a message (think Smalltalk/ObjC) and not a call (think C/C++). Messages can also be subject to multiple dispatch, which requires hand implementing that feature in #1.
还有另一个不同之处。#1很难与IDataBlock接口耦合,Updater类需要了解“someblock”。#2可以通过一个连接调用(或多个连接,包括断开连接)延迟耦合,这将导致更动态的方法。#2的行为就像一条消息(想想Smalltalk/ObjC),而不是一个调用(想想C/ c++)。消息还可能受到多个分派的影响,这需要手工实现#1中的那个特性。
My preference would be to utilize signals/slots due to their flexibility, unless code performance or the need for immediate return data does not allow for it (or the dependence on Qt is not desirable).
由于信号/插槽的灵活性,我倾向于使用它们,除非代码性能或即时返回数据的需求不允许使用它们(或者不希望依赖Qt)。
#3
2
The two forms may appear to be similar. Functionally, that is true. In practice, you are solving a larger problem. In those cases, external circumstances will cause these two soltuions to be not equivalent.
这两种形式似乎是相似的。功能,这是真的。实际上,你正在解决一个更大的问题。在这种情况下,外部环境会导致这两个结果不相等。
A common case is figuring out the relation between source and sink. Do they even know each other? In your first example, updateData() needs to have the sink passed in. But what if the trigger is a GUI button [Update Data] ? Pushbuttons are generic components and shouldn't know about IDataBlock.
一个常见的情况是找出源和汇之间的关系。他们甚至认识彼此吗?在第一个示例中,updateData()需要传入sink。但是如果触发器是GUI按钮[更新数据]呢?按钮是通用组件,不应该知道IDataBlock。
A solution is of course to add a m_someblock member to Updater. The pushbutton would now update whatever member is in Updater. But is this really what you intended?
解决方案当然是向Updater添加一个m_someblock成员。按钮现在将更新更新更新器中的任何成员。但这真的是你想要的吗?