MVC -podstawowe pytania

0

Hej przeczytałam kilka tutoriali i mam kilka pytań odnośnie MVC

  1. W modelu siedzi klasa Student i StudentsBase, operuję głównie na StudentsBase , naciskam sobie w widoku "Sort Students" - sortowaniem powinien zająć się kontroler i odpowiednio uaktualnić model czy model powinien udostępniać prostą metodę do sortowania (dla kontrolera) np. students.Sort()? Bo np. gdy naciskam w widoku "Show Smokers" i chcę tylko wyświetlić sobie palących studentów (dane w bazie nie ulegną zmianie) to domyślam się, że to już zadanie dla kontrolera, który pobiera sobie dane z modelu i je odpowiednio przetwarza tak? (a potem to co przetworzył przekazuje do widoku)

  2. Obecnie w main mam coś takiego:

Model model = new Model();
View view = new View(model);
Controller controller = new Controller(model,view);

przez to mam tylko jedną klasę w kontrolerze do "komunikacji" z modelem o nazwie Model. Będę musiała nawrzucać tam sporo metod - tak się robi czy można stworzyć sobie więcej klas i je przekazywać do kontrolera np. Controller controller = new Controller(groupsModel,studentsModel,view); itd... ?

  1. jest klasa StudentsDatabase- jak powinno wyglądać z perspektywy kontrolera dodanie nowego studenta do bazy studentów tj. modelu?
    students.add(String name, int age, boolean isDrunk); (wtedy tych parametrów może być dużo)
    czy
Student s = new Student(String name, int age);
s.learning(String learningWhat);
s.sleeping();
students.add(s);

tylko wtedy będę musiała zrobić klasę Student w modelu publiczną i dodać w kontrolerze import model.Student;

--no właśnie tutaj mam kolejne pytanie:
4. Obecnie w pakiecie model na klasę Model (która jest jedyną z tego pakietu używaną zewnętrznie) składa mi się kilka mniejszych klas. Żeby je ukryć nie daję żadnego modyfikatora tj;
"default – pozwala na dostęp do danego elementu tylko klasom z danego pakietu (nie istnieje słowo w Javie określające ten rodzaj dostępu, jeżeli chcemy go użyć to po prostu nie podajemy żadnego modyfikatora)"

czy to jest słuszna praktyka?

PS jeżeli znacie jakieś ciekawe materiały o MVC z przykładami w Javie (jak to się prawidłowo robi) to proszę o link :p .
znalazłam coś takiego: http://www.know-how.info.pl/programowanie/java/wzorce-projektowe/klasyczny-mvc/
Pozdrawiam

1
  1. Ale to sortowanie chcesz przeprowadzić faktycznie na modelu (tzn to jest "posortuj mi wszystkich studentów w bazie!") czy chcesz to sobie posortować w widoku po prostu? Jeśli to drugie to ja bym powiedział że to jest zadanie widoku na dobrą sprawę ;]
    Jeśli chodzi o smokerów to nie do końca. Bo rzadko kiedy mamy coś takiego jak faktycznie tylko 3 elementy -> model, widok, kontroler. W praktyce pomiędzy danymi w bazie a kontrolerami jest wiele poziomów logiki biznesowej, więc można powiedzieć że "model" jest mocno rozbudowany. W realnym systemie, w trywialnym przypadku, byłaby warstwa dostępu do danych a nad nią byłaby warstwa "serwisów" z której korzystałyby kontrolery. Kontroler poprosiłby odpowiedni serwis o dane palaczy a potem zwrócił by te dane do widoku.

  2. Raczej robi się tych klas wiele. Bardzo wiele. Dlatego normalni ludzie używają kontenerów IoC a nie tworzą obiekty przez new ;) Bo Kontrolerów też może być więcej niż jeden (podpięte w różnych miejscach) i widoków oczywiście też. Bo jak masz aplikację większą niż hello world to przecież taki obiekt "Kontroler" to by był jakis God-Object (patrz: antywzorzec projektowy).

  3. Ani jedno ani drugie tak naprawdę. Powinna być osobna klasa (!) na dane studenta która jest wypełniana danymi wprowadzonymi przez uzytkownika. Od biedy można użyć samej klasy Student, ale jak korzystasz później z ORMów to powoduje to czasem pewne kłopoty jak ktoś nie do końca rozumie jak działają transakcje i sesje. W twoim przypadku ta druga opcja jest sensowniejsza.

  4. Istnieje słowo i brzmi ono "package private" / dostęp pakietowy. Nie rozumiem pytania. Pytasz czy słuszne jest ukrywanie klas których nikt nie powinien widzieć? Tak ;]

1

@karolinaa

  1. Nie dokładnie tak
    Kontroler pełni pewną warstwę abstrakcji pomiędzy klientem a aplikacją (dla kontrolera nie jest istotne czy user klika w aplikacji czy żądania przychodzą z http). Kontroler ma za zadanie po prostu delegować żądania. W Twoim przypadku: klikasz sort na widoku -> na kontrolerze wywołujesz sort, np.
public void sort() {
    model.sort();
}

a całą logikę biznesową realizuje model i jego serwisy. Jak już serwis posortuje odpowiednio dane czy też za pomocą odpowiedniego sql'a wyciągniesz je z bazy to tylko uaktualniasz (ustawiasz modelowi kolekcję z odpowiednimi danymi) a obserwator update'uje widok. Widać tutaj że kontroler uczestniczy tylko w przekazaniu żądania - nigdzie więcej. Implementując bardzo prosty wzorzec Obserwator (w standardowym API lub swój) sprawisz, że widok uaktualni się "automatycznie" gdy tylko zmieni się stan danych.

Ale jeżeli nie masz zamiaru zmieniać danych w bazie ani w modelu to jak kolega wyżej napisał to zadanie widoku.

  1. W tym przypadku konstruktory MVC wyglądają ok.
    Ja jednak tworzę odpowiedni widok w kontrolerze, tam też dla np. aplikacji z GUI wiąże listenery, dzięki czemu odpowiednio deleguje żądania z np. kliknięcia na jakiś button do modelu.
    Jak koledzy wyżej napisali: wiele klas i pamiętaj że aplikacja może korzystać z wielu modeli, widoków i kontrolerów.

  2. Danych dla studenta prawdopodobnie tyle ile zdefiniujesz na widoku.
    Mogło by to wyglądać np tak, że w kontrolerze masz metodę:

public addStudent(param1, param2, ...) {
    model.addStudent(param1, param2, ...);
}

Gdyby tych parametrów było bardzo dużo możesz np. użyć wzorca Builder. Kontroler nie powinien wiedzieć że istnieje coś takiego jak Student - ba, nawet go to nie obchodzi.

  1. To jest słuszna praktyka jeżeli chcesz mieć "zasięg pakietowy".
0

Tak chodziło mi o sortowanie bez wpływu na model, no ale okey czyli już wiem, że to zadanie dla widoku. Co do zasięgu też już wiem. Mam teraz inny kłopot - czy sprawdzać poprawność wprowadzonych danych w widoku ma widok i tylko widok? np. mam w widoku metodę zwracającą wprowadzone ID:

public int getStudentId() {
	return new Integer(txtId.getText());
}

a użytkownik nie poda ID albo poda ID, które nie istnieje w bazie i kliknie na btnDelStudent. I teraz nie umiem tego zrobić. Mam dwa ActionListenery dla tego przycisku jeden w kontrolerze i jeden w widoku. zmieniłam w widoku metodę zwracającą wprowadzone id:

public int getStudentAge() {
	try {
		return new Integer(txtAge.getText());
	} catch (NumberFormatException e) {
		return -1;
	}
}

i po prostu za każdym razem w kontrolerze sprawdzam id zwrócone z widoku:

/* ActionListener w kontrolerze dla klikniecia w btnDelStudent */
class delStudentListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
		int id = view.getStudentId();
		if (id < 0 || id > model.getNofStudents()) {
			view.addToConsole("ERROR: podales zle ID");
			return;
		}
		Student s = model.getStudent(id);
		view.addToConsole("WYKASUJE " + s);
		model.delStudent(id);
	}
}

Ale czy to tak się powinno robić?
I ostatnie pytanie - widzę tutaj http://www.know-how.info.pl/programowanie/java/wzorce-projektowe/klasyczny-mvc/ ,że widok zmienił coś w modelu bez udziału kontrolera.
Czy jest dobra praktyka?

@Shalom
Dobra kontenery IoC, serwisy itd... ale wiesz ja póki co jestem początkująca.

1

A bym rzucił wyjątkiem zamiast zwracać jakąś arbitralną wartość określającą kod błędu ;)

Dobra kontenery IoC, serwisy itd... ale wiesz ja póki co jestem początkująca.

Tym bardziej powinnaś uczyć się dobrych praktyk ;)

1

Modyfikowanie modelu przez widok jest niedopuszczalne! Widok - jak nazwa wskazuje ma wyświetlać to co "oferuje" model.

Co do kodu:

public int getStudentAge() {
    try {
        return new Integer(txtAge.getText());
    } catch (NumberFormatException e) {
        return -1;
    }
}

Raczej nie należy programować defensywnie. Ogólne założenie metody jest OK: get czyli zwróć poprawną wartość albo Exception. Ale try/catch wygląda tutaj tak jakbyś spodziewała się błędu. Jak wyżej: lepiej rzucić Exception lub oddelegować go dalej:

public int getStudentAge() throws NumberFormatException {
    return Integer.parseInt(txtAge.getText());
}

niż zwracać -1.

0
dzanesko napisał(a):

Modyfikowanie modelu przez widok jest niedopuszczalne! Widok - jak nazwa wskazuje ma wyświetlać to co "oferuje" model.

Okey czyli ten model, który przekazuje do widoku View view = new View(model); mogę go rozumiem używać tylko "read-only" ? (metody typu np. `model.get*)

1

Tak, tylko i wyłącznie w trybie read-only.

0

finalnie zrobiłam to tak w kontrolerze:

class delStudentListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
		try {
			int id = view.getStudentId();
			Student s = model.getStudent(id);
			model.delStudent(id);
			view.addToConsole("Usuwam " + s);
		} catch(NumberFormatException ee) {
			view.addToConsole("ERROR: ID tylko cyfry");
		} catch(IndexOutOfBoundsException aa) {
			view.addToConsole("ERROR: Nie ma takiego ID");
		}
	}
}

view.getStudentID() rzuca wyjątkiem a model.getStudent(id); rzuca tym drugim (IndexOutOfBoundsException).
model.delStudent(); ani view.addToConsole() nie rzuca żadnym, ale jeszcze o tym pomyślę w przyszłości (muszę doczytać o tych wyjątkach).
Dzięki za pomoc

0

Nie wiem dokładnie jak wygląda cała implementacja ale:

  1. pobierasz id z gui
  2. tworzysz studenta pobierając go z modelu
  3. następnie usuwasz tego studenta
  4. i na koniec wyświetlasz już nie istniejącego studenta

Zamiast klasy delStudentListener w kontrolerze zrobiłbym metodę:

public void deleteStudent(int id) {
    model.deleteStudent(id);
}

w modelu:

public void deleteStudent(id) {
    // usuwanie studenta
    // update widoku
}

Kontroler powinien zdelegować akcję do modelu a ten powinien zająć się logiką.
Exception typu "litera zamiast cyfry" możesz walidować na widoku wyśweitlając np. komunikat aż do momentu wprowadzenia cyfry. Model, który dostanie już poprawną wartość w zależności od tego czy student istnieje czy też nie uaktualni odpowiednio widok.

0

@dzanesko racja poprawiłam.

dzanesko napisał(a):

w modelu:

public void deleteStudent(id) {
    // usuwanie studenta
    // update widoku
}

Kontroler powinien zdelegować akcję do modelu a ten powinien zająć się logiką.

to model może sterować widokiem? czyli tak teoretycznie:

  • Widok: może korzystać ro model
  • Kontroler: może korzystać rw z modelu i odświeżać widok
  • Model: może odświeżać widok?
1

@karolinaa w klasycznym MVC:

  • widok wyświetla aktualny stan Modelu (co może wymagać użycia na przykład Observera żeby wiedzieć kiedy model się zmienił, czyli to co nazwałaś "odświeżaniem widoku przez model")
  • kontroler wywołuje akcje na modelu
  • model przechowuje stan "logiki" aplikacji
    W MVP różnica jest taka że widok nie "obserwuje" modelu, a zamiast tego to Prezenter (zmodyfikowany Kontroler) zajmuje się informowaniem Widoku o zmianie Modelu (to co nazwałas "odświeżaniem widoku przez kontroler").
    Ale nie polecam mieszać tych 2 podejść, bo trudno się potem odnaleźć w kodzie kiedy pewne operacje są w różnych miejscach w kodzie zaimplementowane w różny sposób.
1
  • Widok: może korzystać ro model: TAK
  • Kontroler: może korzystać rw z modelu i odświeżać widok: NIE
  • Model: może odświeżać widok?: TAK (nawet musi)

Kontroler: jak wyżej pisałem, to pewna warstwa abstrakcji pomiędzy userem a app. Może on jedynie wywoływać metody modelu jak w powyższym kodzie, jednak już nie uczestniczy w update widoku (bo robi to model). Model "organizuje" się sam dostając parametry od kontrolera. Podsumowując kontroler pobiera z widoku i przekazuje modelowi czyli działa w trybie 'r' dla widoku i tyle.

0

OK dzięki

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