Czołem Bracia i Siostry w kodzie
Nie będzie to tak do końca typowy post z zapytaniem o pomoc, jako że rozwiązanie jakie zastosowałem działa. Chciałbym jedynie się dowiedzieć, czy gdzieś można coś by usprawnić.
Wstęp do sytuacji przed zmianą wygląda tak:
- Są sobie 2 niezależne aplikacje - middleware które dostarcza różnakie dane, oraz GUI napisane w qmlu.
- Komunikują się ze sobą przy pomocy współdzielonych w .rep definicji dla Qt remote objects, używając url np. "tcp://127.0.0.1:19991". Bardzo możliwe, iż w przyszłości adres ten będzie w pełni sieciowy, a nie na tej samej maszynie.
Taka architektura działa OK, jednak sęk w tym, iż bezpośrednio w kodzie qmlowym są odwołania do owych zdalnych obiektów, więc jakiekolwiek przerobienie ich definicji wymusi zmiany w qmlu; już była zresztą sytuacja iż gościu z innego zespołu rzeźbił nam w plikach qmlowych bo się adres url do komunikacji zmienił. Poza tym część ludzi w moim zespole C++ nie ogarnia nawet trochę.
Zeźliłem się, i klnąc się na Peruna, Trygława, Swaroga i Welesa zapowiedziałem, iż tak zostać to nie może.
Postawiłem sobie za cele:
- Schować używanie zdalnych obiektów i niskopoziomowego C++ w qmlu.
- Sprawić, żeby komunikacja qmla z middleware'm była prosta, wygodna, i nie wymagająca znajomości co tam się w niższej warstwie dzieje.
- Uniezależnić się jak najbardziej od innych modułów i zespołów (definicje obiektów zdalnych będzie robił ktoś inny)
- Architektura ma generalnie spełniać KISS i YAGNI, oraz być niekłopotliwa w dodawaniu nowych zabawek/edycji istniejących.
Oto co stworzyłem.
Jest sobie klasa Communicator, będąca zbiorem interfejsów. Owe interfejsy zawierają w sobie repliki zdalnych obiektów, i robią mapowanie przychodzących z nich danych.
class Communicator : public QObject
{
Q_OBJECT
QML_ATTACHED(Communicator)
QML_ANONYMOUS
Q_PROPERTY( InterfaceSomeInterface *interfaceSome READ getInterfaceSome CONSTANT)
Q_PROPERTY( InterfaceOtherInterface *interfaceOther READ getInterfaceOther CONSTANT)
public:
static Communicator* qmlAttachedProperties(QObject *object)
{
static Communicator communicator;
return &communicator;
}
InterfaceSomeInterface* getInterfaceSome() const;
InterfaceOtherInterface * getInterfaceOther () const;
private:
Communicator(QObject *parent = nullptr);//w konstruktorze są tworzone instancje interfejsów
};
Communicator rejestruje się w qmlu jako attached property jako Backend.
A tak wygląda interfejs:
class InterfaceSomeInterface : public QObject
{
Q_OBJECT
QML_ANONYMOUS
//tu w zależności od tego, co chcę udostępnić w qmlu
Q_PROPERTY(int neededData READ neededData NOTIFY neededDataChanged)
public:
//konstruktor interfejsu, nic niezwykłego. W jego wnętrzu odbywa się np. połączenie sygnałów repliki z sygnałami interfejsu
int neededData() const;//te gettery przeważnie robią replica.neededData();
private:
SomeRemoteObjectReplica replica;
signals:
void neededDataChanged(int data);
};
Interfejs rejestruje się w qmlu jako uncreatable tudzież anonymous type
I jako to teraz w praktyce działa - w qmlu osoba, przyjmijmy iż jest to nieogar cplusplusowy, chce się dobrać do czegoś z backendu. Jedyne co musi zrobić, to użyć komendy:
Backend.interfaceSome.neededData
intellisense ładnie podpowiada nazwy zdefiniowane jako Q_PROPERTY. Mogę dzięki tym interfejsom robić mocki dla zdalnych obiektów których definicja jeszcze nie została uzgodniona, mogę w jedną klasę interfejsu dać kilka replik, albo na odwrót - stworzyć kilka interfejsów które będą działać na tej samej replice, jedynie koncentrować się na czymś innym.
I cóż o tym sądzicie Bracia/Siostry? Jakoś można to ulepszyć, tudzież zapominałem o czymś co mnie wkrótce ugryzie w zadziec?