Aplikacja dla freelancerów- wstępny schemat modelu

0

Hey ;)

Zabrałem się za projektowanie apki dla freelancerów. Planuję wykorzystać WPF oraz język C#, stosując wzorzez MVVM. Problem w tym, że nie do końca rozumiem, jak ten wzorzec działa, więc zacząłem od modelu. Nie do końca jestem pewien, co jeszcze będzie potrzebne, więc zamieściłem chwilowo podstawowe obiekty. Mam kilka pytań:

  1. Jeśli chodzi o dokumenty, to czy encja dokumentu powinna posiadać metodę Print() czy powinien zajmować się tym jakiś manager? Jeśli tak, jak go skonstruować? Chciałbym to zrobić tak, żeby móc w przyszłości dodawać inne dokumenty i potem taki obiekt tylko przesłać do tej samej funkcji drukującej.

  2. Chciałbym zrobić tą apkę tak, by była wielojęzyczna. Czy takie pomocnicze klasy, które wypełniają treść okienek i dokumentów należą do modelu? Zasoby językowe znajdowałyby się poza bazą danych.

  3. ContentFiller należy do warsty VM? W zasadzie ma on za zadanie zaciągnięcie plików językowych i odpowiednie wypełnianie okienek i dokumentów (prawdopodobnie będzie to podzielone na więcej klas).

Czegoś tutaj jeszcze w tym modelu wg was brakuje? Projekt wydaje się być sporym wyzwaniem, a powiedzmy, że jestem raczej średniozaawansowany, a chciałbym to wykonać naprawdę dobrze.

Zapraszam do dyskusji. :)

PS
Jeszcze jedno pytanie, aby się upewnić, czy dobrze rozumuję:

Każdy widok powinien mieć odpowiadającą mu klasę VM, a ta klasa zawiera właściwości, które są prezentowane na widoku (i zbindowane z nimi). Prawda czy fałsz? :)

1
S-cat napisał(a):
  1. Jeśli chodzi o dokumenty, to czy encja dokumentu powinna posiadać metodę Print() czy powinien zajmować się tym jakiś manager? Jeśli tak, jak go skonstruować? Chciałbym to zrobić tak, żeby móc w przyszłości dodawać inne dokumenty i potem taki obiekt tylko przesłać do tej samej funkcji drukującej.

Utwórz klasę serwisową obsługującą dokumenty i wstrzyknij ją do VM za pomocą mechanizmu IoC (Unity Container albo Ninject). Będzie posiadać funkcję drukowania dokumentów różnych typów przekazanych za pomocą interfejsu np: public void Print(IDocument document);

S-cat napisał(a):
  1. Chciałbym zrobić tą apkę tak, by była wielojęzyczna. Czy takie pomocnicze klasy, które wypełniają treść okienek i dokumentów należą do modelu? Zasoby językowe znajdowałyby się poza bazą danych.

Nie. Model nie widzi widoku. Język GUI to część widoku więc zmiana języków nie powinna być częścią modelu, a osobnym mechanizmem. Widok jest wypełniany przez własności VM, które uzupełniasz za pomocą modelu. Model to logika biznesowa.

S-cat napisał(a):
  1. ContentFiller należy do warsty VM? W zasadzie ma on za zadanie zaciągnięcie plików językowych i odpowiednie wypełnianie okienek i dokumentów (prawdopodobnie będzie to podzielone na więcej klas).

ContentFilter może należeć do klasy serwisowej obsługującej dokumenty. Możesz tam zrobić metodę filtrującą, np. IList<IDocument> GetDocuments(string name, strine header);. Same parametry filtrowania przekażesz przez VM. Co do ustawień językowych to napisałem już wyżej.

S-cat napisał(a):

Każdy widok powinien mieć odpowiadającą mu klasę VM, a ta klasa zawiera właściwości, które są prezentowane na widoku (i zbindowane z nimi). Prawda czy fałsz? :)

Poniekąd tak. Widok powinien mieć własny VM. Nie jest to jednak reguła, ponieważ pojedyncza kontrolka również może mieć swój VM jako DataContext. Jeżeli mówimy o widoku jako o całym oknie to można powiedzieć, że nie zawsze. Jeżeli o wszystkim co ma cokolwiek wspólnego GUI to tak. Ciekawostka: Nie jest regułą trzymanie pustego code-behind. Korzystając z MVVM c-b możesz spokojnie wykorzystać do obsługi zdarzeń, które wydelegujesz z VM np. wyświetlania messageboksów.

0
grzesiek51114 napisał(a):

Utwórz klasę serwisową obsługującą dokumenty i wstrzyknij ją do VM za pomocą mechanizmu IoC (Unity Container albo Ninject). Będzie posiadać funkcję drukowania dokumentów różnych typów przekazanych za pomocą interfejsu np: public void Print(IDocument document);

Ten interfejs, o którym mówisz to u mnie w zasadzie klasa abstrakcyjna Document. Powinienem ją nim zastąpić?

grzesiek51114 napisał(a):

Nie. Model nie widzi widoku. Język GUI to część widoku więc zmiana języków nie powinna być częścią modelu, a osobnym mechanizmem. Widok jest wypełniany przez własności VM, które uzupełniasz za pomocą modelu. Model to logika biznesowa.

Skoro tak, to każdy VM powinien mieć właściwości odpowiadające za treść na kontrolkach, którą przy otwarciu okna, pobiera z zewnętrznych plików i binduje? Czy takie coś może wrzucić do c-b?

grzesiek51114 napisał(a):

ContentFilter może należeć do klasy serwisowej obsługującej dokumenty. Możesz tam zrobić metodę filtrującą, np. IList<IDocument> GetDocuments(string name, strine header);. Same parametry filtrowania przekażesz przez VM. Co do ustawień językowych to napisałem już wyżej.

Tutaj miałem na myśli ContentFiller, nie Filter. Taki content filler miałbym wstrzyknąć do VM? Czyli miałoby to postać np. interfejsu IFiller, z którego dziedziczą poszczególne wypełniacze, dla poszczególnego okna, a każdy VM miałby w sobie referencję do obiektu typu IFiller? Czy to zbędne udziwnienie?

PS
Czy gotowy dokument też jest widokiem? Faktury i umowy będą tak naprawdę templatką, wypełnianą szczególnymi już danymi z poziomu programu.

1
grzesiek51114 napisał(a):

Ten interfejs, o którym mówisz to u mnie w zasadzie klasa abstrakcyjna Document. Powinienem ją nim zastąpić?

Tak. Nic nie stoi na przeszkodzie, żeby zrobić z tego interfejs z dostępnymi własnościami;

grzesiek51114 napisał(a):

Skoro tak, to każdy VM powinien mieć właściwości odpowiadające za treść na kontrolkach, którą przy otwarciu okna, pobiera z zewnętrznych plików i binduje?

Takie coś możesz spokojnie wrzucić do VM przy czym najlepiej utworzyć metody pobierające dane w ramach jakiegoś serwisu, który będzie obsługiwał większą część reszty. Tak żeby logicznie to się trzymało kupy. Taki serwis możesz np. wstrzyknąć do VM. Można też zrobić do tego osobną klasę etc... Cokolwiek byleby VM pozostał VM czyli klasą udostępniającą własności widokowi, a nie czymś co zawiera w sobie tonę logiki, którą można przecież przenieść do klas serwisowych (service classes).

grzesiek51114 napisał(a):

Tutaj miałem na myśli ContentFiller, nie Filter. Taki content filler miałbym wstrzyknąć do VM? Czyli miałoby to postać np. interfejsu IFiller, z którego dziedziczą poszczególne wypełniacze, dla poszczególnego okna, a każdy VM miałby w sobie referencję do obiektu typu IFiller? Czy to zbędne udziwnienie?

Aha. No to coś takiego nie musi być częścią VM, ponieważ steruje to bezpośrednio widokiem aplikacji. Można to wrzucić do eventu odpowiedzialnego za odczytywanie danej wersji językowej Twojego programu. Nie wiem czy mnie dobrze zrozumiesz po tym co napisałem więc posłużę się przykładem: Mam proszę ja Ciebie w swoim programie taki panel, który pokazuje mi aktualnie używane przez użytkownika filtry wyszukiwania. Po prostu gość wdzydza filtr, a informacja o tym jaki to filtr zostaje dodana w GUI w formie nieaktywnego guzika na pasku i w ten sposób user wie, że np. używa kaskadowo ileś tam filtrów (tak... wiem, że chodzi o filler - to tylko przykład hehe).

Teraz jak obsłużyć taką informację? Bardzo prosto. Mam wewnątrz VM takiego delegata: public delegate void ActivateFilterEventHandler(string filterName);. Na jego podstawie tworzę sobie event, który będzie się odpalał kiedy user użyje jakiegoś filtru: public event ActivateFilterEventHandler ActivateFilter;.

Super, teraz np. w komendzie należy zrobić jego wywołanie w momencie kiedy user użyje filtru:

public ICommand UseFilter
{
	get
	{
		return new RelayCommand(
		()=>
		{
			// Some logic...
			if(this.ActivateFilter != null) this.ActivateFilter(this.SurnameFilter);
		},null);
	}
}

Tak odpalony event można obsłużyć w miejscu gdzie sterujemy widokiem czyli... w code-behind:)

this.viewModel.ActivateFilter += (filterName) =>
{
	// Pseudo-code:
	var button = new Button();
	button.Content = filterName;
	button.IsEnabled = false;
	this.StackPanel.Children.Add(button);
}

Viola! GUI samo się układa. Projektant GUI decyduje w code-behind jakiego użyje sposobu na to, żeby pokazać userowi jakich użył filtrów. Ty jako projektant logiki pod spodem dostarczasz jedynie eventa, który transportuje dla GUI nazwę filtru :) Dlatego taki projektant użyje c-b zamiast XAML? Odpowiedź jest prosta: ilość nieaktywnych guzików, czy jakichkolwiek kontrolek z nazwą filtru nie jest z góry określona i nie da się tego zrobić przez XAML. Można do tego napisać sobie fabrykę kontrolek ustawiających filtry w GUI, do wyboru do koloru.

grzesiek51114 napisał(a):

Czy gotowy dokument też jest widokiem? Faktury i umowy będą tak naprawdę templatką, wypełnianą szczególnymi już danymi z poziomu programu.

Dokument też jest widokiem, owszem no, bo jakoś musi zostać wyświetlony ale pod spodem siedzi logika, którą zapewne trzeba będzie wrzucić do modelu.

0

Dzięki serdeczne, biorę się zatem za dalsze projektowanie. Pewnie się tu jeszcze odezwę. :)

0

Hey ;) Poszedłem za twoimi radami (w pewnym stopniu, nie przetrawiłem jeszcze wszystkiego, szczególnie ten fragment z eventami), ale doszedłem mniej więcej do takiego etapu (załącznik).
Co sądzicie?

1

Bardziej chodzi o taką architekturę: http://imgur.com/a/uRGMk

Po pierwsze, do wszelkiej maści purystów UML'a: diagram jest mocno uproszczony, ponieważ chodziło o pokazanie wszystkiego najbardziej przejrzyście.

  • GUI Speciffic Layer jest warstwą zależną od interfejsu graficznego, czyli co sobie projektanci powymyślają to tam właśnie wrzucą. Chcesz tworzyć dynamicznie tonę przycisków w menu? Masz od tego fabryczkę i ona pozwoli Ci to zrobić w zależności od parametrów jakie jej zapodasz. Coś jak to co pokazywałem Ci w swoim poprzednim poście;

  • Model jak widzisz składa się nie tylko z prostych klas, opisujących tabelę w bazie danych ale także z usług, które wykonują całą logikę biznesową aplikacji. W tym wypadku tworzą dokument, drukują go odpowiednio najpierw formatując czy zwracają z bazy danych listę wszystkich dokumentów. Model jest dodatkowo dość abstrakcyjny czyli bogaty w interfejsy co pozwala dokładnie określić jakich cech i zachowań będzie potrzebowała klasa, która będzie dziedziczyła od tych interfejsów. Zyskasz dzięki temu możliwość np. zmiany silnika bazy danych, którego inne warstwy nawet nie zauważą. Będziesz sobie chciał z MySQL'a przejść na Postgresa? Proszę bardzo: interfejs determinuje co musi zostać zaimplementowane i kropka - musisz się dostosować.

  • GUI Independent Layer jest warstwą niezależną od widoku (Poniekąd, bo nie zawsze jest to prawda hehe. Trywialnym przykładem może być kontrolka PasswordBox, której nie da się zbindować bezpośrednio z powodu bezpieczeństwa i trzeba przesłać ją do VM jako np. parametr komendy.) czyli generalnie mówimy tutaj o ViewModelu, który dostarcza widokowi własności, może propagować eventy, które obsłużysz gdzie chcesz (np. w code behind) oraz oczywiście o Modelu. Widać dokładnie, że VM jest to warstwą pośrednia pomiędzy modelem, a widokiem. Widać również, że model nie wie nic o widoku.

  • CRUD Communication komunikacja z silnikiem bazy danych. Operacje typu Create, Read, Update, Delete, również za pomocą ORM'a rzecz jasna.

0

Dzięki wielkie- biorę się za jakiś kod, muszę w praktyce to przetestować, wtedy będę mieć pewnie więcej pytań. ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1