2014-07-07 12 views
10

Mam kilka zestawów akcji (np. Kopiuj, wklej, cofnij, ponów, pokaż dokowalne okno XYZ, powiększenie itp.), Które Nie chcę duplikować w wielu lokalizacjach, ale są one udostępniane przez różne części GUI, takie jak menu główne, pasek narzędzi i menu z prawym przyciskiem myszy.Jaki jest najlepszy sposób udostępniania akcji między oddzielnymi klasami GUI (menu, paski narzędzi itp.)?

Jaki jest najlepszy sposób na ich udostępnienie? Używam Qt 5.3 z C++, ale jest to w większości niezależne od konkretnego środowiska GUI lub języka.

Niektóre możliwości:

  1. wyznaczyć jedno centralne miejsce, powiedzieć główne okno, aby stworzyć wszystkie z nich z ich tekst, ikony, a zwrotnego. Następnie:

    1. Przekaż akcje do konstruktora podczas tworzenia podskładników GUI. Może to spowodować, że konstruktor będzie wyświetlał długie listy.

    2. Ustawianie wywołań w podskładnikach GUI po utworzeniu podskładnika i przekazywanie wszystkich niezbędnych działań. To sprawia, że ​​konstruktor jest krótszy, ale na końcu wcale nie jest ładniejszy.

    3. Dostawcy dostaw z głównego okna i mają podskładniki, aby uzyskać pożądane działania. Podkomponenty mają na ogół wskaźnik do głównego okna. To sprawia, że ​​głównym oknem jest brak wiedzy na temat kogo obchodzi, które działanie, ale także ujawnia grupę publicznych członków (chyba że używam Attorney-Client idiom lub podobnego).

    4. Dodaj je do osobnego, globalnego repozytorium, w którym główne okno dodaje je, a użytkownicy szukają ich według nazwy lub klucza lub czegoś w razie potrzeby. Jest to podobne do innych opcji, ale oddziela obawy nieco lepiej i eksponuje tylko jeden sparametryzowany getter, a nie garść konkretnych modułów pobierających. Minus: Dodaje globalny obiekt, do którego wszyscy uzyskują dostęp.

  2. Zdefiniować działania w „głównym” przypadek użycia, powiedzmy, w menu głównym, a następnie mieć pobierające dla każdego innego. Lokalizuje je w jednym miejscu i oznacza, że ​​główne okno będzie wymagało podania jednej funkcji pobierającej dla głównego menu. Ale nadal eksponuje grupę wewnętrznych członków jako publiczną.

Jakie jest najlepsze podejście? Czy jest coś lepszego, czego tutaj nie wymieniłem?

Odpowiedz

2

Zróbmy krok wstecz i spójrzmy na oprogramowanie, które powstaje. Zwykle dobrą praktyką jest posiadanie konfigurowalnego interfejsu użytkownika, aby użytkownicy mogli tworzyć/modyfikować menu aplikacji i paski narzędzi. Wymaga to utworzenia pasków narzędzi/menu przy użyciu pliku konfiguracyjnego (.xml lub .cfg), który po prostu łączy elementy menu/paska narzędzi z akcjami.

W związku z tym jest wymagane, aby Akcje miały unikalne nazwy/kody akcji, za pomocą których można je skierować.

W związku z tym zalecam 1.4. Możesz utworzyć akcje na żądanie, używając ActionFactory, która otrzyma nazwę/kod akcji jako argument, lub możesz uczynić go obowiązkowym dla Actions, aby zarejestrować się jako ActionRegistry (globalny!), Z którego można je wyszukać.

PS: Innym przypadkiem użycia dla nazwanych akcji jest to, że jeśli oprogramowanie ma pakiet SDK, można łatwo udostępnić interfejsy API do automatyzacji (na przykład ApiExecuteAction(Actions.COPY)).

2

Czy są one opcjonalne, czy też klasa ich wymaga?

jeśli twoja klasa wymaga, aby je przekazać konstruktorowi, mówisz, że lista argumentów stałaby się długa? to dlaczego nie spakować akcji i obiektu najpierw i użyć metody fabrycznej do stworzenia tego obiektu.

Jeśli nie zostaną przekazane im z ustawiaczy i pobierających.

Coś można przeczytać na i rozważenia jest wzór wstrzykiwania zależności, które można przeczytać więcej na temat tutaj:

What is dependency injection?

+0

W odpowiedzi na twoje pytanie, muszą pojawić się w wielu miejscach, co oznacza, że ​​wiele klas potrzebuje do nich dostępu. Twój drugi akapit sugeruje, że mój 1.1 lub 1.4 jest najlepszy. Oba mogą umożliwić wstrzyknięcie zależności. – metal

+2

@metal tak, całkowicie poparłbym rozwiązanie 1.4, ułatwiłoby to również testowanie, sprawiając, że argumenty konstruktora sugerują, że są one obowiązkowe, co pomogłoby w utrzymaniu, jeśli obawiasz się, że wszystko zależy od jednego obiektu , dodaj interfejs, z którego obiekt może czerpać, to przynajmniej pozwoli na "plug and play" z różnymi implementacjami obiektu i złamie zależność kodu źródłowego między nimi. –

1

Problem ponownym QActions w niektórych dialogach jest ponowne połączenie sygnałów.

Możesz uniknąć tego problemu, tworząc zestaw klas do przechowywania grupy sygnałów. Coś takiego:

template < class T > 
class EditionSet 
{ 
    T* parent; 

    public: 

    EditionSet(T* parent) 
     : parent(parent) 
    { 
     cutAction = new QAction("Cut", parent); 
     copyAction = new QAction("Copy", parent); 
     pasteAction = new QAction("Paste", parent); 

     QObject::connect(cutAction, SIGNAL(triggered()), 
         parent, SLOT(CutActionTriggered())); 

     QObject::connect(copyAction, SIGNAL(triggered()), 
         parent, SLOT(CopyActionTriggered())); 

     QObject::connect(pasteAction, SIGNAL(triggered()), 
         parent, SLOT(PasteActionTriggered())); 
    } 

    ~EditionSet() 
    { 
     QObject::disconnect(cutAction, SIGNAL(triggered()), 
          parent, SLOT(CutActionTriggered())); 

     QObject::disconnect(copyAction, SIGNAL(triggered()), 
          parent, SLOT(CopyActionTriggered())); 

     QObject::disconnect(pasteAction, SIGNAL(triggered()), 
          parent, SLOT(PasteActionTriggered())); 

     delete cutAction; 
     delete copyAction; 
     delete pasteAction; 
    } 

    QAction* cutAction; 
    QAction* copyAction; 
    QAction* pasteAction; 
}; 

class dialog : public QDialog 
{ 
    Q_OBJECT 

    public: 

    dialog::dialog(QWidget* parent) 
     : QDialog(parent), 
     ui(new Ui::dialog), 
     editionSet(EditionSet<dialog>(this)) 
    { 
     // ... 

     ui->mainToolBar->addAction(editionSet.cutAction); 
     ui->mainToolBar->addAction(editionSet.copyAction); 
     ui->mainToolBar->addAction(editionSet.pasteAction); 
    } 

    private: 

    EditionSet<dialog> editionSet; 
}; 

Jeśli działania będą zawsze umieszczone w tej samej kolejności, można poprawić tę klasę, aby umożliwić wstawienie „auto”.

template < class T > 
class EditionSet 
{ 
    T* parent; 
    QAction* cutAction; 
    QAction* copyAction; 
    QAction* pasteAction; 

    public: 

    EditionSet(T* parent) 
     : parent(parent) 
    { 
     cutAction = new QAction("Cut", parent); 
     copyAction = new QAction("Copy", parent); 
     pasteAction = new QAction("Paste", parent); 

     QObject::connect(cutAction, SIGNAL(triggered()), 
         parent, SLOT(CutActionTriggered())); 

     QObject::connect(copyAction, SIGNAL(triggered()), 
         parent, SLOT(CopyActionTriggered())); 

     QObject::connect(pasteAction, SIGNAL(triggered()), 
         parent, SLOT(PasteActionTriggered())); 

    } 

    ~EditionSet() 
    { 
     QObject::disconnect(cutAction, SIGNAL(triggered()), 
          parent, SLOT(CutActionTriggered())); 

     QObject::disconnect(copyAction, SIGNAL(triggered()), 
          parent, SLOT(CopyActionTriggered())); 

     QObject::disconnect(pasteAction, SIGNAL(triggered()), 
          parent, SLOT(PasteActionTriggered())); 

     delete cutAction; 
     delete copyAction; 
     delete pasteAction; 
    } 

    void AddActionsTo(QWidget* container) 
    { 
     container->addAction(cutAction); 
     container->addAction(copyAction); 
     container->addAction(pasteAction); 
    } 
}; 

class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 

    public: 
    MainWindow(QWidget *parent = 0) 
     : QMainWindow(parent), 
     ui(new Ui::MainWindow) 
     editionSet(EditionSet<MainWindow>(this)) 
    { 
     ui->setupUi(this); 

     editionSet.AddActionsTo(ui->mainToolBar); 
     editionSet.AddActionsTo(ui->menuBar); 
    } 

    private: 

    EditionSet<MainWindow> editionSet; 
}; 
1

Gorąco polecam 1.1 lub 1.2 jako najmniej uciążliwe rozwiązanie.

1.3 znacznie rozszerza publiczny interfejs klasy okna w bardzo żmudny sposób.

1.4 eliminuje problem globalnej wyjątkowości do nowej "przestrzeni nazw" - tej, w której znajduje się repozytorium poleceń.

2.0 ujawnia wiele prywatnych informacji, wydaje mi się najgorsze.

BTW, jeśli nie czytałeś o wzorze Command, polecam go.

+0

Dzięki. Przeczytałem wzorzec poleceń, a te współdzielone działania są ich instancjami. Pytanie, gdzie powinny mieszkać? – metal

Powiązane problemy