Как решить проблему глобального доступа? - PullRequest
1 голос
/ 10 сентября 2009

Я создаю приложение, и мне нужна мудрость SO-сообщества в вопросе дизайна.

В моем приложении должен быть ТОЛЬКО один экземпляр класса UiConnectionList, UiReader и UiNotifier.

Теперь я нашел два способа сделать это:

Метод 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: Я не думаю, что синглтон - правильный ответ, не так ли?

Спасибо


Хорошо, поэтому два ответа сказали мне сделать экземпляры UiConnectionList и UiNotifier, при желании обернуть их в UiContext и передать их везде, где требуется.

Может ли кто-нибудь перечислить причины (с примерами), почему лучше обходить контекст, чем наличие глобально доступных переменных.

Это поможет мне определить, какой метод лучше (или лучше подходит для моего приложения).

Спасибо

Ответы [ 7 ]

4 голосов
/ 10 сентября 2009

При использовании в globals.h у вас будет несколько определений Globals :: UiConnectionList и Globals :: UiNotifier для каждого используемого вами модуля компиляции (файл .cc или .cpp). Это не способ сделать ровно один экземпляр этих предложений. Вы должны использовать шаблон синглтона, как предлагалось ранее.

Если вы не хотите использовать шаблон синглтона, правильный способ - определить оба предложения в одной единице компиляции, а затем объявить их как extern в заголовочном файле результатом является один ваш глобальный экземпляр класса, но это не помешает его копированию или созданию. Из вашего примера вы не знаете разницу между объявлением и определением.

2 голосов
/ 10 сентября 2009

Я бы не делал их глобальными, вместо этого создавал бы три объекта в вашем главном и передавал их туда, где они нужны. За другим программистом легче следовать, потому что он видит, когда и где они используются. Это также дает вам лучший контроль над тем, когда создавать и уничтожать их, чем если вы объявляете их глобальными.

РЕДАКТИРОВАТЬ: для ясности, как правило, программы становятся все более и более сложными, поскольку время идет за счет добавления кода различными разработчиками с различными идеями о дизайне и т. Д. В целом (ИМХО), когда вы начинаете вводить глобальные переменные в программу, это поощряет других программистов тот же самый. Вот почему я предпочитаю, чтобы данные передавались туда, где они используются, на процедурном языке в качестве аргумента или на языке ООП, передаваемом через ctor. Тогда легче увидеть зависимости.

1 голос
/ 10 сентября 2009

Прежде всего, вы правы. Этот вопрос не имеет ничего общего с паттерном Синглтона. Это вопрос классового дизайна.

В этом случае я бы предпочел реализацию, отличную от вашей. В обоих ваших примерах вы используете пространство имен под названием «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. Синглтонная реализация как статическая переменная не очень хороша и должна быть сделана лучше. Особенно, чтобы предотвратить изменение указателя. Но это другой вопрос.

0 голосов
/ 11 сентября 2009

Многие люди не согласны с тем, использовать ли шаблон global / singleton. Лично мне это не нравится только потому, что это противоречит концепции слабо связанных классов.

Каждый класс должен делать что-то одно и должен иметь возможность существовать как можно больше сам по себе. Использование классами глобального экземпляра UiConnectionList не только создает проблемы сопровождения, но также означает, что классы должны знать, откуда они берут свой экземпляр, когда им предпочтительно сообщать, какой список использовать при создании.

Подумайте о ресурсе и его менеджере.

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

В этом примере ресурс и его менеджер очень скромно связаны. Менеджер может найти свои созданные ресурсы, и ресурс знает, в каком менеджере он был создан, но ресурсу сообщается, какому менеджеру он принадлежит, не определяя его сам (через глобального менеджера).

Другой способ - создать ресурс (или подкласс), а затем попросить менеджера добавить его в свой список, по существу, временно связав их, но до тех пор они существуют отдельно и не полагаются на заранее определенные экземпляры.

0 голосов
/ 10 сентября 2009

Несмотря на то, что, как уже упоминалось, ваше решение неверно, я бы не стал использовать синглтон. Использование синглтона для достижения вашей цели затруднит тестирование вашего кода. Вместо этого классы должны зависеть от чисто виртуального интерфейса IConnetion или тому подобного. Будет ли неправдоподобной отправка экземпляров объектам при их создании? По крайней мере, у вас должна быть возможность сделать это (или, предпочтительно, использовать установщик), чтобы сделать ваш код тестируемым. Обратите внимание, что «внешнее» решение, предложенное Петром более или менее, такое же, как синглтон.

0 голосов
/ 10 сентября 2009

Лучший способ сделать это - использовать метод Singleton. Это было проверено и доказано. Кроме того, класс потерпит неудачу, если я определю другую переменную UiConnectionList, например, в моей локальной области видимости.

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

Всегда помните при создании одноэлементного класса. Блокировка (Private-tify?) Большой четверки: конструктор, конструктор копирования, оператор присваивания, деструктор

Кроме того, поскольку вы используете глобальные переменные, я предполагаю, что вам не нужно выделять или скрывать. Таким образом, метод синглтона - лучший способ сделать это.

Вот пример реализации синглтона.

// Meyers singleton
static UiConnectionList* UiConnectionList::getSingletonPtr()
{
    static UiConnectionList x;
    return &x;
}
0 голосов
/ 10 сентября 2009

Самое меньшее, что вы можете сделать, - это создать класс / структуру UiContext. Определите все остальные вещи как переменные-члены этого класса. Затем создайте экземпляр UiContext в своем основном классе и передайте его любому классу, который этого требует.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...