Table of Contents

Návrhové vzory - factory

factory

Factory má za úkol vytvářet instance objektů.
Ukazatel na třídu Factory slouží jako “proměnná uchovávající konstruktor třídy”

Abstrakní třída Factory

Třída Factory bude obsahovat jen metodu createInstance

Výsledný typy metody createInstance bude třída společná pro vytvářené objekty, v našem případě QWidget.

Metoda bude abstraktní

class Factory
{
public:
    virtual QWidget * createInstance () = 0;
};

Factory pro jednotlivé grafické prvky

Vytvoříme třídu ButtonFactory odvozenou z třídy Factory. \ Implemetujeme metodu createInstance.

class ButtonFactory : public Factory
{
public:
    QWidget * createInstance ()  { return new QPushButton ("Button"); }
};

Odvozená třída může skladovat další data.
Např. EditorFactory skladuje text, který byl parametrem konstruktoru.
A createInstance použije text při vytváření instance.

class EditorFactory : public Factory
{
private:
    QString text;
public:
    QWidget * createInstance ()
    {
        QTextEdit * e = new QTextEdit;
        e->setText (text);
        return e;
    }
    EditorFactory (QString p_text) : text (p_text) { }
};
class TreeFactory : public Factory
{
public:
    QWidget * createInstance ()  { return new QTreeWidget; }
};

Použití Factory v jednoduché aplikaci

Jednotivé Factory budeme skladovat v asociativním poli factories.
Indexem bude jméno předávané pomocí drag and drop, jméno bude typu QString.
Hodnoty v poli budou ukazatele na jednotlivé Factory.
Samotné pole bude typu QHash, což je obdoba std::map.

extern QHash <QString, Factory *> factories;

Během drag and drop budeme data typu Factory přenášet jako data našeho následujícího formátu:

const QString tool_format = "application/x-shape";

Tlačítka pro jednotlivé "Factory"

Třídu ToolButton odvodíme ze třídy QToolButton, bude obsahovat:

class ToolButton : public QToolButton
{
private:
    QString name;
protected :
    void mousePressEvent (QMouseEvent *event);
public:
    ToolButton (QWidget * p_parent, QString p_name) :
        QToolButton (p_parent),
        name (p_name)
    {
        setText (p_name);
    }
};

Metoda mousePressEvent zahájí “tažení dat pomocí myši”.

void ToolButton::mousePressEvent (QMouseEvent *event)
{
    QDrag * drag = new QDrag (this);
    QMimeData * data = new QMimeData ();
    data->setData (tool_format, name.toLatin1());
    drag->setMimeData (data);
    drag->start ();
}

Cílový panel pro jednotlivé grafické prvky

Plochu hlavního okna naší aplikace bude pokrývat třída Panel odvozená od QWidget.

class Panel : public QWidget
{
    Q_OBJECT
public:
    explicit Panel (QWidget *parent = nullptr);
 
protected:
    void dragEnterEvent (QDragEnterEvent *event);
    void dropEvent (QDropEvent *event);
};

Konstruktor bude mít za parametr odkaz na nadřazené okno,
tento odkaz předáme konstruktoru na nadřazenou třídu QWidget
a povolíme vhazování dat pomocí myši setAcceptDrops (true)

Panel::Panel(QWidget *parent) : QWidget (parent)
{
    setAcceptDrops (true);
}

Odsouhlasení nebo odmítnutí tažených dat

Virtuální funkce dragEnterEvent pocházející z nadřazené třídy QWidget
vyzvedne odkaz na přenášená data do proměnné typu const QMimeData *
a pokud data obsahují náš formát, tak drop odsouhlasí.

Výsledek se projeví na vzhledu kurzoru myši během taření dat.

void Panel::dragEnterEvent (QDragEnterEvent *event)
{
    const QMimeData * data = event->mimeData ();
    if (data->hasFormat (tool_format))
    {
        event->accept();
    }
}

Vhození dat

Implementujeme virtální metodu dropEvent

factory->createInstance ()
void Panel::dropEvent (QDropEvent *event)
{
    const QMimeData * data = event->mimeData ();
    if (data->hasFormat (tool_format))
    {
        QString name = data->data(tool_format);
        Factory * factory = factories [name];
        QWidget * widget = factory->createInstance ();
 
        QPoint pos = event->pos();
        widget->setParent (this);
        widget->move (pos);
        widget->setVisible(true);
    }
}

Přidání virtuálních funkcí z nadřazených tříd v prostředí Qt Creator

Pravou myší na identifikátor Panel nebo QWidget, Refactor, Insert Virtual Functions of Base Classes

Hlavní okno s paletou nástrojů

class MainWindow : public QMainWindow
{
public:
    MainWindow(QWidget *parent = nullptr);
    void addTool (QString text, Factory * factory);
    /* ... */
};

Funkce addTool

void MainWindow::addTool (QString name, Factory * factory)
{
    factories [name] = factory;
    ToolButton * b = new ToolButton (this, name);
    ui->toolBar->addWidget (b);
}

V konstruktoru vytvoříme jednotlivé nástroje umístěné do palety, funkci addTool předáme jako parametry

MainWindow::MainWindow(QWidget *parent)
{
    /* ... */
 
    addTool ("Button",    new ButtonFactory);
    addTool ("Edit",      new EditorFactory ("some text"));
    addTool ("Tree View", new TreeFactory);
}

Vzhled okna po vhození jednoho exempláře od každé “factory”

gitlab