diff --git a/Documentation/programmEntwurf.org b/Documentation/programmEntwurf.org index 3098189..8446484 100644 --- a/Documentation/programmEntwurf.org +++ b/Documentation/programmEntwurf.org @@ -1,70 +1,19 @@ -Programmentwurf +#+TITLE: Programmentwurf - LinkDitch +#+AUTHOR: valentin.theodor@web.de (Klarname und Matrikelnummer siehe Mail) +#+DATE: Abgabedatum 29.05.2022 +#+options: toc:1 -[Bezeichung] -Name: [Name, Vorname]\\ -Martrikelnummer: [MNR] -Abgabedatum: [DATUM] -* - :PROPERTIES: - :CUSTOM_ID: section - :END: -Allgemeine Anmerkungen: -- es darf nicht auf andere Kapitel als Leistungsnachweis verwiesen - werden (z.B. in der Form “XY wurde schon in Kapitel 2 behandelt, daher - hier keine Ausführung”) - -- alles muss in UTF-8 codiert sein (Text und Code) - -- sollten mündliche Aussagen den schriftlichen Aufgaben widersprechen, - gelten die schriftlichen Aufgaben (ggf. an Anpassung der schriftlichen - Aufgaben erinnern!) - -- alles muss ins Repository (Code, Ausarbeitung und alles was damit - zusammenhängt) - -- die Beispiele sollten wenn möglich vom aktuellen Stand genommen werden - - - finden sich dort keine entsprechenden Beispiele, dürfen auch ältere - Commits unter Verweis auf den Commit verwendet werden - - Ausnahme: beim Kapitel “Refactoring” darf von vorne herein aus allen - Ständen frei gewählt werden (mit Verweis auf den entsprechenden - Commit) - -- falls verlangte Negativ-Beispiele nicht vorhanden sind, müssen - entsprechend mehr Positiv-Beispiele gebracht werden - - - Achtung: werden im Code entsprechende Negativ-Beispiele gefunden, - gibt es keine Punkte für die zusätzlichen Positiv-Beispiele - - - Beispiele - - - “Nennen Sie jeweils eine Klasse, die das SRP einhält bzw. - verletzt.” - - - Antwort: Es gibt keine Klasse, die SRP verletzt, daher hier 2 - Klassen, die SRP einhalten: [Klasse 1], [Klasse 2] - - Bewertung: falls im Code tatsächlich keine Klasse das SRP - verletzt: volle Punktzahl ODER falls im Code mind. eine Klasse - SRP verletzt: halbe Punktzahl - -- verlangte Positiv-Beispiele müssen gebracht werden - -- Code-Beispiel = Code in das Dokument kopieren * Kapitel 1: *Einführung* :PROPERTIES: :CUSTOM_ID: kapitel-1-einführung :END: *** Übersicht über die Applikation - :PROPERTIES: - :CUSTOM_ID: übersicht-über-die-applikation - :END: -[Was macht die Applikation? Wie funktioniert sie? Welches Problem löst -sie/welchen Zweck hat sie?] +Der Name der Anwendung ergibt sich aus einem Wortspiel und ist inspiriert von dem Programm [[https://github.com/victoriadrake/link-snitch][LinkSnitch]]. Die Anwendung ist zur organisierten Abspeicherung von Links bzw. URLs gedacht. Diese können durch die Anwendung abgespeichert werden. @@ -73,14 +22,9 @@ Zusätzlich kann die Anwendung auch Tags zu Links hinzufügen können, wenn die Eigene Regeln für Tags können auch angelegt werden, sie werden durch einen Regulären Ausdruck beschrieben. Der User, welcher einen Eintrag angelegt hat wird auch gespeichert. -TODO: Die Anwendung soll Persistenz enthalten sowie verschiedene Methoden zum Durchsuchen (nach Kategorie, User) und Exportieren der Daten. +Die Anwendung enthält Persistenz in Form von CSV-Dateien sowie verschiedene Methoden zum Durchsuchen (nach Kategorie, User, Tag und gruppiert nach Hostname) der Daten. *** Wie startet man die Applikation? - :PROPERTIES: - :CUSTOM_ID: wie-startet-man-die-applikation - :END: -[Wie startet man die Applikation? Welche Voraussetzungen werden -benötigt? Schritt-für-Schritt-Anleitung] **** Voraussetzungen: - Java 17 - Maven @@ -91,16 +35,41 @@ mvn clean install #+end_src **** Ausführen - #+begin_src + #+begin_src shell java -jar 0-Plugin/target/0-Plugin-1.0-SNAPSHOT-jar-with-dependencies.jar [PARAMETER] #+end_src + oder alternativ mit dem beigelegten Shell-Script + + #+begin_src shell +./link-ditch [PARAMETER] + #+end_src + +Die Verfügbaren Commands der Anwendung sollten recht selbsterklärend sein und werden aufgelistet, wenn man die Anwendung ohne Parameter startet. + +#+begin_src shell +$ ./link-ditch +All available subcommands: + +Usage: +link add http://example.org yourUsername [category1 category2 .. categoryN] +link get +link category aCategoryName +link tag aTagName +link user aUserName +link hosts + +Usage: +tag add tagName tagRegexExpression + +Usage: +category add categoryName +category get +#+end_src + +Die Speicherung der Daten erfolgt in CSV-Dateien, die im Workingdirectory abgelegt werden. + *** Wie testet man die Applikation? - :PROPERTIES: - :CUSTOM_ID: wie-testet-man-die-applikation - :END: -[Wie testet man die Applikation? Welche Voraussetzungen werden benötigt? -Schritt-für-Schritt-Anleitung] #+begin_src mvn clean test @@ -110,10 +79,6 @@ mvn clean test :CUSTOM_ID: kapitel-2-clean-architecture :END: *** Was ist Clean Architecture? - :PROPERTIES: - :CUSTOM_ID: was-ist-clean-architecture - :END: -[allgemeine Beschreibung der Clean Architecture in eigenen Worten] Die Clean Architecture ist eine Software Architektur, die es ermöglichen soll langlebige Systeme zu entwickeln. Dazu wird der eigentliche Zweck einer Anwendung von möglichst vielen technischen Details getrennt. @@ -130,21 +95,9 @@ Die konkreten Schichten sind (von langlebig nach kurzlebig und von wenigen (kein Durch die Dependency Rule wird sichergestellt, dass Abhängigkeiten immer von außen nach innen sind, und somit eine äußere Schicht ausgetauscht werden könnte, ohne, dass die inneren Schichten angepasst werden müssten. *** Analyse der Dependency Rule - :PROPERTIES: - :CUSTOM_ID: analyse-der-dependency-rule - :END: -[(1 Klasse, die die Dependency Rule einhält und eine Klasse, die die -Dependency Rule verletzt); jeweils UML der Klasse und Analyse der -Abhängigkeiten in beide Richtungen (d.h., von wem hängt die Klasse ab -und wer hängt von der Klasse ab) in Bezug auf die Dependency Rule] - - Da die Abhängigkeiten zwischen den einzelnen Schichten durch Maven restriktiv kontrolliert werden gibt es kein negativ Beispiel. **** Positiv-Beispiel: Dependency Rule - :PROPERTIES: - :CUSTOM_ID: positiv-beispiel-dependency-rule - :END: [[./uml/LinkCliAdapter.png]] @@ -163,18 +116,8 @@ In der Plugin Schicht ist mit dem GenericCSVDAO eine Klasse gegeben, die dies Das Domänen Repository CategoryRepository ist abhängig von einem PersistenceAdapter (Interface), welche das CSVCategoryPersistenceAdapter implementiert. Somit ist das Repository der Domäne zur Compile Zeit nicht abhängig von dem CSVCategoryPersistenceAdapter des Adapter sondern nur zur Runtime, da im Adapter eine Implementation des benötigten Interfaces liegt. *** *Analyse der Schichten* - :PROPERTIES: - :CUSTOM_ID: analyse-der-schichten - :END: -[jeweils 1 Klasse zu 2 unterschiedlichen Schichten der -Clean-Architecture: jeweils UML der Klasse (ggf. auch zusammenspielenden -Klassen), Beschreibung der Aufgabe, Einordnung mit Begründung in die -Clean-Architecture] **** Schicht: Domain - :PROPERTIES: - :CUSTOM_ID: schicht-name - :END: [[./uml/LinkUrl.png]] @@ -183,9 +126,6 @@ Clean-Architecture] So stellt sie beispielsweise durch die Verwendung der Java Klasse URL sicher, dass die Url ein valides Format hat und somit die Domänenregeln erfüllt. **** Schicht: Plugin - :PROPERTIES: - :CUSTOM_ID: schicht-name-1 - :END: [[./uml/GitHubTagMatcher.png]] @@ -200,9 +140,6 @@ Clean-Architecture] :PROPERTIES: :CUSTOM_ID: analyse-single-responsibility-principle-srp :END: -[jeweils eine Klasse als positives und negatives Beispiel für SRP; -jeweils UML der Klasse und Beschreibung der Aufgabe bzw. der Aufgaben -und möglicher Lösungsweg des Negativ-Beispiels (inkl. UML)] **** Positiv-Beispiel :PROPERTIES: @@ -230,11 +167,6 @@ Dies ist jedoch nicht ihre Responsibility. :PROPERTIES: :CUSTOM_ID: analyse-open-closed-principle-ocp :END: -[jeweils eine Klasse als positives und negatives Beispiel für OCP; -jeweils UML der Klasse und Analyse mit Begründung, warum das OCP -erfüllt/nicht erfüllt wurde -- falls erfüllt: warum hier -sinnvoll/welches Problem gab es? Falls nicht erfüllt: wie könnte man es -lösen (inkl. UML)?] **** Positiv-Beispiel :PROPERTIES: @@ -307,9 +239,6 @@ UML nicht vorhanden, da es schneller wäre den Fix einzubauen und das UML zu gen :PROPERTIES: :CUSTOM_ID: analyse-liskov-substitution--lsp-interface-segreggation--isp-dependency-inversion-principle-dip :END: -[jeweils eine Klasse als positives und negatives Beispiel für entweder -LSP oder ISP oder DIP); jeweils UML der Klasse und Begründung, warum man -hier das Prinzip erfüllt/nicht erfüllt wird] **Dependency-Inversion-Principle** @@ -333,35 +262,48 @@ hier das Prinzip erfüllt/nicht erfüllt wird] :PROPERTIES: :CUSTOM_ID: analyse-grasp-geringe-kopplung :END: -[jeweils eine bis jetzt noch nicht behandelte Klasse als positives und -negatives Beispiel geringer Kopplung; jeweils UML Diagramm mit -zusammenspielenden Klassen, Aufgabenbeschreibung und Begründung für die -Umsetzung der geringen Kopplung bzw. Beschreibung, wie die Kopplung -aufgelöst werden kann] **** Positiv-Beispiel - :PROPERTIES: - :CUSTOM_ID: positiv-beispiel-3 - :END: + +[[./uml/GenericCSVDAO.png]] +[[./uml/GenericCSVDAOWithEntites.png]] + +Die Klasse GenericCSVDAO ist für das persitieren von Entities gedacht. +Sie besitzt aber nur eine geringe Kopplung zu den Entities, die sie persitieren soll. +Mit ihr kann quasi jede Klasse persitiert werden, die das Interface CSVSerializable implementiert (aktuell sind das LinkEntity, CategorEntity, und CustomTagMatcherEntity). +Dadurch besteht keine Abhängig der Klasse GenericCSVDAO, zu den Eigenschaften der Entites, sondern nur zur dem Interface. +Dies sorgt für eine gute Wiederverwendbarkeit des Codes, da alle Interaktionen mit dem Dateisystem und das Aufteilen der Datei zu einzelnen Objekten generisch für alle zu persistierenden Datentypen implementiert ist. +Das Interface GenericDAO sorgt außerdem dafür, dass Klassen die GenericCSVDAO nutzen nicht direkt an diese Implementation gebunden sind, sondern an das Interface. +So könnte man beispielsweise die CSV-Implementation schnell und einfach durch eine Datenbank austauschen. + + **** Negativ-Beispiel - :PROPERTIES: - :CUSTOM_ID: negativ-beispiel-3 - :END: + + + [[./uml/LinkCliAdapterOnly.png]] + + Die Klasse LinkCliAdapter bildet die Schnittstelle für die Nutzereingaben zu dem LinkUseCase und wandelt dabei die Daten von einfachen Strings zu den Domänenobjekten (ValueObjects) um. + Sie ist direkt an den LinkUseCase gebunden und hat somit eine hohe Kopplung. + Die Kopplung könnte durch das Einführen eines Interfaces, das die Funktionalität des LinkUseCases beschreibt verringert werden. + Dies wurde jedoch nicht gemacht, da die Kopplung als wenig schlimm angesehen wird. + Der Code des Usecases sollte langlebiger sein, als der des Adapters. + Es ist also deutlich Wahrscheinlicher, dass sich der Adapter ändert. + Auch entspricht diese technische Kopplung der fachlichen: werden neue Funktionen im Usecase hinzugefügt sollten sie auch im Adapter ergänzt werden, damit der Anwender sie benutzen kann. + *** Analyse GRASP: Hohe *Kohäsion* :PROPERTIES: :CUSTOM_ID: analyse-grasp-hohe-kohäsion :END: -[eine Klasse als positives Beispiel hoher Kohäsion; UML Diagramm und -Begründung, warum die Kohäsion hoch ist] + +[[./uml/LinkRepositorySingle.png]] + +Die Klasse LinkRepository hat eine hohe Kohäsion, da alle vorhanden Funktionen mit dem einzigen Attribut "links" arbeiten, somit ist der semantische Zusammenhang zwischen den Methoden und den Daten hoch. *** *Don't Repeat Yourself (DRY)* :PROPERTIES: :CUSTOM_ID: dont-repeat-yourself-dry :END: -[ein Commit angeben, bei dem duplizierter Code/duplizierte Logik -aufgelöst wurde; Code-Beispiele (vorher/nachher); begründen und -Auswirkung beschreiben] Das Interface SubCommand definiert einen CLI SubCommand, der wiederum einzelne Funktionen hat. Diese einzelnen Funktionen werden in einer Map gespeichert, welche den Namen auf die Methode mappt. @@ -415,7 +357,6 @@ abstract public class Subcommand { } #+end_src -#+RESULTS: Die angebenen Änderungen sind im Commit [[https://tea.filefighter.de/qvalentin/LinkDitch/commit/78730bc69f][78730bc69f]] sichtbar. Da die neuen Implementation des Interfaces im selben Commit hinzugefügt wurden ist, wurden diese direkt ohne den duplizierten Code committet. @@ -427,7 +368,7 @@ Die angebenen Änderungen sind im Commit [[https://tea.filefighter.de/qvalentin/ :PROPERTIES: :CUSTOM_ID: unit-tests :END: -[Nennung von 10 Unit-Tests und Beschreibung, was getestet wird] +/[Nennung von 10 Unit-Tests und Beschreibung, was getestet wird]/ @@ -462,7 +403,6 @@ Die angebenen Änderungen sind im Commit [[https://tea.filefighter.de/qvalentin/ :PROPERTIES: :CUSTOM_ID: atrip-automatic :END: -[Begründung/Erläuterung, wie ‘Automatic' realisiert wurde] Automatic wurde durch die einfache Ausführbarkeit realisiert. So muss nur ein Befehl ausgeführt werden um die Test zu starten. @@ -512,9 +452,6 @@ Mit Drone CI bekommt man dann gleich auch einen guten Überblick, wie oft die Te :PROPERTIES: :CUSTOM_ID: atrip-thorough :END: -[jeweils 1 positives und negatives Beispiel zu ‘Thorough'; jeweils -Code-Beispiel, Analyse und Begründung, was professionell/nicht -professionell ist] **** Positives Beispiel Beim CategoryNameTest werden (bis auf generierten Code) sämtliche Methoden getestet und sämtliche Sonderfälle für die Eingaben in einzelnen Test geprüft (constructorThrowsNull, constructorThrowsBlank, constructorThrowsEmpty, constructorThrowsTooShort) @@ -530,9 +467,6 @@ Beim CategorEntityTest werden bis auf die beiden Koversationsmethoden Richtung C :PROPERTIES: :CUSTOM_ID: atrip-professional :END: -[jeweils 1 positives und negatives Beispiel zu ‘Professional'; jeweils -Code-Beispiel, Analyse und Begründung, was professionell/nicht -professionell ist] **** Positives Beispiel Der Test GenericCSVDAOTest verwendet eine Datei. Damit dies sauber abläuft wird eine temporäre Datei verwendet. @@ -630,22 +564,22 @@ Der Commit [[https://tea.filefighter.de/qvalentin/LinkDitch/commit/d1fdad7cf9][d :PROPERTIES: :CUSTOM_ID: code-coverage :END: -[Code Coverage im Projekt analysieren und begründen] -Die Coverage ist mit etwa 25 % deutlich zu niedrig. +Die Coverage ist mit etwa 22 % deutlich zu niedrig. Da es sich bei dem Projekt jedoch um Code handelt, der niemals wirklich produktiv eingesetzt werden wird und der nicht langfristig weiterentwickelt wird, ist dies verkraftbar. Es wurden hauptsächlich die komplizierteren Stellen getestet, wie beispielsweise die CSV-Persistierung und die Interaktion mit der Github-APi. Bei diesen Stellen wurden grade genug Test geschrieben, um sicherzustellen, dass die Grundfunktion korrekt ist. Teilweise wurde während des Entwicklungsprozesses gemerkt, dass es besser gewesen wäre, manche Stellen zu testen. Für Fehler die beim manuellen Testen der Anwendung aufgefallen waren wurden teilweise extra Tests geschrieben. +Zur Anaylse des Codes wird Sonarqube genutz: https://sonar.filefighter.de/dashboard?id=de.qvalentin%3ALinkDitch + +[[./img/coverage.png]] +[[./img/coverageTrend.png]] *** Fakes und Mocks :PROPERTIES: :CUSTOM_ID: fakes-und-mocks :END: -[Analyse und Begründung des Einsatzes von 2 Fake/Mock-Objekten; -zusätzlich jeweils UML Diagramm der Klasse] - 1. CategoryCommandsTest: Beim CategoryCommandsTest wurde die Klasse CategoryCommands erstellt, doch anstatt ein Objekt der Klasse CategoryCliAdapter beim Erstellen zu übergeben wurde ein Mock übergeben. @@ -654,44 +588,44 @@ zusätzlich jeweils UML Diagramm der Klasse] Dieses Mock wird dann aufgerufen und die Parameter des Aufrufs werden überprüft. Zusätzlich wird definiert, welche Rückgabewerte das Mock liefern soll. - #+begin_src java - class CategoryCommandsTest { +#+begin_src java +class CategoryCommandsTest { - CategoryCliAdapter mockAdapter = mock(CategoryCliAdapter.class); + CategoryCliAdapter mockAdapter = mock(CategoryCliAdapter.class); - @Test - void addCommandWorks() { - var categoryName = "funStuff"; - ArgumentCaptor valueCapture = - ArgumentCaptor.forClass(String.class); - doNothing().when(mockAdapter).addCategory(valueCapture.capture()); - var sut = new CategoryCommands(mockAdapter); + @Test + void addCommandWorks() { + var categoryName = "funStuff"; + ArgumentCaptor valueCapture = + ArgumentCaptor.forClass(String.class); + doNothing().when(mockAdapter).addCategory(valueCapture.capture()); + var sut = new CategoryCommands(mockAdapter); - var returnValue = sut.executeSubcommand( - new String[]{"add", categoryName}); + var returnValue = sut.executeSubcommand( + new String[]{"add", categoryName}); - assertEquals(categoryName, valueCapture.getValue()); - assertEquals("Added the new category", returnValue); - } - - @Test - void getCommandWorks() { - var sut = new CategoryCommands(mockAdapter); - when(mockAdapter.getCategories()).thenReturn( - Set.of("funStuff", "workStuff")); - var returnValue = sut.executeSubcommand(new String[]{"get"}); - - var expected = - "Available Categories:" + System.lineSeparator() + - "funStuff" + System.lineSeparator() + "workStuff"; - - var expectedDifferentOrder = - "Available Categories:" + System.lineSeparator() + - "workStuff" + System.lineSeparator() + "funStuff"; - assertTrue(expected.equals(returnValue) || expectedDifferentOrder.equals(returnValue)); - } + assertEquals(categoryName, valueCapture.getValue()); + assertEquals("Added the new category", returnValue); } - #+end_src + + @Test + void getCommandWorks() { + var sut = new CategoryCommands(mockAdapter); + when(mockAdapter.getCategories()).thenReturn( + Set.of("funStuff", "workStuff")); + var returnValue = sut.executeSubcommand(new String[]{"get"}); + + var expected = + "Available Categories:" + System.lineSeparator() + + "funStuff" + System.lineSeparator() + "workStuff"; + + var expectedDifferentOrder = + "Available Categories:" + System.lineSeparator() + + "workStuff" + System.lineSeparator() + "funStuff"; + assertTrue(expected.equals(returnValue) || expectedDifferentOrder.equals(returnValue)); + } +} +#+end_src Das Mock ist hier besonders nützlich, da wir uns an der Grenze zwischen zwei Schichten befinden. Für das Erstellen des CategoryCliAdapters wird eine Usecase benötigt, welcher wiederum Instanzen aus der Domäne benötigt, welche wiederum bestimmte Instanzen benötigen. @@ -716,9 +650,6 @@ zusätzlich jeweils UML Diagramm der Klasse] :PROPERTIES: :CUSTOM_ID: ubiquitous-language :END: -[4 Beispiele für die Ubiquitous Language; jeweils Bezeichung, Bedeutung -und kurze Begründung, warum es zur Ubiquitous Language gehört] - **** Link **Bedeutung**: Steht für eine eindeutige URL, die von der Anwendung gespeichert werden soll @@ -740,9 +671,6 @@ und kurze Begründung, warum es zur Ubiquitous Language gehört] :PROPERTIES: :CUSTOM_ID: entities :END: -[UML, Beschreibung und Begründung des Einsatzes einer Entity; falls -keine Entity vorhanden: ausführliche Begründung, warum es keines geben -kann/hier nicht sinnvoll ist] [[./uml/Link.png]] @@ -758,9 +686,6 @@ Die Getter sind hauptsächlich für die Konvertierung zum Ausgabe- bzw. Persiste :PROPERTIES: :CUSTOM_ID: value-objects :END: -/[UML, Beschreibung und Begründung des Einsatzes eines Value Objects; -falls kein Value Object vorhanden: ausführliche Begründung, warum es -keines geben kann/hier nicht sinnvoll ist]/ [[./uml/LinkUrl.png]] @@ -776,9 +701,6 @@ Mit der Methode hostEquals können beispielsweise zwei Urls verglichen werden, o :PROPERTIES: :CUSTOM_ID: repositories :END: -[UML, Beschreibung und Begründung des Einsatzes eines Repositories; -falls kein Repository vorhanden: ausführliche Begründung, warum es -keines geben kann/hier nicht sinnvoll ist] [[./uml/LinkRepository.png]] @@ -795,9 +717,6 @@ Auch kann die Domänenspezifische Abfragelogik in der Domäne implementiert werd :PROPERTIES: :CUSTOM_ID: aggregates :END: -[UML, Beschreibung und Begründung des Einsatzes eines Aggregates; falls -kein Aggregate vorhanden: ausführliche Begründung, warum es keines geben -kann/hier nicht sinnvoll ist] Bei der Implementierung gibt es keine Klasse, die ein Aggregate widerspiegelt, da jedes Aggregat aus genau einem Entity besteht und somit eine reine Wrapper-Klasse unnötig ist. So verwaltet das CategoryRepository genau das Entity Category und das LinkRepository das Entity Link. @@ -815,9 +734,6 @@ Grundsätzlich könnten Aggregates eingesetzt werden, aufgrund der geringen Komp :PROPERTIES: :CUSTOM_ID: code-smells :END: -/[jeweils 1 Code-Beispiel zu 2 Code Smells aus der Vorlesung; jeweils -Code-Beispiel und einen möglichen Lösungsweg bzw. den genommen -Lösungsweg beschreiben (inkl./ /(Pseudo-)Code)]/ **** Duplicated Code Da es bei Java keine Funktion zum durchsuchen eines Sets gibt wurde an mehreren Stellen ein Konstrukt, wie unten sichtbar verwendet. @@ -927,11 +843,10 @@ Die Änderung wurde mit Commit [[https://tea.filefighter.de/qvalentin/LinkDitch/ :PROPERTIES: :CUSTOM_ID: refactorings :END: -[2 unterschiedliche Refactorings aus der Vorlesung anwenden, begründen, -sowie UML vorher/nachher liefern; jeweils auf die Commits verweisen] **** Replace Error Code with Exception Der Code zum überprüfen, ob eine URL auf eine aktuell erreichbare Ressource zeigt gab im Falle einer IO Exception den ErrorCode 500 (anhand des HTTP Status Codes) zurück. +Dieser wurde dann von der aufrufenden Methode interpretiert und ein Fehler geworfen. Somit konnte ein Internal Server Error der angefragten Ressource nicht von einem lokalen Fehler, wie fehlender Netzwerkverbindung oder einem DNS-Fehler unterschieden werden. Deutlich besser ist es, direkt eine passende Exeption zu werfen und dabei die Informationen an den Nutzer weiterzuleiten, sowie die möglichen IOExceptions zu unterscheiden und einen Sonderfall für einen DNS Error, der recht wahrscheinlich ist einzuführen. @@ -978,19 +893,45 @@ private static int getResponseCode(LinkUrl url) { Das Refactoring wurde mit Commit [[https://tea.filefighter.de/qvalentin/LinkDitch/commit/7fbc3f722c][7fbc3f722c]] durchgeführt. + +**** Rename Method + +In den Klassen LinkCliAdapter, LinkCommands und LinkUseCase gibt es jeweils eine Funktion groupByHosts, die die nächste Schicht aufruft, bis im Repository die Daten entsprechend angeordnet werden. +Nur in der Klasse LinkRepository hieß diese Methode groupByHost ohne "s" am Ende. +Diese Inkonsistenz ist schlecht und verwirrt beim Lesen des Codes. + +Das Refactoring wurde mit Commit [[https://tea.filefighter.de/qvalentin/LinkDitch/commit/ba6a889d35][ba6a889d35]] durchgeführt. + * Kapitel 8: Entwurfsmuster :PROPERTIES: :CUSTOM_ID: kapitel-8-entwurfsmuster :END: -/[2 unterschiedliche Entwurfsmuster aus der Vorlesung (oder nach -Absprache auch andere) jeweils sinnvoll einsetzen, begründen und -UML-Diagramm]/ *** Entwurfsmuster: Dekorator - :PROPERTIES: - :CUSTOM_ID: entwurfsmuster-name - :END: -*** Entwurfsmuster: [Name] - :PROPERTIES: - :CUSTOM_ID: entwurfsmuster-name-1 - :END: + +Für die Synchronisation der Datenhaltung im Arbeitsspeicher und der Persistenz in Form von CSV-Dateien wird das Dekorator Entwurfsmuster verwendet. + +[[./uml/CustomSetPersistenceDecorator.png]] +[[./uml/CustomSetPersistenceDecoratorClasses.png]] +Zum Vergrößern des Bildes siehe Datei: ./uml/CustomSetPersistenceDecoratorClasses.png + +Jedes der Repositories (LinkRepository, CategoryRepository und TagMatcherRepository) hält die Daten in einem Set. +Doch statt hierfür ein normales Set zu verwenden wird eine Implementation des Interfaces CustomSet verwendet. +Für dieses Interface gibt es zwei Implementationen. +Das CustomStrictSet entspricht quasi einem normalen Set, nur dass es Exeptions wirft, wenn ein Element hinzugefügt wird, das bereits existiert. +Die andere Implementation CustomSetPersistenceDecorator bildet den Dekorator für das CustomSet. +Dieser schaltet sich vor ein CustomStrictSet und fängt sämtliche Aktionen ab, welche den Zustand der Daten verändern. +Diese Aktionen werden dann sowohl in der Datenstruktur im Arbeitsspeicher durchgeführt als auch über das PersistenceAdapter an die Persistenzschicht weitergeleitet und dort verarbeitet. +Hierfür erhält der Dekorator ein PersistenceAdapter, wobei T dem Datentyp entspricht, der durch das Repository verwaltet wird. + +Durch den Einsatz des Entwurfsmusters wird sichergestellt, dass eine Veränderung der Daten im Arbeitsspeicher auch immer in die CSV-Dateien persistiert wird. +Sämtliche Repository-Methoden, die den Zustand ändern müssen nicht mehr das PersistenceAdapter aufrufen sondern dies geschieht automatisch. +Dadurch wird der Code einfacher lesbar und Fehler können vermieden werden. +Diese Ansatz orientiert sich an Aspectorientierter Programmierung, hier wird des Aspect der Persistenz vom Repository losgelöst und durch den Dekorator geregelt. + +*** Entwurfsmuster: Builder (Erbauer) + +[[./uml/Tag.png]] + +Das Builder Entwurfsmuster wird bei der Klasse Tag für den Optionalen Wert additionalData genutzt. +Die Funktion addAdditionalData fügt den optionalen Wert hinzu und gibt das veränderte Objekt zurück. diff --git a/Documentation/programmEntwurf.pdf b/Documentation/programmEntwurf.pdf index 513d83e..2b20f67 100644 Binary files a/Documentation/programmEntwurf.pdf and b/Documentation/programmEntwurf.pdf differ diff --git a/Documentation/test-ide.png b/Documentation/test-ide.png deleted file mode 100644 index 4c7a650..0000000 Binary files a/Documentation/test-ide.png and /dev/null differ diff --git a/Documentation/uml/CSVLinkPersistenceAdapterClasses.png b/Documentation/uml/CSVLinkPersistenceAdapterClasses.png new file mode 100644 index 0000000..4c1389e Binary files /dev/null and b/Documentation/uml/CSVLinkPersistenceAdapterClasses.png differ diff --git a/Documentation/uml/CustomSetPersistenceDecorator.png b/Documentation/uml/CustomSetPersistenceDecorator.png new file mode 100644 index 0000000..7f8d5a9 Binary files /dev/null and b/Documentation/uml/CustomSetPersistenceDecorator.png differ diff --git a/Documentation/uml/CustomSetPersistenceDecoratorClasses.png b/Documentation/uml/CustomSetPersistenceDecoratorClasses.png new file mode 100644 index 0000000..abec926 Binary files /dev/null and b/Documentation/uml/CustomSetPersistenceDecoratorClasses.png differ diff --git a/Documentation/uml/GenericCSVDAO.png b/Documentation/uml/GenericCSVDAO.png new file mode 100644 index 0000000..df4b562 Binary files /dev/null and b/Documentation/uml/GenericCSVDAO.png differ diff --git a/Documentation/uml/GenericCSVDAOWithEntites.png b/Documentation/uml/GenericCSVDAOWithEntites.png new file mode 100644 index 0000000..397bcc6 Binary files /dev/null and b/Documentation/uml/GenericCSVDAOWithEntites.png differ diff --git a/Documentation/uml/LinkCliAdapterOnly.png b/Documentation/uml/LinkCliAdapterOnly.png new file mode 100644 index 0000000..d3481b5 Binary files /dev/null and b/Documentation/uml/LinkCliAdapterOnly.png differ diff --git a/Documentation/uml/LinkRepositorySingle.png b/Documentation/uml/LinkRepositorySingle.png new file mode 100644 index 0000000..0b96249 Binary files /dev/null and b/Documentation/uml/LinkRepositorySingle.png differ diff --git a/Documentation/uml/Tag.png b/Documentation/uml/Tag.png new file mode 100644 index 0000000..8b6e5db Binary files /dev/null and b/Documentation/uml/Tag.png differ