PSE_learning/parts/01_patterns.tex

523 lines
21 KiB
TeX
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\part{Pattern im Projekt}
\section{Layers Pattern}
\subsection{Erkläre die Funktionsweise + Skizze}
\begin{itemize}
\item Client schickt eine Anfrage an Layer N
\item Layer N reicht da er nicht vollständig alleine beantworten kann, Anfragen an darunterliegenden Layer weiter
\item Eine Anfrage kann bei Bedarf auch in mehrere Anfragen an darunterliegende Layer geteilt werden
\item dies wird immer weiter fortgesetzt bis Layer 1 erreicht ist
\item dabei gehen Abhängigkeiten nur von oben nach unten
\end{itemize}
\begin{figure}[!htp]
\centering
\includegraphics[width=0.8\textwidth]{pics/esa_layers.jpg}
\end{figure}
\subsubsection{3 Schichten Architektur:}
\begin{itemize}
\item Data Source Layer (data): Zugriff auf Daten, kümmert sich um Kommunikation mit anderen Systemen (z.B.: Datenbank)
\begin{itemize}
\item enthält Entities > Java Repräsentation vom DB Entity
\begin{itemize}
\item im Projekt wurde eine AbstractEntity erstellt, welche die id managed
\end{itemize}
\item beinhaltet DAO und DAOImpl >> DocumentDAO, DocumentlibraryDAO
\begin{itemize}
\item damit man auf die Entities zugreifen kann.
\item um die DB zu abstrahieren.
\item enthält Methoden mit denen auf die DB zugegriffen wird
\item eine DAOException kontrolliert den Input
\begin{itemize}
\item der EntityManager Aufruf in DAOImpl befindet sich innerhalb eines Try Blocks
\item im catch wird der Cause in die DaoException gewrapped
\end{itemize}
% TODO: ??
\end{itemize}
\end{itemize}
\item Domain Layer(service): enthält Business Logik (Berechnungen, Datenvalidierung, ...)
\begin{itemize}
\item beinhaltet
\begin{itemize}
\item \textbf{Service Layer Pattern} (aka Session Fassade - siehe~\ref{sec:slp})
\item DTO >> DocumentDTO
\item Mapper >> DocumentMapper
\begin{minted}[breaklines=true]{java}
public static Document toEntity(DocumentDTO documentDTO, Document document){};
public static DocumentDTO toDTO(Document document){};
\end{minted}
\end{itemize}
\end{itemize}
\item Presentation Layer(web): serverseitig, kümmert sich um Benutzerinteraktion
\begin{itemize}
\item Controller (ViewHelper) >> DocumentController, DocumentListController
\item View (WebApp)
\end{itemize}
\end{itemize}
\begin{figure}[tph!]
\centering
\includegraphics[width=0.4\textwidth]{pics/layer-pattern}
\end{figure}
\section{Data Access Object (DAO) Pattern}
Befindet sich im Projekt in data und damit innerhalb des Data Layer.
\subsection{Erkläre die Funktion + Skizze}
\begin{itemize}
\item Client erstellt ein DAO Object und kann nach Entitäten suchen, einfügen, löschen, etc.
\item das DAO selbst soll keine spezifischen Elemente enthalten (Entity Manager, SQL Exception -> stattdessen DAOException)
\item dadurch entsteht eine Kapselung bei der die DAOImpl ohne den Client zu verändern ausgetauscht werden kann
\end{itemize}
\begin{figure}[!htp]
\centering
\includegraphics[width=0.8\textwidth]{pics/dao_pat1.jpg}
\end{figure}
\begin{minted}[breaklines=true]{java}
@ApplicationScoped
public class DocumentDAOImpl implements DocumentDAO, Serializable {
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(DocumentDAOImpl.class);
@PersistenceContext
private EntityManager entityMangaer;
@Override
public List<Document> findByCommunity(Community community) {...}
@Override
public List<Document> findByUser(User user) {...}
@Override
public void insert(Document document) {...}
@Override
public void delete(Document document) {...}
@Override
public Document findById(Long id) {...}
}
\end{minted}
\subsection{Nenne die Konsequenzen der Anwendung}
\begin{itemize}
\item Zugriff auf persistenten Speicher wird abstrahiert
\item Details des Speichers werden versteckt
\item ermöglicht einheitlichen Zugriff auf Daten
\item entkoppelt Implementierung von Persistierung (Datenbank,...)
\item ermöglicht Objektorientierte Ansicht des Speichers
\end{itemize}
\section{Service Layer Pattern (auch Session Fassade - in unserem Projekt im Domain Layer}\label{sec:slp}
\subsection{Erkläre die Funktion + Skizze}\label{subsubsec:service-layer-pattern}
\begin{itemize}
\item Der Service Layer (Ordner "`service"' im Projekt) delegiert auf die Business Logik (Zeile 68 community.setDocumentlibrary) und zum DAO (z.B. Zeile 66)
\item Bei wenig Logik wird zumindest Transaktions (Zeile 40), Error (ab Zeile 42) und Validierungshandling (ab Zeile 23) im Service erledigt
\end{itemize}
\begin{figure}[!htp]
\centering
\includegraphics[width=0.9\textwidth]{pics/sl_pat1.jpg}
\includegraphics[width=0.6\textwidth]{pics/domain-layer.png}
\end{figure}
\begin{minted}[xleftmargin=\parindent,linenos,breaklines=true]{java}
@Local(DocumentService.class)
@Remote(DocumentServiceRemote.class)
@Stateless
public class DocumentServiceImpl implements DocumentService, DocumentServiceRemote, Serializable {
private static final long serialVersionUID = -1L;
private static final Logger logger = LoggerFactory.getLogger(DocumentServiceImpl.class);
@Inject
private DocumentDAO documentDAO;
@Inject
private DocumentlibraryDAO documentlibraryDAO;
@Inject
private CommunityDAO communityDAO;
@Inject
private UserDAO userDAO;
@Inject
private MessageDAO messageDAO;
@Override
public DocumentDTO addDocument(Long communityID, String userID, byte[] data, String filename) {
Document addedDocument;
User user;
// Validierungshandling gefolgt von Error Handling
try {
if (communityID <= 0) throw new IllegalArgumentException("community must not be empty");
if (userID == null) throw new IllegalArgumentException("user must not be empty");
if (data == null) throw new IllegalArgumentException("uploaded file must not be empty");
if (filename == null) throw new IllegalArgumentException("filename must not be empty");
Documentlibrary documentlibrary = documentlibraryDAO.findByCommunityId(communityID);
//create a document library, if there isn't already one in the database
if (documentlibrary == null) {
documentlibrary = addDocumentlibrary(communityID);
}
user = userDAO.getByUserId(userID);
addedDocument = new Document(documentlibrary, user, filename, data);
documentDAO.insert(addedDocument); // Transaktionshandling
logger.info(String.format("Document %s saved in database", filename));
// Error Handling
} catch (IllegalArgumentException iaex) {
String errorMsg = "Uploading file failed (illegal argument)";
logger.error(errorMsg, iaex);
throw new ServiceException(errorMsg);
} catch (Exception ex) {
String errorMsg = String.format("Uploading file %s failed.", filename);
logger.error(errorMsg, ex);
throw new ServiceException(errorMsg);
}
String msgText = "Uploaded Document " + filename + " by user " + user.getUserId();
addMessageToStream(communityID, user, msgText, addedDocument);
return DocumentMapper.toDTO(addedDocument);
}
private void addMessageToStream(Long communityID, User user, String text, Document document) {...}
private Documentlibrary addDocumentlibrary(Long communityID) {
logger.info("Create missing documentlibrary");
Community community;
Documentlibrary documentlibrary = new Documentlibrary();
documentlibraryDAO.insert(documentlibrary); // Delegation zum DAO
community = communityDAO.findById(communityID); // Delegation zum DAO
community.setDocumentlibrary(documentlibrary); // Delegation zur Business Logik (Entity)
communityDAO.update(community); // Delegation zum DAO
return documentlibrary;
}
@Override
public List<DocumentDTO> getDocumentsFromCommunity(Long communityID) {...}
@Override
public List<DocumentDTO> getDocumentsFromUser(String userID) {...}
@Override
public void removeDocument(Long documentID) {...}
@Override
public DocumentDTO getDocumentById(Long documentID) {...}
}
\end{minted}
\subsection{Nenne die Konsequenzen der Anwendung}
\begin{itemize}
\item Reduzierung der Abhängigkeiten zwischen Presentation und Domain Layer
\item Zentralisiertes Sicherheits und Transaktionshandling
\item verbirgt vor Client Komplexität der Business Logik
\item stellt Client ein grobkörniges Interface zur Verfügung
\item gut für Remote Aufrufe geeignet (weniger Aufrufe)
\end{itemize}
\section{Model-View-Controller (MVC) Pattern}
\subsection{Erkläre die Funktion + Skizze}
MVC unterteilt eine interaktive Applikation in drei Teile: Model, View und Controller.
\begin{itemize}
\item Controller und View befinden sich im Presentation Layer und haben gegenseitig Abhängigkeiten
\item Das Model darf keine Abhängigkeiten haben (Controller und View hängen vom Model ab)
\end{itemize}
\begin{figure}[!htp]
\centering
\includegraphics[width=0.8\textwidth]{pics/mvc_pat.jpg}
\end{figure}
\subsubsection{Model}
\begin{itemize}
\item Es befinden sich Teile im Domain und Data Source Layer.
\item Das Model enthält die Kernfunktionalität und Daten. (z.B.: Datenbankzugriff)
\item Im Projekt wird dies durch die Ordner \textit{service} und \textit{data} repräsentiert
\end{itemize}
\subsubsection{View}
\begin{itemize}
\item Im Projekt im Ordner \textit{webapp} zu finden.
\item Enthält im Projekt xhtml Dateien zur Darstellung und User Interaktion
\end{itemize}
\subsubsection{Controller}
\begin{itemize}
\item Im Projekt sind Controllerklassen im Ordner \textit{web} zu finden.
\item Sie enthalten die Logik und behandeln Benutzereingaben
\end{itemize}
\section{Front Controller}
\subsection{Erkläre die Funktion + Skizze}
\begin{itemize}
\item Client schickt Request an Front Controller
\item FC erfasst nur Infos die er für die weiter Delegation braucht
\item FC gibt Request an entsprechenden ConcreteCommand oder View weiter
\item es gibt zwei Implementierungsvarianten des Controller
\begin{multicols}{2}
\begin{itemize}
\item Servlet
\item ConcreteCommand
\end{itemize}
\end{multicols}
\end{itemize}
\subsection{Servlet}
\begin{itemize}
\item Im Projekt wurde der Front Controller in Form eines Servlet realisiert,
\item die Einbindung erfolgt in der Konfigurationsdatei \textit{src/main/webapp/WEB-INF/web.xml},
\item Servlet ist eine Java-API, welche auf einem Server betrieben wird,
\item die Verarbeitung von Requests und Responses wird ermöglicht,
\item JSF und JSP können darauf aufsetzen, in unserem Projekt wurde JSF verwendet
\end{itemize}
\begin{minted}[linenos,breaklines=true]{xml}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
...
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
\end{minted}
\subsubsection{Java Server Faces (JSF)}
\begin{itemize}
\item JSF basiert auf dem MVC-Pattern
\item JSF-View-Code ist im Projekt im Ordner \textit{src/main/webapp/*} zu finden
\item JSF-Logik befindet sich in den Java-Beans (im Projekt \textit{/src/main/java/at/fhj/swd/psoe/web/*})
\item in unserem Projekt gibt es zu jeder xhtml-View eine eigene Controller-Klasse, welche dem ViewHelper-Pattern entspricht
\item in unserem Projekt kommt PrimeFaces zum Einsatz (eine konkrete Implementierungsart von JSF => Einbindung in pom.xml)
%TODO Toni fragen MVC-Aufteilung JSF
\end{itemize}
\begin{figure}[h!]
\centering
\includegraphics[width=0.5\textwidth]{pics/fc_pat.jpg}
\end{figure}
\begin{minted}[linenos,breaklines=true]{xml}
<!-- Pfad: /src/main/webapp/community/documentManagement.xhtml -->
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://xmlns.jcp.org/jsf/core"
template="communityTemplate.xhtml">
<ui:define name="communityContent">
<h1>#{msg.document_manage_title}</h1>
<f:metadata>
<f:viewAction action="#{documentListController.loadDocumentsFromCommunity()}" />
</f:metadata>
<h:form id="doclistform">
<p:commandButton value="Refresh list" actionListener="#{documentListController.loadDocumentsFromCommunity()}" update="@form doclistform"></p:commandButton>
<p:dataTable id="doclisttable" value="#{documentListController.communityDocuments}" var="docs">
<p:column class="documenttimecolumn" headerText="#{msg.document_uploaded}">#{docs.createdTimestamp}</p:column>
<p:column class="documenttimecolumn" headerText="#{msg.label_userid}">#{docs.user.userId}</p:column>
<p:column headerText="#{msg.label_filename}">#{docs.filename}</p:column>
<p:column headerText="" class="documentbuttoncolumn">
<p:commandButton value="#{msg.button_download}" ajax="false"
onclick="PrimeFaces.monitorDownload(start, stop);">
<p:fileDownload value="#{documentController.downloadDocument(docs.id)}"/>
</p:commandButton>
</p:column>
<p:column headerText="" class="documentbuttoncolumn">
<p:commandButton id="btnDel" value="#{msg.button_delete}"
actionListener="#{documentController.removeDocument(docs.id)}"
update="@form doclistform">
</p:commandButton>
</p:column>
</p:dataTable>
</h:form>
<h:form id="formdocupload" enctype="multipart/form-data">
<p:fileUpload id="fileupload"
dragDropSupport="false"
update="@form doclistform"
fileUploadListener="#{documentController.uploadDocument}"
allowTypes="/(\.|\/)(pdf|jpe?g|docx)\$/" sizeLimit="5000000"
mode="advanced" label="Add document (.pdf .jpg .docx)">
</p:fileUpload>
</h:form>
<p:messages id="feedbackBox" severity="info,error" showDetail="true" showSummary="false">
<p:autoUpdate/>
</p:messages>
</ui:define>
</ui:composition>
\end{minted}
\subsection{Nenne die Konsequenzen der Anwendung}
\begin{itemize}
\item es muss nur EIN (Front) Controller konfiguriert werden
\item da bei jedem Request ein neues Command Objekt erzeugt wird ist Thread-Safety nicht notwendig
\item da nur EIN Controller sind auch Erweiterungen durch z.B.: Decorator einfach (auch zur Laufzeit)
\end{itemize}
\section{View Helper (\textit{/src/main/java/at/fhj/swd/psoe/web/*})}
\subsection{Erkläre die Funktion + Skizze}
\begin{figure}[h!]
\centering
\includegraphics[width=0.8\textwidth]{pics/view-helper_pat1.jpg}
\end{figure}
\begin{itemize}
\item View (xhtml-Dateien im Ordner \textit{/src/main/webapp/*}) delegiert Aufgaben an Helper (z.B. DocumentController im Ordner web)
\item Helper adaptieren View zu Model (Klassen in den Ordnern \textit{src/main/java/at/fhj/swd/psoe/service/*} und \textit{src/main/java/at/fhj/swd/psoe/data/*})
\item in View befindet sich HTML Code im ViewHelper Java Code zur Aufbereitung der Daten (+ wenig HTML)
\end{itemize}
\subsection{Nenne die Konsequenzen der Anwendung}
\begin{itemize}
\item kapselt Design-Code in View und View-Processing-Code Logik in Helper
\item steigert Wiederverwendbarkeit, Wartbarkeit und Strukturierungsqualität der Anwendung
\item vereinfacht Tests (Helperfunktionen ohne View)
\item bessere Trennung zwischen
\begin{itemize}
\item Presentation und Data Source Layer
\item Entwickler und Designer
\end{itemize}
\end{itemize}
\section{Dependency Injection (CDI-Framework -> eingebunden im \textit{./pom.xml})}
\subsection{Erkläre die Funktion + Skizze}
%TODO Zarwos is die Dependency-Injection in unserem Projekt guat und wo is se, verdammte Scheiße noch amol, wo konfigurierst denn den Dreck donn überhaupt???
\begin{figure}[!htp]
\centering
\includegraphics[width=0.8\textwidth]{pics/dependency_inj_pat.jpg}
\end{figure}
\begin{itemize}
\item ein Hauptgrund für Dependency Injection ist die Trennung zwischen Business- und Instanzierungslogik
\item Klassen sollen nur noch Abhängigkeiten auf Interfaces haben
\item die Verwendung des new Operators stattdessen würde eine Abhängigkeit zur Implementierungsklasse herstellen
\item Implementierungsklassen sollen auf Interfaces referenzieren, wobei es ihnen egal wie diese Referenz hergestellt wird
\item Die Instanziierung wird in die sogenannte Assembler Klasse "`herausgezogen"'
\item Objekte werden mittels des Assembler "`gebaut"'.
\item Zudem instanziiert er die Objekte und injected die Referenzen
\item So hat er Client nur mehr eine Abhängigkeit auf das Interface
\item wird die Implementierung getauscht, so geschieht das direkt im Assembler ohne den Client zu beeinflussen
\item der Container erledigt DI über @Inject indem er prüft welche Klasse das Interface implementiert und entsprechende Referenz bei der Annotation einhängt
\item Es wird zwischen Constructor Injection und Setter Injection unterschiedlichen
\begin{minted}[linenos,breaklines=true]{java}
// Constructor Injection
puplic class Client {
private Interface iface;
public Client(Interface iface) {
this.iface = iface;
}}
// Setter Injection
puplic class Client {
private Interface iface;
public setIface(Interface iface)
{
this.iface = iface;
}}
\end{minted}
\end{itemize}
\subsection{Nenne die Konsequenzen der Anwendung}
\begin{itemize}
\item loose gekoppelte Objekte
\item Referenzen nur noch auf Interfaces
\item hohe Flexibilität (Strategy, Proxy,..)
\item bessere Erweiterbarkeit und Testbarkeit
\end{itemize}
\section{Data Transfer Object (DTO) Pattern}
\subsection{Erkläre die Funktion (Skizze - ein Grund für DTO)}
\begin{figure}[!htp]
\centering
\includegraphics[width=0.9\textwidth]{pics/lok-vs-remote.jpg}
\end{figure}
\begin{itemize}
\item Transportiert Daten zwischen Prozessen um Remote Methodenaufrufe zu minimieren
\item besteht aus Fields, Getter und Setter
\item fasst Daten verschiedener Objekte zusammen die vom Remote Objekt benötigt werden
\item ev. Map, Record Set, ... -> um Anzahl der Aufrufe zu minimieren
\end{itemize}
\begin{minted}[linenos,breaklines=true]{java}
package at.fhj.swd.psoe.service.dto;
import java.io.Serializable;
import java.util.Date;
public class DocumentDTO implements Serializable {
private static final long serialVersionUID = 4016557982897997689L;
private Long id;
private Long documentlibraryID;
private String filename;
private UserDTO user;
private byte[] data;
private Date createdTimestamp;
public DocumentDTO() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getDocumentlibraryID() {
return documentlibraryID;
}
public void setDocumentlibraryID(Long documentlibraryID) {
this.documentlibraryID = documentlibraryID;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public UserDTO getUser() {
return user;
}
public void setUser(UserDTO user) {
this.user = user;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public Date getCreatedTimestamp() {
return createdTimestamp;
}
public void setCreatedTimestamp(Date createdTimestamp) {
this.createdTimestamp = createdTimestamp;
}
@Override
public String toString() {
return "DocumentDTO{" +
"id=" + id +
", documentlibraryID=" + documentlibraryID +
", filename='" + filename + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof DocumentDTO)) return false;
DocumentDTO that = (DocumentDTO) o;
return id.equals(that.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
}
\end{minted}
\subsection{Konsequenzen der Anwendung}
\begin{itemize}
\item kapselt und versteckt
\item nimmt Komplexität
\item steigert Effizienz da weniger Aufrufe über Remotegrenze
\end{itemize}