Dlaczego Qt nadużywa terminologii dotyczącej modelu / widoku?

104

Myślę, że terminologia używana w Qt z kontrolkami modelu / widoku jest błędna. Na swojej stronie z wyjaśnieniami stwierdzają, że uprościli MVC do MV, łącząc Widok i Kontroler i podają następujący obraz:

obraz wyjaśniający Qt MVC

Myślę jednak, że błędnie nazwali role obiektów i myślę, że

  1. To, co nazywają widokiem ze scalonym kontrolerem, jest w rzeczywistości tylko widokiem.
  2. To, co nazywają modelem, jest w rzeczywistości tylko kontrolerem.
  3. Jeśli naprawdę chcesz mieć model, byłby to gdzieś tam, gdzie są ich „Dane”.

Mówię o zwykłym i rozsądnym sposobie wykorzystania komponentu modelu / widoku Qt w swojej aplikacji. Oto powody:

  1. Zwykle jest to komponent Qt, który jest używany bez dodawania logiki kontrolera specyficznej dla twoich obiektów)
  2. To nie jest model, tylko dlatego, że powinieneś zaimplementować kilka metod Qt, takich jak rowCount, columnCount, data itp., Które nie mają nic wspólnego z twoim modelem. W rzeczywistości istnieją typowe metody modelowe, które można znaleźć w kontrolerach. Oczywiście możesz tutaj zaimplementować zarówno logikę kontrolera, jak i modelu, ale po pierwsze, byłby to dość zły projekt kodu, a po drugie scaliłbyś kontroler i model, a nie kontroler i widok, tak jak są.
  3. Jak powiedziano w przypadku 2., jeśli chcesz oddzielić logikę modelu, to z pewnością nie jest to niebieskie pole na obrazku, ale raczej przerywana ramka „Dane” (oczywiście komunikująca się z rzeczywistymi danymi).

Czy Qt jest źle w ich terminologii, czy tylko ja nie rozumiem? (Swoją drogą: powodem, dla którego nie jest to kwestia akademicka, jest to, że zacząłem kodować swój projekt po ich nazewnictwie i wkrótce odkryłem, że kod najwyraźniej nie jest w porządku. Dopiero potem zdałem sobie sprawę, że powinienem nie próbuj umieszczać logiki modelu w tym, co nazywają modelem)

gorn
źródło
1
MFC ustanowiło standard dla 2-częściowego modelu / widoku guis z CDoc i CView - nie ma powodu, aby konkretny MVC był `` poprawny ''
Martin Beckett
@Martin B: Przyjrzę się MFC, ale nawet jeśli istnieją różne modele MVC, myślę, że powinny być spójne w swojej terminologii i myślę, że przedstawiłem ważne argumenty, dlaczego zastosowana terminologia nie jest spójna w tym konkretnym przypadku. Po prostu stwierdzają, że połączyli Widok i Kontroler, ale wydaje mi się, że jest to po prostu mylące w tej sprawie. Nie sądzę, aby istniał model MVC, w którym cała logika specyficzna dla aplikacji, czy to prezentacja, czy logika modelu, musi być umieszczona w jednym obiekcie o nazwie Model.
gorn
1
@Martin B: Również pod terminologią qt wszystkie modele mają wspólny interfejs API, który nie ma nic wspólnego ze strukturą Modelu, ale wszystko ma związek z ogólną strukturą kontrolera, co jest wyraźnym znakiem, że nie można nazywać go modelem. Nie mówię, że jest JEDEN poprawny model MVC, ale to nie znaczy, że cokolwiek można nazwać modelem MVC. Może jest on również wadliwy w MFC i mogę mu się przyjrzeć, ale bardziej interesuje mnie to, że Qt robi to dobrze, niż MFC, którego nie zamierzam używać. Czy masz jakieś dobre łącze, w którym wyjaśniono separację modelu / widoku MFC?
gorn
1
Terminologia MVC nie została w żadnym wypadku uzgodniona jednogłośnie, więc twoje pytanie może zostać uznane za dyskusyjne. Wielu jednak zgodzi się z doskonałą pracą Martina Fowlera ( martinfowler.com/eaaDev/index.html ). Zwykle kontroler obsługuje dane wejściowe użytkownika iw tym sensie widżety Qt zdecydowanie łączą widok i kontroler.
Arnold Spence,
1
Rozumiem, że MVC ma wiele smaków, ale nie oznacza to, że wszystko może być MVC. Qt przekroczyło granicę i podałem kilka powodów. Martin Fowler wyjaśnia różne typy MVC, ale żaden z nich nie jest wystarczająco podobny do tego, co Qt wymawia MVC. Najbardziej podobny to martinfowler.com/eaaDev/PresentationModel.html , ale rozróżnia to model prezentacji = część kontrolera (interakcja z użytkownikiem) i model (logika danych). Więc chociaż nie ma precyzyjnej definicji MVC, Qt nie przestrzega żadnego z nich. Jeśli możesz podać mi link do takiej definicji, zrób to.
gorn

Odpowiedzi:

78

Zgadzam się z tobą, że nazewnictwo Qt jest mylące. Moim zdaniem jednak problem nie dotyczy samego Qt, ale jest wspólny dla wszystkich frameworków, które pozwalają nam przestrzegać zasady oddzielenia obaw podczas wdrażania naszych interfejsów użytkownika. Kiedy ktoś wymyśla taki framework i znajduje dobry sposób na oddzielenie „rzeczy”, zawsze czuje się zobowiązany do posiadania modułów, które nazywają „Modelami” i innych, które nazywają „Widokiem”. Przez lata pracowałem z tymi frameworkami:

  • MFC
  • Qt
  • Huśtawka
  • SWT
  • WPF z MVVM

Jeśli porównasz, w jaki sposób terminy „Model” i „Widok” są używane w tych strukturach oraz jakie obowiązki mają klasy w „Widoku”, „Modelu” i „Kontrolerze” (jeśli taki istnieje), będziesz stwierdzimy, że istnieją bardzo duże różnice. Z pewnością przydatne byłoby porównanie różnych koncepcji i terminologii, tak aby ludzie przechodzący z jednego frameworka do drugiego mieli szansę pozostać przy zdrowych zmysłach, ale wymagałoby to dużo pracy i badań. Dobrym odczytu jest Martina Fowlera przegląd .

Ponieważ istnieje tak wiele różnych pomysłów co wzorzec MVC może wyglądać, który z nich jest poprawna? Moim zdaniem do ludzi, którzy wymyślili MVC, należy się zwrócić, gdy chcemy wiedzieć, jak ma on zostać wdrożony „poprawnie”. W oryginalnej gazecie smalltalk jest napisane:

Widok zarządza graficznym i / lub tekstowym wyjściem do części obrazu bitmapowego, która jest przypisana do jego aplikacji. Kontroler interpretuje dane wejściowe myszy i klawiatury od użytkownika, nakazując modelowi i / lub widokowi odpowiednią zmianę. Wreszcie model zarządza zachowaniem i danymi domeny aplikacji, odpowiada na zapytania o informacje o jej stanie (zwykle z widoku) oraz odpowiada na instrukcje zmiany stanu (zwykle od kontrolera).

W świetle powyższego odpowiedziałbym w ten sposób na Twoje trzy główne wątpliwości:

  1. W rzeczywistości komponent Qt „zarządza graficznym [...] wyjściem” i „interpretuje dane wprowadzane przez mysz i klawiaturę”, więc można go rzeczywiście nazwać połączonym Widokiem i Kontrolerem w odniesieniu do powyższej definicji.
  2. Zgadzam się, że jesteś / będziesz zmuszony do połączenia Kontrolera i Modelki (ponownie w odniesieniu do powyższej definicji).
  3. Znowu się zgadzam. Model powinien zarządzać tylko danymi domeny aplikacji . To właśnie nazywają „danymi”. Oczywiście zajmowanie się na przykład wierszami i kolumnami nie ma zwykle nic wspólnego z naszą domeną aplikacji.

Gdzie nas to zostawia? Moim zdaniem najlepiej jest dowiedzieć się, co naprawdę oznacza Qt, kiedy używane są terminy „Model” i „Widok”, i używać tych terminów w ich sposób, gdy programujemy w Qt. Jeśli będziesz się niepokoić, to tylko Cię spowolni, a sposób, w jaki wszystko jest ustawione w Qt, pozwala na elegancki projekt - który waży więcej niż ich „złe” konwencje nazewnictwa.

Tilo
źródło
2
Powiedziałbym, że delegat jest kontrolerem Qt, ponieważ delegaci otrzymują i wysyłają dane wejściowe do modelu, który aktualizuje widok za pomocą sygnałów.
Peregring-lk
82

Krótka odpowiedź

MVC Qt ma zastosowanie tylko do jednej struktury danych . Mówiąc o aplikacji MVC , nie powinieneś myśleć o QAbstractItemModellub QListView.

Jeśli chcesz mieć architekturę MVC dla całego programu, Qt nie ma tak „ogromnego” szkieletu modelu / widoku. Ale dla każdej listy / drzewa danych w twoim programie możesz użyć podejścia Qt MVC, które rzeczywiście ma kontroler w swoim widoku. Danych jest wewnątrz lub na zewnątrz wzór; zależy to od typu używanego modelu (własna podklasa modelu: prawdopodobnie w modelu; np. QSqlTableModel: na zewnątrz (ale może być zapisany w pamięci podręcznej) modelu). Aby połączyć modele i widoki, użyj własnych klas, które następnie implementują logikę biznesową .


Długa odpowiedź

Model / widok Qt i terminologia:

Qt zapewnia proste widoki dla swoich modeli. Mają wbudowany kontroler : wybieranie, edytowanie i przenoszenie elementów jest czymś, czym w większości przypadków kontroler „steruje”. Oznacza to, że interpretujemy dane wejściowe użytkownika (kliknięcia myszą i ruchy) i wydaje odpowiednie polecenia modelowi.

Modele Qt są rzeczywiście modelami zawierającymi podstawowe dane. Abstrakcyjne modele oczywiście nie przechowują danych, ponieważ Qt nie wie, jak chcesz je przechowywać. Ale ty rozszerzyć QAbstractItemModel do swoich potrzeb, dodając swoje kontenery danych do podklasy i uczynienie interfejsu modelu z dostępem do danych. Tak więc, i zakładam, że ci się to nie podoba, problem polega na tym, że musisz zaprogramować model, czyli w jaki sposób uzyskuje się dostęp do danych i modyfikuje je w strukturze danych.

W terminologii MVC model zawiera zarówno dane, jak i logikę . W Qt to od Ciebie zależy, czy włączysz część logiki biznesowej do swojego modelu, czy też umieścisz ją na zewnątrz, będąc „widokiem” samym w sobie. Nie jest nawet jasne, co oznacza logika: wybieranie, zmienianie nazwy i przenoszenie elementów? => już zaimplementowane. Robisz z nimi obliczenia? => Umieść go na zewnątrz lub wewnątrz podklasy modelu. Przechowywanie lub ładowanie danych z / do pliku? => Umieść go w podklasie modelu.


Moja osobista opinia:

Bardzo trudno jest zapewnić programiście dobry i ogólny system MV (C). Ponieważ w większości przypadków modele są proste (np. Tylko listy ciągów znaków), Qt dostarcza również gotowy do użycia QStringListModel. Ale jeśli twoje dane są bardziej złożone niż ciągi, to od Ciebie zależy, jak chcesz przedstawić dane za pomocą interfejsu modelu / widoku Qt. Jeśli masz na przykład strukturę z 3 polami (powiedzmy osoby z imieniem, wiekiem i płcią), możesz przypisać te 3 pola do 3 różnych kolumn lub 3 różnych ról. Nie lubię obu podejść.

Myślę, że struktura modelu / widoku Qt jest przydatna tylko wtedy, gdy chcesz wyświetlić proste struktury danych . Trudno jest sobie z tym poradzić, jeśli dane są niestandardowego typu lub nie są uporządkowane w drzewie lub na liście (np. Wykres). W większości przypadków wystarczą listy, a nawet w niektórych przypadkach model powinien zawierać tylko jeden wpis. Szczególnie jeśli chcesz modelować jeden pojedynczy wpis mający różne atrybuty (jedno wystąpienie jednej klasy), struktura modelu / widoku Qt nie jest właściwym sposobem oddzielenia logiki od interfejsu użytkownika.

Podsumowując, myślę, że struktura modelu / widoku Qt jest przydatna wtedy i tylko wtedy, gdy dane są przeglądane przez jeden z widżetów przeglądarki Qt . Jest to całkowicie bezużyteczne, jeśli masz zamiar napisać własną przeglądarkę dla modelu zawierającego tylko jeden wpis, np. Ustawienia aplikacji, lub jeśli twoje dane nie są typu do druku.


Jak korzystałem z modelu / widoku Qt w (większej) aplikacji?

Kiedyś napisałem (w zespole) aplikację, która wykorzystuje wiele modeli Qt do zarządzania danymi. Zdecydowaliśmy się utworzyć plik DataRoledo przechowywania rzeczywistych danych, które były innego typu niestandardowego dla każdej innej podklasy modelu. Stworzyliśmy zewnętrzną klasę modelu o nazwie Modelprzechowująca wszystkie różne modele Qt. Stworzyliśmy również zewnętrzną klasę widoku zwaną Viewtrzymającą okna (widżety), które są połączone z modelami wewnątrz Model. Więc to podejście jest rozszerzonym Qt MVC, dostosowanym do naszych własnych potrzeb. Obie klasy Modeli Viewsame klasy nie mają nic wspólnego z Qt MVC.

Gdzie umieściliśmy logikę ? Stworzyliśmy klasy, które wykonywały rzeczywiste obliczenia na danych, odczytując dane z modeli źródłowych (gdy uległy zmianie) i zapisując wyniki w modelach docelowych. Z punktu widzenia Qt, te klasy logiki byłyby widokami, ponieważ „łączą się” z modelami (nie „widok” dla użytkownika, ale „widok” dla logiki biznesowej części aplikacji).

Gdzie są kontrolerzy ? W oryginalnej terminologii MVC kontrolery interpretują dane wejściowe użytkownika (mysz i klawiatura) i wydają modelowi polecenia, aby wykonać żądaną akcję. Ponieważ widoki Qt już interpretują dane wejściowe użytkownika, takie jak zmiana nazwy i przenoszenie elementów, nie było to potrzebne. Potrzebowaliśmy jednak interpretacji interakcji użytkownika, która wykracza poza widoki Qt.

leemes
źródło
Najbardziej irytujące jest to, że musisz zaimplementować zupełnie różne klasy Qt Model w zależności od tego, jaki widok chcesz. Model widoku listy nie obsługuje poprawnie widoku drzewa i odwrotnie. Kanoniczny model MVC może obsługiwać wiele różnych typów widoków.
smerlin
3
@smerlin: Myślę, że to nie jest poprawne. Zarówno QListView, jak i QTreeView wymagają tylko interfejsu QAbstractItemView, co oznacza, że ​​niestandardowa podklasa tego lub konkretna klasa, taka jak QStandardItemModel, powinna spełniać te wymagania dla obu. Możesz prowadzić drzewo i wyświetlać listę z jednego modelu.
jdi
1
@jdi: są przypadki, w których twoje dane są zarówno listą, jak i drzewem ... np. możesz chcieć wyświetlić swój system plików jako drzewo lub wszystkie pliki jako listę. Modele Qts nie pozwalają na to prawidłowo. Implementacja QAbstractItemModel obsługująca widoki drzewa, pozwala tylko wyświetlać wszystkie pliki / katalogi w katalogu głównym jako listę, ale nie możesz wyświetlić wszystkich plików jako listy. I nie mów, że wyświetlanie danych drzewa jako listy nie może być przydatne. Np. Możesz łatwo posortować je, aby znaleźć plik o największym rozmiarze, jeśli wyświetlasz swoje pliki jako listę, widoki drzew na to nie pozwolą.
smerlin
1
Powiedział, że model proxy jest bardziej czymś w rodzaju widoku (ponieważ modyfikuje sposób dane są oglądane ), a zatem powinno należeć do widoku. Jeśli czytasz moją długą odpowiedź: w klasie „dużej” Viewpowinieneś dodać model proxy, który ma model drzewa jako model bazowy i jest używany przez widok listy twojego systemu plików. Jak mówisz: nie powinno być dwóch modeli dla tych samych danych. Nigdy! (Ale modele proxy nie liczą się jako oddzielne modele.)
leemes
1
@SamPinkus To dlatego, że na to pytanie nie ma jednoznacznej odpowiedzi „ tak” lub „ nie” . Istnieją również różne implementacje QAbstractItemModel, z których niektóre są modelami w sensie MVC, a niektóre nie.
leemes
12

Terminologia nie jest dobra ani zła, jest przydatna lub bezużyteczna.

Możesz nieco zmienić pytanie i zapytać, dlaczego Qt nie jest bardziej przyjazny dla MVC. Odpowiedź jest taka, że ​​wczesni programiści Qt uważają, że oddzielenie V od C w aplikacjach GUI powoduje złe V i C zarówno. Projekt QWidget stara się uprościć ścisłe powiązanie interferencji danych wejściowych myszy z decyzjami dotyczącymi wyjścia pikseli i widać, że to nie jest droga do MVC.

arnt
źródło
Rozumiem twój punkt widzenia i zasadniczo Bym zapytał, dlaczego Qt nie jest bardziej przyjazny dla MVC, ale jest to bardzo trudne, gdy terminologia MVC używana w dokumentacji Qt jest RÓŻNA od tego, co zwykle jest używane w MVC (jak wyjaśniłem w pytaniu). A kiedy istnieje szeroko stosowana terminologia i ktoś używa jej bardzo odmiennie od reszty świata, wydaje mi się, że jest ona nie tylko bezużyteczna, ale że jest po prostu BŁĘDNA i myląca (to zamieszanie doprowadziło mnie do zadania pytania w pierwszym miejsce). Byłbym bardzo zainteresowany, gdybyś miał gdzieś jakieś linki do tych rzeczy, które są omawiane lub wyjaśniane. Dzięki
gorn
Nie mogę nic powiedzieć o tym, dlaczego doktorzy Qt mówią teraz o MVC w sposób, w jaki to robią. Opuściłem Trolltech dawno temu i jestem zdziwiony niektórymi rzeczami, które zostały zrobione w dokumentacji od czasu mojego wyjazdu. (Na moim blogu ja czasami rant trochę o tym, choć.)
arnt
Czy masz wgląd w to, jak terminologia MVC została uzgodniona w Qt? Czy był używany podczas pisania kodu, czy dopiero później podczas procesu dokumentacji.
gorn
Nie używaliśmy słowa „MVC” w dokumentacji Qt podczas mojego pobytu w Trolltech. Ogólnie myślę, że najlepiej jest dokumentować to, co tam jest, a nie pisać o tym, czego nie ma. Jednak po raz kolejny możesz dowiedzieć się, kto dodał ten tekst i dodać tę osobę bezpośrednio.
arnt
1
Inny komentarz. Dyskutowaliśmy o MVC podczas projektowania i wczesnej fazy wdrożenia Qt (kiedy Trollech był firmą trzyosobową), a także oceniliśmy zestaw narzędzi GUI, który używał MVC „prawidłowo”, nie pamiętam jego nazwy. Naszym zdaniem ten zestaw narzędzi był okropny w użyciu i że MVC było w dużej mierze tego przyczyną.
arnt
3

