暴露类的内部组件,以防止编写过多代码并影响性能

Posted

技术标签:

【中文标题】暴露类的内部组件,以防止编写过多代码并影响性能【英文标题】:Exposing internal component of a class in order to prevent writing too much code and impact on performance 【发布时间】:2017-02-17 13:49:14 【问题描述】:

假设我有一堂课

class B : public class QObject 
  Q_OBJECT
public:
  B(QObject* parent=Q_NULLPTR);
signals:
   void signalData(int data);
public slots:
  void slotGetData();
private:

slotGetData() 是外部触发的,基本上从某处检索一些数据并使用signalData(int data) 将其发回。另一方面,我还有另一门课

class A : public class QObject 
  Q_OBJECT
public:
  A(QObject* parent=Q_NULLPTR) 
    // Init B, move to thread, setup timer, connect timer's timeout to B's slotGetData()

    // Connect B to A
    connect(this->B, SIGNAL(signalData(int)), this, SLOT(slotGetData(int)));
  
signals:
  // Emit signal containing data to another Qt component
  void signalData(int x);
private slots:
  // Connect B::signalData(int x) to this slot and re-emit the data using A::signalData(int x). Don't do anything with the data!
  void slotGetData(int x);
private:
  B* workerObj;
  QThread worker;
  QTimer workerTimer;

它主要负责实例化workerObj,将其移动到worker线程并将workerTimertimeout()信号连接到B::slotGetData()

这个类的目的是使B 能够在想要使用它的第三方类中正确集成(多线程),例如:

class ThirdParty : public class QWidget 
  Q_OBJECT
public:
  ThirdParty(QObject* parent=Q_NULLPTR) 
    // Init A

    // Connect to B through A
    connect(this->integrationObj, SIGNAL(signalData(int)), this, SLOT(slotGetData(int)));
  
private slots:
  // Connect A::signalData(int x) to this slot and do something with the x (change UI, whatever)
  void slotGetData(int x);
private:
  A* integrationObj;

ThirdParty 类通过A 间接访问B 的特定功能。

现在我面临的困境如下:

我应该只是将来自B 的数据信号传递到A 并将其作为A 的信号公开还是 我是否应该只返回一个constB 的引用以允许其中包含A 的类(以便它可以使用B)直接连接到BsignalData(int x)

在第一种情况下(我有),我基本上必须镜像B 想要在A 内向外部提供的每个信号(通过在A 中提供相应的专用插槽以及基本上是与B 相同)。不用说,这会导致有太多相同的东西,并且也会有一些(即使只是轻微的)性能影响,因为我得到 2 个信号发射(从BA,然后从 A 到任何其他对象有A) 和2 个槽调用(一个在A 中从B 获取信号,一个在任何其他具有A 的对象中从A 获取信号)。

第二种情况看起来不错,但我担心我会暴露B 的特性,包含A 的类可能不允许访问

如果实施第二种情况,我会有这样的事情(B 不会改变):

class A : public class QObject 
  Q_OBJECT
public:
  A(QObject* parent=Q_NULLPTR);
  const B* getB() const; // Return a reference to B that cannot be changed but can be used to expose B's slots and signals
signals:
private slots:
private:
  B* workerObj;
  QThread worker;
  QTimer workerTimer;


class ThirdParty : public class QWidget 
  Q_OBJECT
public:
  ThirdParty(QObject* parent=Q_NULLPTR) 
    // Init A

    // Connect to B directly!
    connect(this->A->getB(), SIGNAL(signalData(int)), this, SLOT(slotGetData(int)));
  
private slots:
  // Connect B::signalData(int x) to this slot and do something with the x (change UI, whatever)
  void slotGetData(int x);
private:
  A* integrationObj;

我应该在这里做什么?遗憾的是没有私人信号,因此所有B 信号及其所有公共插槽都将被公开。如果要使用所有这些,那么这种暴露程度就没有问题,但除此之外……没有那么多。

【问题讨论】:

【参考方案1】:

facepalm 我已经在我的另一个项目中找到了解决方案。 :D 我只需要在A 内部提供一个connectTo(QObject* thirdparty),它在内部将thirdparty 插槽仅连接到B 的特定信号,因此不会暴露B 中不应该暴露的东西!

另一个更好的解决方案是使用信号到信号连接 (connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type)) 或使用子-父关系,尽管在后者中我必须确保实际上设置了父级。这也称为信号转发,需要 2 个信号但不需要额外的时隙。由于AThirdParty 可见,我可以做(this 这里代表ThirdPartyconnect(this, SIGNAL(signalFromThirdPartyToB()), this->a, SIGNAL(signalFromAToB()))ThirdParty 信号的转发设置为BA,然后执行实际信号-通过调用A中的转发信号到B中的插槽之间的插槽连接(this这里代表Aconnect(this, SIGNAL(signalFromAToB()), this->b, SLOT(slotB()))

【讨论】:

信号槽系统是一种解耦类的方法。像你一样使用它来紧密耦合类似乎违反直觉。一般来说,如果 A 类和 B 类可以互操作但可以解耦,则将它们解耦并让其用户酌情组合/耦合它们。如果您碰巧重用它们,您当然可以使用辅助函数来分解 A 和 B 之间的常见耦合模式。理想情况下,A 不应该知道 B 的类型,反之亦然,它们应该可以单独构建。 AB 紧密耦合,除了一组非常有限的功能。 A 是使B 生存的一件事,基本上负责ThirdParty 实际获取任何数据。 B 提供的许多功能实际上不是插槽,而只是应该只有 A 才能访问的普通功能。

以上是关于暴露类的内部组件,以防止编写过多代码并影响性能的主要内容,如果未能解决你的问题,请参考以下文章

React 限制渲染次数以防止无限循环...重新渲染次数过多

防止 Grails Controller 中的方法暴露为操作

为啥要修改nginx用户和组

收到此错误:错误:重新渲染过多。 React 限制渲染次数以防止无限循环

错误:重新渲染过多。 React 限制了渲染的数量以防止无限循环。即使使用箭头函数

React(性能):如何防止每次状态更改时出现不必要的渲染? [复制]