Qt GUI 编程中的单例是邪恶的吗?

发布于 2024-10-19 02:09:11 字数 760 浏览 0 评论 0原文

我刚刚开始我的第一个相当大的 Qt 项目,该项目主要是一堆带有按钮、选项卡小部件和 Qwt 绘图的屏幕。 Qt Quarterly 27 中描述的面板堆栈模式似乎非常适合我的应用程序。我的每个屏幕都是封装在面板中的 QWidget,面板由 QStackedWidget 显示/隐藏。然而,它对每个面板使用单例模式,这样它们就不会在应用程序启动时立即全部创建,并且每个屏幕也不会创建多个。

所以我开始编码。让面板堆栈正常工作。添加了一些代码,以便动态更新的小部件不会一直动态更新。让我的历史记录堆栈/后退按钮适用于面板。一切看起来都很好,但我有一个挥之不去的担忧:

  1. 我的代码有异味。

我无法与这里和博客上发布的有关单例模式的仇恨进行争论。我想我明白了,我编写的代码确实让我对所有样板行和全局对象感到有点肮脏。但我喜欢不必担心在切换到屏幕并将其添加到历史堆栈之前我是否已经实例化了屏幕。我只是说切换到那个屏幕,它被添加到我的历史堆栈中,神奇的事情就发生了。

根据我的阅读,在某些情况下单例也是值得的。这是特殊情况之一吗?神奇的屏幕切换/历史堆栈让我觉得“是”,但是我必须创建的不同单例类的绝对数量让我觉得“不不不不不”。

我想立即弄清楚如何从我的代码中获取单例模式,这样我就不必以后再这样做了。但我不想仅仅为了摆脱我的单例类而摆脱我的所有单例类,因为它们是邪恶的[需要引用]。

任何意见都非常感谢!

I'm just starting my first fairly large Qt project which will be mostly a bunch of screens with buttons, tab widgets, and Qwt Plots. The panel stack pattern described in Qt Quarterly 27 seems pretty nice for my application. Each of my screens is a QWidget encapsulated in a Panel which is shown/hidden by a QStackedWidget. However it uses a singleton pattern for each Panel so that they aren't all created immediately as the app starts and so that more than one of each screen isn't ever created.

So I started coding. Got the panel stack working. Added some code so that dynamically updating widgets aren't dynamically updating all the time. Got my history stack/back button working for the panels. Everything seems just fine, but I've got one nagging worry:

  1. My code smells.

I am in no place to argue with any of the hate posted here and on blogs about the singleton pattern. I think I get it and the code I've written does make me feel a bit dirty with all the boilerplate lines and global objects. But I do like not having to worry about whether or not I already instantiated a screen before switching to it and adding it to my history stack. I just say switch to that screen, it's added to my history stack, and the magic works.

From what I've read there are also some cases where singletons can be worthwhile. Is this one of those special cases? The magic screen switching / history stack makes me think 'yes' but the sheer number of different singleton classes I'm going to have to create makes me think 'NO no NO NO NO'.

I want to just man up and figure out how to get the singleton pattern out of my code now so that I don't have to do it later. But I don't want to get rid of all my singleton classes just to get rid of my singleton classes because they're EVIL [citation needed].

Any input is much appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

黯然#的苍凉 2024-10-26 02:09:11

我并不是真的讨厌单身人士,但这听起来像是他们没有用的情况。我不明白那篇文章为什么有这么多单例。

首先,PanelStack 本身就是一个单例。为什么?如果那是您的主要小部件,那么只需在 main() 的堆栈上创建它,这样既干净又快速。如果它是更复杂的 UI 的一部分,则将其作为该 UI 的成员放置在那里。常规类在这里就可以了,使其成为单例只会限制其可能的用途。

那么,每个面板也是一个单例吗?此时,即使是单身恋人也应该开始感到单身的人已经太多了。这可能就是您首先问这个问题的原因。让我们看看单例在这里有什么真正的优势。好吧,我从这篇文章中可以看出的唯一优点是能够根据需要动态地延迟创建面板。这实际上是一件好事,但事实上,惰性创建和单例是不同的模式,尽管一个经常使用另一个。

为什么不将所有这些面板放入某个通用容器中呢?在这种情况下,PanelStack 看起来是一个完美的候选者。毕竟这是存放面板的地方。让我们在 PanelStack 中创建一堆方法,而不是一堆单例:

class PanelStack : public QWidget
{
  Q_OBJECT

public:
  int addPanel(AbstractPanel *);
  void showPanel(int);
  RecordingsPanel *getRecordingsPanel();
  ReecrdingDetailsPanel *getRecordingDetailsPanel();

private:
  ...
};

等等。这些 get*Panel() 方法仍然可以根据需要延迟创建面板。现在,它本质上与拥有一堆单例是一样的,还添加了一些优点:

  • 如果我们将面板作为堆栈的子项,那么当删除堆栈时它们会自动删除。无需担心内存管理,这对于单例来说始终是一个痛苦。
  • 您甚至可以在 PanelStack 中实现某种“垃圾收集器”,删除一段时间未使用的面板。或者当达到某种“最大活动面板”限制时。

现在,我能想到的唯一缺点是我们现在在堆栈和面板之间存在依赖关系。但更糟糕的是,将实例存储在一个类中,引入依赖关系,还是全局存储它们?如果您认为堆栈应该独立于面板,这听起来很合理,那么我们可能只需要另一个类来放入所有这些东西。它可以是 QApplication 的子类,或者只是一些随机的“UI 管理器”类,但是将所有内容存储在一个地方仍然比将所有内容存储在全局要好。

这里使用单例只会破坏封装并限制整个 UI 的可能使用。如果我们想要两个带有这些面板的窗口怎么办?或者多个选项卡(想想网络浏览器)?单身人士会咬得很紧。只有当实例在许多不相关的类中广泛访问时(想想数据库连接、记录器、池和其他典型的单例用途),它们才真正有用。它们在 UI 中几乎毫无用处,因为在 UI 中,几乎总是很明显“这个东西属于那里,可能不属于其他地方”。

I don't really hate singletons, but this sounds like a case with no use for them. I don't understand why there are so many singletons in that article.

First, the PanelStack is a singleton by itself. Why? If that's your main widget, then just create it on the stack in the main(), which is both cleaner and faster. If it is a part of a more complicated UI, then put it there as a member of that UI. A regular class is just fine here, making it singleton only limits its possible uses.

Then, each panel is also a singleton? At this point even singleton lovers should begin to feel that there are too many of them already. Which is probably why you are asking this question in the first place. Let's see what real advantages singletons give here. Well, about the only advantage I can figure out from that article is the ability of lazily creating panels on the fly as they are needed. This is actually a good thing, but in fact, lazy creation and singletons are different patterns, although one often uses the other.

Why not just put all those panels in some common container instead? In this case, the PanelStack looks like a perfect candidate for it. It is the very place where panels are stored after all. Instead of a bunch of singletons, let's create a bunch of methods in the PanelStack:

class PanelStack : public QWidget
{
  Q_OBJECT

public:
  int addPanel(AbstractPanel *);
  void showPanel(int);
  RecordingsPanel *getRecordingsPanel();
  ReecrdingDetailsPanel *getRecordingDetailsPanel();

private:
  ...
};

And so on. These get*Panel() methods can still create panels lazily as needed. Now, it's essentially the same thing as having a bunch of singletons, with some advantages added:

  • If we make panels children of the stack, they are automatically deleted when the stack is deleted. No need to worry about memory management which is always a pain with singletons.
  • You could even implement some sort of "garbage collector" in the PanelStack that deletes panels that haven't been used for some time. Or when some sort of "max active panels" limit is reached.

Now, the only disadvantage I can think of is that we have a dependency between the stack and the panels now. But what's worse, to store instances in one class, introducing a dependency, or to store them globally? If you think that the stack should be independent from panels, which does sound reasonable, then we probably just need another class to put all those things in. It could be a subclass of QApplication, or just some random "UI manager" class, but it is still better to store everything in one place than to store everything globally.

Using singletons here only breaks encapsulation and limits the possible uses of the whole UI. What if we want to have two windows with those panels? Or multiple tabs (think web browser)? Singletons will bite hard. And they are only really useful when the instance is accessed widely across many unrelated classes (think DB connections, loggers, pools and other typical singleton uses). They are mostly useless in an UI because with UI it is almost always obvious that "this thing belongs there, and probably nowhere else".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文