Jako funkcja modelu jest odpowiadanie na wnioski o udzielenie informacji, myślę, że nie ma nic złego w określaniu takich metod jak rowCount, columnCountitp myślę modelu jest jakaś otoczka do źródła danych (bez względu na to co to jest tabela SQL lub po prostu tablicą) , dostarcza dane w standardowej formie i powinieneś zdefiniować metody w zależności od struktury źródła danych.

Dmitrij
źródło
2

Uważam, że ich terminologia jest poprawna ... chociaż w rzeczywistych aplikacjach bardzo łatwo jest zatrzeć granice między modelem, widokiem i kontrolerem, w zależności od poziomu abstrakcji: widok jednego poziomu może być modelem wyższego poziomu.

Czuję, że zamieszanie wynika z ich klasy QAbstractModelItem. Ta klasa nie jest elementem modelu, ale jest raczej interfejsem do modelu. Aby ich klasy widoku współdziałały z modelem, musieli utworzyć ogólny abstrakcyjny interfejs do modelu. Jednak model może być pojedynczą pozycją, listą pozycji, tabelą zawierającą 2 lub więcej wymiarów przedmiotów itp .; więc ich interfejs musi obsługiwać wszystkie te odmiany modelu. Trzeba przyznać, że to sprawia, że ​​elementy modelu są dość złożone, a kod kleju, aby działał z rzeczywistym modelem, wydaje się nieco rozciągać metaforę.

Chris Morlier
źródło
Chociaż zgadzam się z tobą z klasą QAbstractModelItem, myślę też, że nawet bez tej komplikacji ich MVC jest błędnie nazwane. Czy mógłbyś wyjaśnić, dlaczego uważasz, że ich terminologia jest poprawna. Bardzo chciałbym usłyszeć, dlaczego nie mam racji w żadnym z moich trzech argumentów.
gorn
0

Myślę, że ... To, co nazywają modelem, jest w rzeczywistości tylko kontrolerem.

Nie, ich „model” zdecydowanie nie jest kontrolerem.

Kontroler jest częścią widocznych dla użytkownika elementów sterujących, które modyfikują model (a zatem pośrednio modyfikują widok). Na przykład przycisk „usuń” jest częścią kontrolera.

Myślę, że często pojawia się zamieszanie, ponieważ wielu widzi coś w rodzaju „kontroler modyfikuje model” i wydaje mi się, że oznacza to funkcje mutujące w ich modelu, takie jak metoda „deleteRow ()”. Ale w klasycznym MVC kontroler jest w szczególności częścią interfejsu użytkownika. Metody, które modyfikują model, są po prostu częścią modelu.

Odkąd wynaleziono MVC, jego rozróżnienie między kontrolerem a widokiem stało się coraz bardziej napięte. Pomyśl o polu tekstowym: wyświetla zarówno tekst, jak i pozwala go edytować, więc czy jest to widok czy kontroler? Odpowiedź musi być taka, że ​​jest częścią obu. W latach sześćdziesiątych, kiedy pracowałeś nad teletypem, rozróżnienie było wyraźniejsze - pomyśl o ed- ale to nie znaczy, że wtedy było lepiej dla użytkownika!

Prawdą jest, że ich QAbstractItemModel jest na wyższym poziomie niż normalnie byłby model. Na przykład elementy w nim mogą mieć kolor tła (technicznie pędzel), co jest zdecydowanie atrybutem widoku! Jest więc argument, że QAbstractItemModel bardziej przypomina widok, a twoje dane są modelem. Prawda jest taka, że ​​jest gdzieś pomiędzy klasycznym znaczeniem widoku i modelu. Ale nie widzę, dlaczego to kontroler; jeśli cokolwiek to jest widżet QT, który go używa.

Arthur Tacca
źródło