1

I'm building an app, and I need the wisdom of the SO community on a design issue.

In my application, there needs to be EXACTLY one instance of the class UiConnectionList, UiReader and UiNotifier.

Now, I have figured two ways to do this:

Method 1: Each file has a global instance of that class in the header file itself.

Method 2: there is a separate globals.h file that contains single global instances of each class.

Example code:

Method 1

file: 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

file: uinotifier.h

#ifndef UINOTIFIER_H
#define UINOTIFIER_H

class UiNotifier
{
public:
    UiNotifier();
};

namespace Globals {
    UiNotifier uiNotifier;
}

#endif // UINOTIFIER_H

Method 2:

file: 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

file: uinotifier.h

#ifndef UINOTIFIER_H
#define UINOTIFIER_H

class UiNotifier
{
public:
    UiNotifier();
};

#endif // UINOTIFIER_H

file: globals.h

#ifndef GLOBALS_H
#define GLOBALS_H

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

namespace Globals {
    UiConnectionList connectionList;
    UiNotifier uiNotifier;
}

#endif // GLOBALS_H

My Question

What is the better/right way to do this?

PS: I don't think that singleton is the right answer here, is it?

Thanks


Okay, so two answers have told me to make instances of UiConnectionList and UiNotifier, optionally wrap it in a UiContext and pass it around wherever required.

Could someone enumerate reasons (with examples) why passing around the context is better than having globally accessible variables.

This will help me judge what method is better (or better suited for my app).

Thanks

gerry3
  • 21,212
  • 8
  • 63
  • 73
jrharshath
  • 23,967
  • 32
  • 94
  • 126
  • I think you're describing the Singleton pattern, yes. – Michael Foukarakis Sep 10 '09 at 04:51
  • I don't think so.. the singleton pattern is about ensuring that only a single instance of a class exists. I have no intention of doing that. All I want to do is a clean Global Access Mechanism. – jrharshath Sep 10 '09 at 05:45
  • Aren't there two issues here? The singleton pattern (Which may have become less favoured than in the past) is an attempt to ensure that only one instance of a class exists, whereas the OP is talking more about visibility of particular instances. Several of the answers have (Correctly, I think) advocated the passing of these instances, rather than making them global. Subjects such as control of the lifecycle of the instances and testability of the code is just as important as making the objects singletons. Passing of interfaces, rather that concrete classes should be considered – belugabob Sep 10 '09 at 07:09

7 Answers7

4

With your usage in globals.h you are going to have a multiple definition of Globals::UiConnectionList and Globals::UiNotifier for each compilation unit (.cc or .cpp file) that you use. This is not the way to make exactly one instance of those clases. You should use the singleton pattern as previous posters suggested.

If you didn't want to use the singleton pattern, the correct way is to define both clases in one compilation unit and then declare them as extern in the header file, the result is your intended one global instance of the class, but that won't prevent it from being copied or copy constructed. From your example you don't know the difference between declaration and definition.

piotr
  • 5,381
  • 1
  • 30
  • 56
2

I wouldn't do them global, instead create the three objects in your main and pass them to wherever they are needed. It is easier to follow for some other programmer because he sees when/where they are used. It gives you also better control when to create and destroy them than if you declare them global.

EDIT: to clarify normally programs get more and more complex as time goes by code being added by various developers with different ideas about design etc. In general (IMHO) once you start introducing globals in a program it encourages other programmers to do the same. That is why I prefer to have data passed to wherever it is used, in a procedural language as an argument or in an OOP language passed in via the ctor. It is then easier to see the dependencies.

