如何解决全球访问问题?

时间:2009-09-10 04:45:08

标签: c++ design-patterns global-variables

我正在构建一个应用程序,在设计问题上我需要SO社区的智慧。

在我的应用程序中,需要有一个类UiConnectionListUiReaderUiNotifier的实例。

现在,我已经想出了两种方法:

方法1: 每个文件在头文件本身中都有该类的全局实例。

方法2:有一个单独的globals.h文件,其中包含每个类的单个全局实例。

示例代码:

方法1

档案:uiconnectionlist.h

#ifndef UICONNECTIONLIST_H
#define UICONNECTIONLIST_H

#include <QObject>
#include <QList>

class UiConnection;

class UiConnectionList : public QObject
{
    Q_OBJECT
public:
    UiConnectionList();

    void addConnection(UiConnection* conn);
    void removeConnection(UiConnection* conn);
private:
    QList<UiConnection*> connList;
};

namespace Globals {
    UiConnectionList connectionList;
}

#endif // UICONNECTIONLIST_H

档案:uinotifier.h

#ifndef UINOTIFIER_H
#define UINOTIFIER_H

class UiNotifier
{
public:
    UiNotifier();
};

namespace Globals {
    UiNotifier uiNotifier;
}

#endif // UINOTIFIER_H

方法2:

档案:uiconnectionlist.h

#ifndef UICONNECTIONLIST_H
#define UICONNECTIONLIST_H

#include <QObject>
#include <QList>

class UiConnection;

class UiConnectionList : public QObject
{
    Q_OBJECT
public:
    UiConnectionList();

    void addConnection(UiConnection* conn);
    void removeConnection(UiConnection* conn);
private:
    QList<UiConnection*> connList;
};

#endif // UICONNECTIONLIST_H

档案:uinotifier.h

#ifndef UINOTIFIER_H
#define UINOTIFIER_H

class UiNotifier
{
public:
    UiNotifier();
};

#endif // UINOTIFIER_H

档案:globals.h

#ifndef GLOBALS_H
#define GLOBALS_H

#include "uiconnectionlist.h"
#include "uinotifier.h"

namespace Globals {
    UiConnectionList connectionList;
    UiNotifier uiNotifier;
}

#endif // GLOBALS_H

我的问题

这样做的更好/正确的方法是什么?

PS:我认为单身人士在这里不是正确的答案,是吗?

由于


好的,所以有两个答案告诉我要创建UiConnectionListUiNotifier的实例,可选择将其包装在UiContext中并在需要的地方传递它。

有人可以列举原因(带示例)为什么绕过上下文比拥有全局可访问变量更好。

这将帮助我判断哪种方法更好(或更适合我的应用)。

由于

7 个答案:

答案 0 :(得分:4)

根据您在globals.h中的用法,您将为您使用的每个编译单元(.cc或.cpp文件)定义Globals :: UiConnectionList和Globals :: UiNotifier的多重定义。这是的方式来制作这些clases的一个实例。您应该使用单例模式作为之前的海报建议。

如果你不想使用单例模式,正确的方法是在一个编译单元中定义两个clases然后声明它们为 extern 在头文件中,结果是您预期的一个类的全局实例,但这不会阻止它被复制或复制构造。从您的示例中,您不知道声明和定义之间的区别。

答案 1 :(得分:2)

我不会将它们全局化,而是在主体中创建三个对象并将它们传递到需要它们的任何地方。对于其他程序员来说,更容易理解,因为他看到了他们使用的时间/地点。它还可以让您更好地控制何时创建和销毁它们,而不是将它们声明为全局。

编辑:为了澄清通常程序变得越来越复杂随着时间的推移,各种开发人员添加的代码对设计有不同的想法等。一般情况下(恕我直言)一旦你开始在程序中引入全局变量,就会鼓励其他程序员去做相同。这就是为什么我更喜欢将数据传递到使用它的任何地方,以过程语言作为参数或通过ctor传入的OOP语言。然后更容易看到依赖关系。

答案 2 :(得分:1)

首先,你是对的。这个问题与Singleton模式无关。这是课堂设计的问题。

在这种情况下,我宁愿采用与您不同的实现方式。在两个示例中,您都使用名为“Global”的命名空间。这打破了单一关注原则,因为这里有许多对象没有其他共同点,而不是全局可访问的单例。你应该将你的单身人士封装在课堂宣言中,而不是这样做。

看看这个:

class UiConnectionList : public QObject
{
    Q_OBJECT

public:
    static UiConnectionList Connections; // This is your global varaible

public:
    UiConnectionList();

    void addConnection(UiConnection* conn);
    void removeConnection(UiConnection* conn);
private:
    QList<UiConnection*> connList;

};

现在可以通过UiConnectionList :: Connections访问您的全局连接。作为静态变量的单例实现并不是很好,应该做得更好。特别是为了防止指针的变化。但这是一个不同的问题。

答案 3 :(得分:0)

您可以做的最少是创建一个UiContext类/结构。将所有其他内容定义为此类的成员变量。然后在主类中创建一个UiContext实例,并将其传递给需要它的任何类。

答案 4 :(得分:0)

更好的方法是使用Singleton方法。它已经过测试和验证。此外,如果我在本地范围内定义了另一个UiConnectionList变量,则该类将失败。

void myfunction()
{
    UiConnectionList connectionList;
    // Any usage to connectionList would be cleared after this function exits.
}

在创建单例类时始终记住。 Lock(Private-tify?)四大:构造函数,复制构造函数,赋值运算符,析构函数

此外,由于您正在使用全局变量,我假设您不需要确定范围或隐藏。因此,单例方法是更好的方法。

以下是实施单身人士的示例。

// Meyers singleton
static UiConnectionList* UiConnectionList::getSingletonPtr()
{
    static UiConnectionList x;
    return &x;
}

答案 5 :(得分:0)

尽管人们已经提到过,但是你的解决方案有问题,我会避免使用单例。使用单例来实现目标将使您的代码难以测试。相反,类应该依赖于纯虚拟接口IConnetion等。将实例发送到对象时是不可信的?您应该至少可以选择(或者最好使用setter)来使您的代码可测试。请注意,piotr或多或少地提出的“extern”解决方案与单身人士相同。

答案 6 :(得分:0)

很多人不同意是否使用全局/单身模式。我个人不喜欢它只是因为它违背了松散耦合的类设计的概念。

每个班级应该做一件事,并且应该尽可能地自己存在。让类使用全局UiConnectionList实例不仅会产生可维护性问题,而且还意味着类必须知道他们从哪里获取实例,最好在创建它们时告诉他们使用什么列表。

想想资源及其经理。

Resource *ResouceManager::createResource (string name)
{
    Resource *res = new Resource(this);
    res->SetName(name);
    resourceList->Add(res);
    return res;
}

在此示例中,资源及其管理器非常适度耦合。经理可以找到其创建的资源,资源知道它在哪个经理中创建,但是资源被告知它所拥有的经理,而不是自己定义(通过全局经理)。

另一种方法是创建一个Resource(或子类),然后要求Manager将其添加到其列表中,基本上是临时耦合它们,但在此之前它们是分开存在的,并且不依赖于预定义的实例。