views:

309

answers:

7

Hi,

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

A: 

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
+2  A: 

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, I have seen a lot of code in my days and normally programs as time goes by get more and more code added to them 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.

Maybe this was answer was off to mark and became too general, sorry 'bout that.

Anders K.
Great answer. I wish everyone did this.
SingleShot
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?
Here Be Wolves
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?
Here Be Wolves
(..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?
Here Be Wolves
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
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
@ThomasIf 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.
Anders K.
A: 

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;
}

Cheers, Roy!

Roy
@Roy: I don't want to *enforce* single instance. the problem is of global access, not single instance.
Here Be Wolves
Singleton is just a glorified global. It's pretty much an equally poor solution.
Jani Hartikainen
+4  A: 

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
you are right. I missed the fine distinction between declaration and definition
Here Be Wolves
+1  A: 

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
could you ellaborate on the change of pointer problem? I've never heard of it.. maybe a link or two will help me..
Here Be Wolves
btw, +1 :)
Here Be Wolves
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
A: 

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.

larsm
A: 

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