AndersK
  • 33,910
  • 6
  • 56
  • 81
  • 1
    Great answer. I wish everyone did this. – SingleShot Sep 10 '09 at 05:06
  • these global objects will be created once at startup, and never destroyed. Do I still need to pass it to all classes who will need it? – jrharshath Sep 10 '09 at 05:40
  • with ref to Naveen's answer, It differs only in that all global object references are wrapped in a UiContext object, and passed around. But if no other instances of UiContext will ever be useful, is it still beneficial to pass it around? – jrharshath Sep 10 '09 at 05:41
  • (..continued) as for wrapping in a UiContext class, isn't placing the globals in a separate namespace equally helpful? especially when there is no need to create more than one instance of the context? – jrharshath Sep 10 '09 at 05:43
  • While this is a general and usual "good practice" remark, I do not think it helps here. In some cases, it makes sense to have a few global variables, when they will be used all over the code, and when you do not want to clutter the interfaces with them. Think debugging/logging, for example. – Camille Sep 10 '09 at 06:05
  • I give you -1 because a singleton must be global accessible. Following rules like "globals are bad" without thinking about the problem isn't good practise. Problems: It makes the method interface wider (extra parameter). It clutters the code with a lot of senseless variable pass through, and it rises the chance that some function will change the pointer to some other object. If you use a static variable like UiConnectionList::Connections the meaning is clear. if you have fa method like FooBar( UiConnectionList * globalConnections) what should that mean? – Thomas Maierhofer Sep 10 '09 at 07:04
  • @Thomas If you work in large software projects, especially ones that grow over time having global variables is a real pain. It may be true that the occasional global variable is fine in small programs but once your program exists for 10 years having a lot of developers working on makes the code a mess. – AndersK Sep 11 '09 at 02:02
  • @claptrap "You make a mess I'm not gonna work with you" – nurettin Dec 13 '13 at 08:27
  • @nurettin "You make a mess I'm not gonna work with you" - I am not sure where that quote came from, certainly not from my posts. I was just saying that having lots of globals gives lots of headaches so better to avoiding them but hey its a free world. – AndersK Dec 13 '13 at 08:59
  • @ThomasMaierhofer who said anything about not thinking about each problem? singletons btw suffer with similar problems as global variables and speaking *generally again* are also something to avoid - when possible. Also your argument "Problems: It makes the method interface wider (extra parameter)" is not necessarily true you can easily pass it to a class and keep a reference to it shared by all methods. – AndersK Dec 13 '13 at 09:04
  • "But what about singletons" - not an argument, but that has been beaten to death see http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons -- note the similarity to global variables. – AndersK Dec 13 '13 at 09:20
  • @claptrap You're right I just get irritated when people start bragging about their professional experience (which you just edited out) and start throwing strawmen around. I just find your way of avoiding headaches very peculiar, but yeah, free world. – nurettin Dec 13 '13 at 10:55
  • @nurettin sorry 'bout that, no intention of bragging - when I read what I wrote a second time it did sound snotty so therefore I removed it. lets just agree to disagree, peace – AndersK Dec 13 '13 at 12:33
1

First of all, you're right. This question has nothing to do with the Singleton pattern. It is a question of class design.

In this case i would prefer a different implementation than yours. In both of your examples you use a namespace called "Global". This is breaking the single concern principle, because here are a lot of objects having nothing else in common than being global accessible singletons. Instead of doing this you should encapsulate your singletons in the class declaration itself.

Look at this:

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;

};

Now your global connections can be accessed via UiConnectionList::Connections. The singleton implementation as a static variable isn't really good and should be done better. Especially to prevent the change of the pointer. But this is a different question.

Thomas Maierhofer
  • 2,585
  • 16
  • 33
  • could you ellaborate on the change of pointer problem? I've never heard of it.. maybe a link or two will help me.. – jrharshath Sep 10 '09 at 07:04
  • You shouldn't be able to change the pointer to your singleton object like: UiConnectionList::Connections = 0; This can accidentally happen in one of your functions (assigning local variable instead of comparing or whatever). This is hard to debug, especially when yo do multi-threading. A good singleton pattern prevents this, for example by declaring private UiConnectionList::connections and providing a public inline function like: UiConnectionList * Connections(){return connections; }. There are several other methods out there to implement a singleton in C++. just google "singleton C++" – Thomas Maierhofer Sep 10 '09 at 15:43
0

The least you can do is to create a UiContext class/structure. Define all other things as member variables of this class. Then create a instance of UiContext in your main class and pass it to whichever class that requires it.

Naveen
  • 69,046
  • 43
  • 164
  • 225
0

The better way to do this is to use the Singleton method. It has been tested and proven. Besides, the class would fail if I defined another UiConnectionList variable for example in my local scope.

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

Always remember when creating a singleton class. Lock (Private-tify?) the big four: Constructor, Copy Constructor, Assignment Operator, Destructor

Also, since you're using global variables, I'm assuming you don't need scoping or hiding. So the singleton method is the better way to do it.

Here's an example on implementing a singleton.

// Meyers singleton
static UiConnectionList* UiConnectionList::getSingletonPtr()
{
    static UiConnectionList x;
    return &x;
}
Roy
  • 308
  • 2
  • 11
0

Even though, as people have mentioned already, your solution is faulty, I would avoid using a singleton. Using singleton to achieve your goal will make your code hard to test. Instead classes should depend on a pure virtual interface IConnetion or the like. Would sending instances to objects when they are created be implausible? You should at least have the option to do so (or preferably using a setter) to make your code testable. Note that the "extern" solution propsed by piotr more or less is the same as a singleton.

larsmoa
  • 11,450
  • 5
  • 54
  • 82
0

A lot of people disagree on whether to use the global/singleton pattern. I personally don't like it merely because it goes against the concept of loosely coupled class design.

Each class should do one thing, and should be able to exist by itself as much as possible. Having classes use a global UiConnectionList instance is not only creating maintainability issues, but its also means that the classes have to know where they're getting their instance from when they should preferably be told what list to use when they are created.

Think of a resource and its manager.

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

In this example, the resource and its manager are very modestly coupled. The manager can find its created resources, and the resource knows which manager it was created in, but the resource is told which manager its owned by, not defining it itself (through a global manager).

The other way is to create a Resource (or a subclass) then ask a Manager to add it to its list essentially temporary coupling them, but until then they exist separately and aren't relying on pre-defined instances.

Nick Bedford
  • 4,168
  • 27
  • 34