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í

  • = 0
  • nebude mít implementaci
  • nelze vytvářet exempláře přímo z třídy Factory pomocí new Factory
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:

  • proměnná name - jméno předávané pomocí drag and drop a v našem případě i nadpis tlačítka
  • virtuální funkci mousePressEvent původně deklarovanou v nadřazené tříde QWidget reagující na stisk tlačítka pomocí myši
  • konstruktor který uloží svůj parametr do proměnné name a současně zobrazí text na tlačítku
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”.

  • vytvoříme objekt typu QDrag objsahují parametry pro drag and drop, v konstruktoru předáme jako parametr naše okno
  • přenášená data budou uložena v objektu typu QMimeData, stejný typ se používá i pro práci se schránkou (clipboard)
  • data mohou být uložena vetool_format více formátech (např. text, obrázek, …) z nichž si cílová aplikace může vybrat pro sebe vhodný formát
  • použijeme náš formát “application/x-shape” a předáme jméno Factory jako ascii řetězec znaků
  • data uložíme do objekt typu QDrag
  • nakonec zahájíme drag and drop
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

  • opět vyzvedneme tažená data
  • zkontrolujeme formát, ale jelikož jsem odsouhlasili jen náš formát, je to zbytečné
  • do proměnné name uložíme data prřenášená v rámci našeho formátu
  • z pole factories vyzvedneme objekt představující naší factory
    • pomocí drag and drop přenášíme text, nikoliv objekt typy Factory
  • použití factory : zavoláme factory→createInstance () a tím vytvoříme grafický prvek
  • do proměnné pos uložíme souřadnici myši při vhození dat
  • grafický prvek přidáme do panelu
  • posuneme na zmíněnou souřadnici
  • grafický prvek zobrazíme
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

  • přidá factory do pole factories
  • vytvoří tlačítko s příslušným textem
  • přidá tlačítko do nástrojové lišty
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

  • jméno zobrazované na tlačítku a předávané během drag and drop
  • nový exemplář Factory, který bude uložen v poli factories a použit při vhození dat pomocí myši
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

 
uop/factory.txt · Last modified: 2020/12/14 13:15 by 88.103.111.44
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki