Trochę przyzwoita aplikacja internetowa składa się z mieszanki wzorców projektowych. Wymienię tylko te najważniejsze.
Podstawowy (architektoniczny) wzorzec projektowy, którego chcesz użyć, to wzorzec Model-View-Controller . Kontroler ma być reprezentowana przez aplet, który (w) bezpośrednio tworzy / używa konkretny model i widok na podstawie wniosku. Model ma być reprezentowane przez klasy JavaBeans. Jest to często podzielne w modelu biznesowym, który zawiera działania (zachowanie) i modelu danych, który zawiera dane (informacje). View ma być reprezentowane przez pliki JSP, które mają bezpośredni dostęp do ( danych ) model EL (Expression Language).
Następnie istnieją odmiany zależne od tego, jak obsługiwane są akcje i zdarzenia. Popularne to:
MVC oparty na zapytaniach (działaniach) : jest to najprostszy do wdrożenia. The ( Biznes ) model współpracuje bezpośrednio z HttpServletRequest
i HttpServletResponse
obiekty. Musisz samodzielnie zebrać, przekonwertować i zweryfikować parametry żądania. View może być reprezentowany przez plain vanilla HTML / CSS / JS i nie utrzymania stanu całej żądań. Tak działa między innymi Spring MVC , Struts and Stripes .
Komponenty MVC : trudniejsze do wdrożenia. Ale kończy się to prostszym modelem i widokiem, w którym wszystkie „surowe” API serwletów są całkowicie oderwane. Nie powinno być potrzeby samodzielnego gromadzenia, konwertowania i sprawdzania parametrów żądania. Controller robi to zadanie i ustawia zebrane, przeliczone i potwierdzone parametry żądania w Modelu . Wystarczy zdefiniować metody działania, które będą działać bezpośrednio z właściwościami modelu. View jest reprezentowany przez „składniki” w smaku taglibs JSP lub elementów XML, który z kolei generuje HTML / CSS / JS. Stan widokudla kolejnych żądań jest utrzymywany w sesji. Jest to szczególnie przydatne w przypadku konwersji, sprawdzania poprawności i zmian wartości po stronie serwera. Tak między innymi JSF , Wicket i Play! Pracuje.
Na marginesie, hobby ze stworzonym przez siebie frameworkiem MVC jest bardzo przyjemnym ćwiczeniem edukacyjnym i polecam je, dopóki przechowujesz je do celów osobistych / prywatnych. Ale kiedy przejdziesz do profesjonalizmu, zdecydowanie zaleca się wybranie istniejącego frameworka, zamiast odkrywania go na nowo. Nauka istniejących i dobrze rozwiniętych ram zajmuje o wiele mniej czasu niż samodzielne opracowanie i utrzymanie solidnych ram.
W poniższym szczegółowym wyjaśnieniu ograniczę się do MVC opartego na żądaniach, ponieważ jest to łatwiejsze do wdrożenia.
Najpierw część kontrolera powinna implementować wzór kontrolera frontowego (który jest specjalistycznym rodzajem wzoru mediatora ). Powinien składać się tylko z jednego serwletu, który zapewnia scentralizowany punkt wejścia dla wszystkich żądań. Powinien utworzyć model na podstawie informacji dostępnych w żądaniu, takich jak pathinfo lub ścieżka serwletu, metoda i / lub określone parametry. Model biznesowy jest nazywany Action
w poniższym HttpServlet
przykładzie.
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
Action action = ActionFactory.getAction(request);
String view = action.execute(request, response);
if (view.equals(request.getPathInfo().substring(1)) {
request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
}
else {
response.sendRedirect(view); // We'd like to fire redirect in case of a view change as result of the action (PRG pattern).
}
}
catch (Exception e) {
throw new ServletException("Executing action failed.", e);
}
}
Wykonanie akcji powinno zwrócić jakiś identyfikator, aby zlokalizować widok. Najprościej byłoby użyć go jako nazwy pliku JSP. Map tego apletu na określonym url-pattern
w web.xml
, na przykład /pages/*
, *.do
albo nawet po prostu *.html
.
W przypadku prefix-wzory jak na przykład /pages/*
można było powołać się URL jak http://example.com/pages/register , http://example.com/pages/login , etc i zapewnienia /WEB-INF/register.jsp
, /WEB-INF/login.jsp
z odpowiednim GET i POST działań . Części register
, login
itp Następnie dostępnych request.getPathInfo()
, jak w powyższym przykładzie.
Kiedy używasz sufiks wzorców jak *.do
, *.html
itp, to można powołać się URL jak http://example.com/register.do , http://example.com/login.do itp i trzeba zmienić przykłady kodu w tej odpowiedzi (także ActionFactory
), aby zamiast tego wyodrębnić części register
i .login
request.getServletPath()
Action
Powinny być zgodne z wzorca Strategy . Należy go zdefiniować jako typ abstrakcyjny / interfejs, który powinien wykonywać pracę na podstawie przekazanych argumentów metody abstrakcyjnej (jest to różnica w stosunku do wzorca poleceń , przy czym typ abstrakcyjny / interfejs powinien wykonywać pracę opartą na argumenty, które zostały przekazane podczas tworzenia implementacji).
public interface Action {
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Możesz Exception
bardziej sprecyzować wyjątek niestandardowy, taki jak ActionException
. To tylko podstawowy przykład rozpoczęcia, reszta zależy od Ciebie.
Oto przykład, LoginAction
który (jak sama nazwa wskazuje) loguje użytkownika. User
Sama jest z kolei model danych . View jest świadoma obecnością User
.
public class LoginAction implements Action {
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = userDAO.find(username, password);
if (user != null) {
request.getSession().setAttribute("user", user); // Login user.
return "home"; // Redirect to home page.
}
else {
request.setAttribute("error", "Unknown username/password. Please retry."); // Store error message in request scope.
return "login"; // Go back to redisplay login form with error.
}
}
}
ActionFactory
Powinny być zgodne z metodą wzorca fabryczne . Zasadniczo powinien zapewniać metodę kreacji, która zwraca konkretną implementację typu abstrakcyjnego / interfejsu. W takim przypadku powinien zwrócić implementację Action
interfejsu na podstawie informacji podanych w żądaniu. Na przykład metoda i pathinfo (pathinfo to część za kontekstem i ścieżką serwletu w adresie URL żądania, z wyłączeniem ciągu zapytania).
public static Action getAction(HttpServletRequest request) {
return actions.get(request.getMethod() + request.getPathInfo());
}
To actions
z kolei powinno być statyczne / Map<String, Action>
obejmujące całą aplikację, które przechowuje wszystkie znane działania. Od Ciebie zależy, jak wypełnić tę mapę. Kodowanie:
actions.put("POST/register", new RegisterAction());
actions.put("POST/login", new LoginAction());
actions.put("GET/logout", new LogoutAction());
// ...
Lub konfigurowalny na podstawie pliku konfiguracyjnego właściwości / XML w ścieżce klasy: (pseudo)
for (Entry entry : configuration) {
actions.put(entry.getKey(), Class.forName(entry.getValue()).newInstance());
}
Lub dynamicznie w oparciu o skanowanie w ścieżce klas dla klas implementujących określony interfejs i / lub adnotację: (pseudo)
for (ClassFile classFile : classpath) {
if (classFile.isInstanceOf(Action.class)) {
actions.put(classFile.getAnnotation("mapping"), classFile.newInstance());
}
}
Pamiętaj, aby utworzyć „nic nie rób” Action
w przypadku braku mapowania. Niech na przykład zwróci bezpośrednio request.getPathInfo().substring(1)
wtedy.
Inne wzory
To były dotychczas ważne wzory.
Aby przejść o krok dalej, możesz użyć wzorca Fasada, aby utworzyć Context
klasę, która z kolei otacza obiekty żądania i odpowiedzi oraz oferuje kilka wygodnych metod delegowania do obiektów żądania i odpowiedzi i Action#execute()
zamiast tego przekazuje ten argument jako metodę. Dodaje to dodatkową warstwę abstrakcyjną, aby ukryć surowy API Servleta. Powinieneś więc zasadniczo zerować import javax.servlet.*
deklaracje przy każdej Action
implementacji. W kategoriach JSF to właśnie robią klasy FacesContext
i ExternalContext
. Konkretny przykład można znaleźć w tej odpowiedzi .
Następnie jest wzorzec stanu dla przypadku, w którym chcesz dodać dodatkową warstwę abstrakcji, aby podzielić zadania gromadzenia parametrów żądania, konwertowania ich, sprawdzania ich poprawności, aktualizowania wartości modelu i wykonywania akcji. W kategoriach JSF to właśnie LifeCycle
robi.
Następnie jest wzorzec złożony dla przypadku, w którym chcesz utworzyć widok oparty na komponencie, który można dołączyć do modelu i którego zachowanie zależy od stanu cyklu życia opartego na żądaniach. W kategoriach JSF UIComponent
reprezentują to.
W ten sposób możesz stopniowo ewoluować w kierunku struktury opartej na komponentach.
Zobacz też: