letzer push
This commit is contained in:
@@ -14,7 +14,7 @@ Quellen und Hilfsmittel angegeben, sowie wörtliche und sinngemäße Zitate geke
|
||||
\vspace{2cm}
|
||||
|
||||
\noindent
|
||||
Füssen, den 27. Juni 2023
|
||||
Füssen, den 10. Juli 2024
|
||||
\hspace*{2cm}%
|
||||
\dotfill\\
|
||||
\hspace*{8.5cm}%
|
||||
|
||||
7
Bestandteile/fazit.tex
Normal file
7
Bestandteile/fazit.tex
Normal file
@@ -0,0 +1,7 @@
|
||||
\chapter{Fazit}
|
||||
Abschließend lässt sich sagen, dass Refactoring ein wichtiger Bestandteil der Softwareentwicklung ist.
|
||||
Es hilft dabei, die Qualität und Wartbarkeit von Code zu verbessern und somit die Lebensdauer von Software zu verlängern.\\
|
||||
Das Seminar hat gezeigt, dass Strukturen im Code eine wichtige Rolle spielen und Verhalten in verschiedenen Formen abgebildet werden kann.
|
||||
Auch das Vezichten auf Refactoring kann in manchen Fällen sinnvoll sein, um die Entwicklung nicht zu verlangsamen, auch wenn es auf den ersten Blick nicht so wirkt.
|
||||
Ein weiterer wichtiger Punkt ist die Sicherheit von Software. Diese kann durch Tests, Werkzeuge, formale Verifikation und Fehlertoleranz gewährleistet werden.\\
|
||||
|
||||
30
Bestandteile/immerRefactoren.tex
Normal file
30
Bestandteile/immerRefactoren.tex
Normal file
@@ -0,0 +1,30 @@
|
||||
\chapter{Wann ist Refactoring sinnvoll?}
|
||||
Im ersten Moment hört es sich so an, als wäre Refactoring in jedem Fall ein Weg, Qualität, Nutzbarkeit und Wartbarkeit von Software zu verbessern.
|
||||
Jedoch gibt es Situationen, in denen Refactoring keine Verbesserung liefert. Im Gegenteil, es kann sogar zu einer Verschlechterung führen \citep[S. 321 f.]{fiveLines.2023}.
|
||||
Diese Fälle sollen im folgenden Kapitel beleuchtet werden.\\
|
||||
\begin{singlespace}
|
||||
\textit{„Durch Refactorings machen wir manche Änderungen leichter und manche schwieriger. Wir führen Refactorings durch, um Änderungen in eine Richtung zu unterstützen, von der wir glauben, dass sich die Software in diese entwickelt. Je mehr Code wir haben, desto sicherer können wir uns über die Richtung und die typische Art der Änderung sein [...]“}\citep[S. 321]{fiveLines.2023}\\
|
||||
\end{singlespace}
|
||||
\par
|
||||
Gerade bei neu entwickelten Codeteilen ist die Struktur oft noch nicht vollständig ausgereift.
|
||||
Hier ist die oberste Priorität, erst die Korrektheit und Vollständigkeit sicherzustellen.
|
||||
Ist dies nicht der Fall ist es für Entwickler wertvoll, schnell Änderungen vornehmen zu können und verschiedene Ansätze auszuprobieren.
|
||||
Refactoring würde in diesem Fall die Entwicklung verlangsamen und ist daher nicht sinnvoll \citep[S. 321]{fiveLines.2023}.\\
|
||||
Trotzdem ist es wichtig durch dieses Vorgehen die Struktur des umliegenden Codes nicht zu vernachlässigen.
|
||||
Bezieht sich die Unsicherheit nur auf einen Teilbereich, sollte dieser vom Rest gekapselt werden, um andere Refactorings nicht zu behindern \citep[S. 321]{fiveLines.2023}.\\
|
||||
\par
|
||||
Auch bei bestehenden Code, oder Code mit genug Sicherheit die Struktur zu festigen, sollte nicht sofort jedes mögliche Refactoring durchgeführt werden, auch wenn es noch so offensichtlich wirkt.\\
|
||||
\begin{singlespace}
|
||||
\textit{„Versuchen wir, die Entwicklungsrichtung vorherzusagen, können wir der Codebasis langfristig mehr schaden als nutzen. Wie bei den meisten Dingen in unserer Arbeit sollten wir uns nicht auf Vermutungen stützen, sondern auf empirisch erhobene Daten.“}\citep[S. 322]{fiveLines.2023}\\
|
||||
\end{singlespace}
|
||||
\par
|
||||
Es ist also wichtig Refactoring nur an den Stellen durchzuführen, an denen es bereits Änderungen, in eine bestimmte Richtung, vorgekommen sind.
|
||||
Wenn es keine Anzeichen gibt, dass geplante Änderungen in Zukunft gebraucht werden, wäre er zum einen Zeitverschwendung, zum anderen erhöt es die Komplexität, ohne Nutzen davon zu erhalten \citep[S. 322]{fiveLines.2023}.\\
|
||||
Auch wenn Code eine sehr schlechte Qualität aufweist, ist es nicht sinnvoll diesen zu Refacotren, solange keine Änderungen nötig sind.
|
||||
Ist er so gesehen als externe API nutzbar und funktioniert, bringt es keinen Vorteil diesen zu verbessern \citep[S. 89]{refactoring.2020}.\\
|
||||
\begin{singlespace}
|
||||
\textit{„Darüber hinaus gibt es noch den Fall, dass es einfacher ist, den Code neu zu schreiben, als ein Refactoring vorzunehmen. Das ist allerdings eine schwierige Entscheidung.“}\citep[S. 90]{refactoring.2020}\\
|
||||
\end{singlespace}
|
||||
\par
|
||||
Auch hier sollte auf Grundlage von Erfahrung und Beobachtung gehandelt werden.
|
||||
Es sollte eindeutige Hinweise geben, dass eine Neuentwicklung auf lange sicht mehr Zeit spart und Verbesserungen liefert, als diesen Teil umfassend zu refactoren \citep[S. 90]{refactoring.2020}.
|
||||
@@ -1,33 +1,57 @@
|
||||
\chapter{Ist Refactoring immer sinnvoll?}
|
||||
\section{Beobachten statt vorhersagen}
|
||||
|
||||
|
||||
\chapter{Sicherheit gewinnen, ohne den Code zu verstehen}
|
||||
Wenn Änderungen an einer Software durchgeführt werden, ist es von hoher Bedeutung, die Sicherheit zu haben, dass danach weiterhin alles funktioniert.
|
||||
Auch die erfahrensten Entwickler machen hin und wieder Fehler. Fehler sind menschlich.\\
|
||||
Im folgenden werden einige Maßnahmen vorgestellt, die die Funktionstüchtigkeit von Softwareprojekten gewährleisten können. Insbesondere im Kontext von Refactoring spielt dies eine wichtige Rolle, da dort oft umfassende Änderungen vorgenommen werden. vgl. \citep[S. 323]{fiveLines.2023}
|
||||
\section{Sicherheit durch Tests}
|
||||
\chapter{Sicherheit in der Softwareentwicklung}
|
||||
Wird Software neu entwickelt oder werden Änderungen vorgenommen, ist es von hoher Bedeutung, die Sicherheit zu haben, dass alles wie erwartet funktioniert.
|
||||
Egal wie erfahren ein Entwickler sein mag, Fehler sind unausweichlich.\\
|
||||
Um mehr Sicherheit zu erlangen, werden im Folgenden Maßnahmen vorgestellt, die Funktionstüchtigkeit von Softwareprojekten gewährleisten. Insbesondere im Kontext von Refactoring spielt dies eine wichtige Rolle, da dort oft umfassende Änderungen vorgenommen werden \citep[S. 323]{fiveLines.2023}.
|
||||
\section{Verschiedene Arten von Tests}
|
||||
Die einfachste und verbreitetste Möglichkeit, die Korrektheit von etwas zu überprüfen, ist das Testen. Auch in der Softwareentwicklung ist dies der Fall. Allerdings ist es hier nicht so einfach, wie es sich anhört: Rund die Hälfte der Entwicklungszeit wird in das Testen investiert \citep[Introduction]{artoftesting.2011}.\\
|
||||
Es gibt viele verschiedene Arten, um Software zu testen, wobei diese sich auf unterschiedlichen Ebenen bewegen. Die folgende Auflistung soll einen Überblick darüber geben.
|
||||
\begin{itemize}
|
||||
\item \textbf{Unit-Tests} (oder Modultests) sind ein Prozess, bei dem die einzelnen Unterprogramme, Unterroutinen, Klassen oder Prozeduren in einem Programm getestet werden. Genauer gesagt wird der Test nicht zunächst auf das gesamte Programm konzentriert, sondern zuerst auf die kleineren Bausteine des Programms \citep[S. 85]{artoftesting.2011}.
|
||||
\item \textbf{Integrations Tests} sollen das Zusammenspiel von einzelnen Modulen, aus dem vorherigen Schritt, testen. Oft ist es der Fall, dass Integrations Tests nicht seperat durchgeführt werden, sondern die Stellen durch umfangreichere Unit-Tests beretis abgedeckt werden \citep[S. 117 f.]{artoftesting.2011}.
|
||||
\item \textbf{Unit-Tests} (oder Modultests) sind ein Prozess, bei dem die einzelnen Unterprogramme, Unterroutinen, Klassen oder Funktionen in einem Programm getestet werden. Der Test wird also nicht auf das gesamte Programm gerichtet, sondern nur auf kleinere Bausteine \citep[S. 85]{artoftesting.2011}.
|
||||
\item \textbf{Integrations Tests} sollen das Zusammenspiel von einzelnen Modulen, aus dem vorherigen Schritt, testen. Oft ist es der Fall, dass Integrations Tests nicht separat durchgeführt werden, sondern die Stellen durch umfangreichere Unit-Tests beretis abgedeckt werden \citep[S. 117 f.]{artoftesting.2011}.
|
||||
\item \textbf{Funktionale Tests} sind ein Prozess, um Abweichungen zwischen dem Programm und der externen Spezifikation zu finden. Eine externe Spezifikation beschreibt das Verhalten des Programms aus der Sicht des Endbenutzers. Diese Tests werden normalerweise als Black-Box-Test durchgeführt, da das interne Verhalten des Codes in den vorherigen Schritten überprüft wurde. \citep[S. 119]{artoftesting.2011}
|
||||
\end{itemize}
|
||||
Diese Liste ist unvollständig, da nur Test-Arten gelistet sind, die den Code direkt betreffen. Es gibt zusätlich noch \textbf{System Tests}, \textbf{Akzeptantz Tests} und \textbf{Installations Tests}. Diese zielen eine höhere Ebene an und sollen die Software als ganzes Testen um festzustellen, ob die angeforderten Problemstellungen des Kunden richtig erfüllt werden und somit z.B.: Missverständnisse bei den Anforderungen ausgeschlossen werden können.
|
||||
Es ist wichtig zu verstehen, dass diese Ebenen unabhänig voneinander laufen müssen. Funktionieren alle Unit-Tests ordnugnsgemäß, heißt das nicht, dass die Software insgesamt funktioniert, da z.B.: Klassen falsch verwendet werden können.
|
||||
\textit{„[Außerdem bleibt immer] das Risko, dass unsere Tests genau die Stelle, an der ein Fehler auftritt, nicht abdecken oder dass sie etwas anderes testen, als wir glauben.“} \citep[S. 323]{fiveLines.2023}
|
||||
Es ist wichtig zu verstehen, dass diese Ebenen unabhänig voneinander laufen müssen. Funktionieren alle Unit-Tests ordnungsgemäß, heißt das nicht, dass die Software insgesamt funktioniert, da z.B.: Klassen falsch verwendet werden können.\\
|
||||
\begin{singlespace}
|
||||
\textit{„[Außerdem bleibt immer] das Risko, dass unsere Tests genau die Stelle, an der ein Fehler auftritt, nicht abdecken oder dass sie etwas anderes testen, als wir glauben.“} \citep[S. 323]{fiveLines.2023}\\
|
||||
\end{singlespace}
|
||||
\par
|
||||
Um den Zeitaufwand des Testens auf lange Sicht geringer zu halten, gibt es wie in \citep[S. 323]{fiveLines.2023} die Möglichkeit einige dieser Tests zu automatisieren.
|
||||
\par
|
||||
In der modernen Softwareentwicklung sind automatiserte Tests oft Teil einer „Continous Integration“. Dabei muss jeder Commit oder jeder Pull Request vorher definierte Aufgaben erfolgreich absolvieren. Dabei können Unit-Tests, Buildvorgänge \cite{CIMeyer.2014}
|
||||
\section{Sicherheit durch Werkzeuge}
|
||||
Um den Zeitaufwand des Testens auf lange Sicht geringer zu halten, gibt es wie in \citep[S. 323]{fiveLines.2023} beschrieben, die Möglichkeit das Testen zu automatisieren.\\
|
||||
Um noch einen Schritt weiter zu gehen hat sich in der modernen Softwarentwicklung das Konzept der „Continous Integration“ (CI) etabliert. Dabei muss jeder Commit oder jeder Pull Request vorher definierte Aufgaben erfolgreich absolvieren. Unit-Tests, Buildvorgänge und automatisches Deployment sind dabei häufig Bestandteile.
|
||||
Das kontinuierliche Durchlaufen der Tests, ermöglicht eine frühzeitige Erkennung von auftretenden Fehlern \cite{CIMeyer.2014}.
|
||||
\section{Wekzeuge in der Entwicklungsumgebung}
|
||||
\begin{wrapfigure}{r}{0.4\textwidth}
|
||||
\centering
|
||||
\includegraphics[width=0.35\textwidth]{Bilder/screenshotWebstorm} % Replace with your image file
|
||||
\caption{Screenshot WebStorm Refactoring-Tools \cite{webstorm.2024}}
|
||||
\label{webstormRefactor}
|
||||
\end{wrapfigure}
|
||||
Gerade für Refactorings bieten die meisten modernen Entwicklungsumgebungen verschiedenste Werkzeuge, die den Entwickler unterstützen sollen. In der nebenliegenden Abbildung \ref{webstormRefactor} ist ein Auszug aus den Refactoring-Tools
|
||||
\section{Sicherheit durch formale Verifikation}
|
||||
\section{Sicherheit durch Fehlertoleranz}
|
||||
Softwareentwicklung ist ein zeitaufwendiger Prozess, in dem es schnell zu Fehlern kommen kann. Um diese zu vermeiden, wurden verschiedenste Werkzeuge entwickelt, die die Programmierenden unterstützen sollen.
|
||||
Moderne IDEs (Integrated Development Environments) haben das Ziel die Entwicklungsgeschwindigkeit zu erhöhen und Fehler zu vermeiden.
|
||||
Sie kombinieren eine Vielzahl dieser hilfreichen Werkzeuge in einem einzelnen Programm. Darunter gehören unter anderem Syntax-Highlighting, Autovervollständigung, Debugging-Tools und und andere Überprüfungen, die kontinuierlich im Hintergrund laufen.
|
||||
Auch Werkzeuge, die der Entwickler bewusst einsetzen kann, werden angeboten. Dazu gehören zum Beispiel Versionsverwaltung und Refactoring-Tools \citep[S. 275]{modernIDEs.2022}.\\
|
||||
Die nebenstehende Abbildung \ref{webstormRefactor} zeigt das Kontextmenü für Refactoring Hilfen in der IDE WebStorm. Hier kann der Entwickler verschiedene Aktionen auswählen, die dann automatisiert durchgeführt werden.
|
||||
\newpage
|
||||
Ein Beispiel dafür ist die Namensänderung eines markierten Feldes. Dabei kann ein neuer Name eingegeben werden und die IDE ändert alle Stellen, an denen das Feld verwendet wird, automatisch ab.
|
||||
Diese Unterstützung wird für die meisten gängigen Refactorings angeboten.\cite{webstorm.2024}
|
||||
Trotzdem ist es wichtig, sich nicht vollständig auf diese Werkzeuge zu verlassen. Vorgenommene Änderungen sollten immer überprüft werden, da auch diese Tools fehlerhaft sein können \citep[S. 323 f.]{fiveLines.2023}.
|
||||
|
||||
\section{Formale Verifikation der Software}
|
||||
Die nächste Thematik spielt vor allem bei besonders kritischer Software eine Rolle.
|
||||
Beispiele hierfür sind Systeme, von denen Menschenleben abhängen, oder bei denen erhebliche Geldsummen im Spiel sind, wie Flugzege oder der nächste Marsrover \citep[S. 324]{fiveLines.2023}.\\
|
||||
Formale Verifikation beschreibt das Erheben einer Beweisführung über die Funktionstüchtigkeit von Code. Alle möglichen Ausgänge eines Programms müssen auf mathematischer Ebene bewiesen werden \cite{glinz2004formale}.
|
||||
Da dieser Prozess extrem zeitaufwendig ist und Fehler in der Beweisführung unter allen Umständen vermieden werden sollten, gibt es auch hier spezielle Werkzeuge, die den Entwickler unterstützen und die Berechnungen auf Richtigkeit überprüfen.
|
||||
Auch hier gilt, kommt externe Software zum Einsatz, sind Fehler dieser nicht grundsätzlich auszuschließen \citep[S. 324]{fiveLines.2023}.
|
||||
|
||||
\section{Eine Fehlertoleranz einbauen}
|
||||
Der letzte Ansatz, der untersucht werden soll, bezieht sich auf eine gewisse Fehlertoleranz der Software.
|
||||
Es gibt mehrere Möglichkeiten, diese in Code einzubauen.
|
||||
Eine davon ist das Verwenden von Feature-Schaltern. Diese können im Fehlerfall komplette Funktionaltäten deaktivieren, um die Software weiterhin lauffähig zu halten.
|
||||
Alternativ kann auch ein Mechanismus eingebaut werden, der bei einem Fehler auf eine ältere stabile Version der Software zurückgreift \citep[S. 324]{fiveLines.2023}. \\
|
||||
Leider gewährleistet auch dieses Vorgehen keine 100-prozentige Sicherheit, da Fehler falsch erkannt werden können.\\
|
||||
\begin{singlespace}
|
||||
\textit{„Ein Beispiel hierfür wäre eine Funktion, die im Fehlerfall -1 zurückgibt, anstatt einen Fehler zu werfen. Das System erwartet vielleicht eine ganze Zahl, und -1 passt zu dieser Erwartung.“} \citep[S. 324]{fiveLines.2023}.\\
|
||||
\end{singlespace}
|
||||
\par
|
||||
Zusammenfassend lässt sich sagen, dass es viele verschiedene Möglichkeiten gibt, die Sicherheit von Software zu gewährleisten. Jedoch ist keine dieser Methoden fehlerfrei. Es ist wichtig, mehrere dieser Ansätze zu kombinieren, um die Wahrscheinlichkeit von Fehlern zu minimieren.
|
||||
Diese Beobachtung speißt die Aussage, dass Software nie fertig ist, da in ihrer Lebenszeit immer wieder neue Fehler entdeckt werden, die behoben werden müssen.
|
||||
@@ -1,13 +1,24 @@
|
||||
\chapter{Einleitung}
|
||||
In dieser Arbeit solle genauer untersucht werden, was Refactoring tatsächlich an Code verändert, welche Möglichkeiten es gibt ein bestimmtes Verhalten überhaupt darzustellen. Als Grundlage dieser Arbeit gilt das 11. Kapitel aus dem Buch \textit{„five lines of code“} von Christian Clausen \cite{fiveLines.2023}. Dieses wurde im Rahmen des Seminars „Refactoring“, bei Professor Dr. Georg Hagel, untersucht und für diese Arbeit aufgearbeitet. Das Kapitel des Buchs kann zwar eigenständig gelesen werden, aber ein grundlegendes Verständnis von Refactoring ist trotzdem erforderlich.\\
|
||||
Neben dem Untersuchen des Verhaltens und der Struktur im Code, sollen einige Maßnahmen vorgestellt werden, mit denen Sicherheit erlangt werden kann, dass Code ordnungsgemäß funktioniert. Gerade nach einem umfassenden Refactoring spielt dies eine große Rolle.
|
||||
In dieser Arbeit soll genauer untersucht werden, was Refactoring tatsächlich an Code verändert, welche Möglichkeiten es gibt ein bestimmtes Verhalten überhaupt darzustellen.
|
||||
Als Grundlage dieser Arbeit gilt das 11. Kapitel aus dem Buch \textit{„five lines of code“} von Christian Clausen \cite{fiveLines.2023}.
|
||||
Dieses wurde im Rahmen des Seminars „Refactoring“, bei Professor Dr. Georg Hagel, untersucht und für diese Arbeit aufgearbeitet.
|
||||
Das Kapitel des Buchs kann zwar eigenständig gelesen werden, aber ein grundlegendes Verständnis von Refactoring ist trotzdem erforderlich.\\
|
||||
Außerdem wird erläutert, in welchen Situationen auf ein Refactoring verzichtet werden sollte und welche Gründe es dafür gibt.\\
|
||||
Anschließend sollen einige Maßnahmen vorgestellt werden, mit denen Sicherheit erlangt werden kann, dass Code ordnungsgemäß funktioniert.
|
||||
Gerade nach einem umfassenden Refactoring spielt dies eine große Rolle.
|
||||
Abschließend werden einige Fälle vorgestellt, in denen Refactoring aus verschiedenen Gründen häufig nicht durchgeführt wird und es wird gezeigt wieso dies der Fall sein sollte.
|
||||
\chapter{Strukturen in der Softwareentwicklung}
|
||||
Bevor man sich mit den Strukturen in der Softwareentwicklung auseinandersetzen kann, ist es wichtig, sich erneut vor Augen zu führen, was Software eigentlich ist.\\
|
||||
"\emph{Software modelliert einen Teil der Welt. Die Welt - und unser Verständnis davon - entwickelt sich, und unsere Software muss sich entwickeln, um ein akkurates Modell zu sein.}" \cite{fiveLines.2023}\\
|
||||
Das heißt also, dass Code verschiedenste Gegebenheiten aus der Realität abbildet. Darunter zählen Informationen, Zusammenhänge und ganze Abläufe. Zusammen ergibt sich dadurch eine Struktur, ein wiedererkennbares Muster, welches sich sowohl in der echten Welt als auch in der Software finden lässt.
|
||||
\section{Verschiedene Arten von Strukturen}
|
||||
"\emph{Software modelliert einen Teil der Welt. Die Welt - und unser Verständnis davon - entwickelt sich, und unsere Software muss sich entwickeln, um ein akkurates Modell zu sein.}" \citep[S. 311]{fiveLines.2023}\\
|
||||
Dies heißt außerdem das Software nie fertig ist, da sie sich immer an die Ständig ändernde Welt anpassen.\\
|
||||
Code bildet also verschiedenste Gegebenheiten aus der Realität ab. Darunter zählen Informationen, Zusammenhänge und ganze Abläufe.
|
||||
Zusammen ergibt sich dadurch eine Struktur, ein wiedererkennbares Muster, welches sich sowohl in der echten Welt als auch in der Software finden lässt. \citep[S. 311]{fiveLines.2023}
|
||||
\subsubsection{Verschiedene Arten von Strukturen}
|
||||
|
||||
Es gibt verschiedene Bereiche in der Softwareentwicklung, in denen Struktur eine Rolle spielt. Es ist möglich diese Bereiche an zwei Achsen einzuteilen. Zum einen gibt es einige Faktoren welche den Menschen, also die Softwareentwickler betreffen. Auf der anderen Seite befindet sich der tatsächliche Code. Um die Einteilung sinnvoll zu gestalten, teilt die andere Achsen anhand des Wirkungsbereiches der Strukturen ein. \cite{fiveLines.2023}
|
||||
Es gibt verschiedene Bereiche in der Softwareentwicklung, in denen Struktur eine Rolle spielt.
|
||||
Es ist möglich diese an zwei Achsen einzuteilen.
|
||||
Zum einen gibt es einige Faktoren welche den Menschen, also die Softwareentwickler direkt betreffen, oder aber den tatsächliche Code.\\
|
||||
Auf der zweiten Achse wählt Clausen den Wirkungsbereich als Einteilung \citep[S. 311]{fiveLines.2023}.
|
||||
Die folgende Tabelle zeigt das Ergebnis der Einteilung.
|
||||
|
||||
|
||||
@@ -26,8 +37,7 @@ Die folgende Tabelle zeigt das Ergebnis der Einteilung.
|
||||
\end{table}
|
||||
|
||||
|
||||
Melvin E. Conway stellt bereits 1968 Beobachtungen an, dass es eine gewisse Symmetrie zwischen der Arbeitsweise von Entwicklerteams und den Zusammenhängen der tatsächlichen Systemen gibt. \cite{conway.1968}
|
||||
Diese Aussage wurde von Harvard Wissenschaftlern bestätigt. \cite{maccormack.2012}
|
||||
\section{Weitere Einschränkungen}
|
||||
|
||||
Auch das Nutzerverhalten kann bestimmte Strukturen vorgeben, da diese ein immer gleiches Verhalten erwarten. Auch wenn einige Abläufe optimiert oder umstrukturiert werden können, ist es nicht immer sinnvoll dies zu tun, da dann ggf. Nutzer neu geschult werden müssen.\cite{fiveLines.2023}
|
||||
Melvin E. Conway stellt bereits 1968 Beobachtungen an, dass es eine gewisse Symmetrie zwischen der Arbeitsweise von Entwicklerteams und den Zusammenhängen der tatsächlichen Systeme gibt. \cite{conway.1968}
|
||||
\par
|
||||
Auch das Nutzerverhalten kann bestimmte Strukturen vorgeben, da diese ein immer gleiches Verhalten erwarten.
|
||||
Auch wenn einige Abläufe optimiert oder umstrukturiert werden können, ist es nicht immer sinnvoll dies zu tun, da dann ggf. Nutzer neu geschult werden müssen.\citep[S. 312]{fiveLines.2023}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
\chapter{Ungenutzte Strukturen im Code}
|
||||
Im letzten Kapitel sollen einige Spezialfälle untersucht werden, bei denen oft aus verschiedenen Gründen auf ein Refactoring Verzichtet wird.
|
||||
Im letzten Kapitel sollen einige Spezialfälle untersucht werden, bei denen oft aus verschiedenen Gründen auf ein Refactoring verzichtet wird.
|
||||
Gründe dafür können unter anderem Zeitmangel, fehlende Erfahrung oder gar Faulheit der Entwickler sein.
|
||||
|
||||
\section{Leerzeilen nutzen}
|
||||
Wenn im Code Leerzeilen verwendet werden, hat dies zumeist einen Grund.
|
||||
Programmierende grenzen dadurch Gruppierungen von Aufrufen, Variablen oder generell zusammengehörigen Codeteilen voneinander ab.
|
||||
Programmierende grenzen dadurch Gruppierungen von Aufrufen, Variablen oder generell zusammengehörenden Codeteilen voneinander ab.
|
||||
Da Leerzeilen in kürzester Zeit eingefügt werden können und einen großen Effekt zur Lesbarkeit des Codes beitragen, werden diese fast immer benutzt.
|
||||
Dies ist auch ein Grund dafür, dass viele kleinere Refactorings nicht durchgeführt werden, da diese mehr Zeit in anspruch nehmen würden. \citep[S. 325]{fiveLines.2023}
|
||||
Dies ist auch ein Grund dafür, dass viele kleinere Refactorings nicht durchgeführt werden, da diese mehr Zeit in Anspruch nehmen würden. \citep[S. 325]{fiveLines.2023}
|
||||
\begin{figure}[ht]
|
||||
\centering
|
||||
\begin{minted}{typescript}
|
||||
@@ -24,10 +24,10 @@ function login(username: string, password: string) {
|
||||
\label{fig:LoginLeerzeile}
|
||||
\end{figure}\\
|
||||
Das Codebeispiel \ref{fig:LoginLeerzeile} zeigt eine stark vereinfachte Methode, um einen Login durchzuführen.
|
||||
In Zeile Nr. 6 wird die Überprüfung der Anmeldedaten und die annschließende Anfrage des Nutzers, von den weiteren Bestandteilen des Logins räumlich voneinander getrennt.
|
||||
In Zeile Nr. 6 wird die Überprüfung der Anmeldedaten und die anschließende Anfrage des Nutzers, von den weiteren Bestandteilen des Logins räumlich voneinander getrennt.
|
||||
Das durchzuführende Refactoring wirkt in diesem Fall fast trivial.
|
||||
Die beiden Bestandteile vor und nach der Leerzeile können nach \citep[S. 325]{fiveLines.2023} in eigene Methoden gezogen werden.
|
||||
Jediglich passende Funktions Namen müssen gewählt werden.
|
||||
Es müssen nur noch passende Funktionsnamen gewählt werden.
|
||||
Das folgende Ergebnis \ref{fig:LoginErgebnis} entsteht dadurch.
|
||||
\begin{figure}[ht]
|
||||
\centering
|
||||
@@ -53,7 +53,7 @@ function login(username: string, password: string) {
|
||||
\label{fig:LoginErgebnis}
|
||||
\end{figure}
|
||||
\newpage
|
||||
Eine weiterer Punkt, an dem oft auf Leerzeilen zurückgegriffen wird, ist das Erstellen von Klassen.
|
||||
Ein weiterer Punkt, an dem oft auf Leerzeilen zurückgegriffen wird, ist das Erstellen von Klassen.
|
||||
Dort werden oft zusammengehörende Felder gruppiert.
|
||||
Das folgende Codebeispiel \ref{fig:LeerzeileFelder} zeigt den Anfang einer Klasse für eine Nutzer Authentifizierung.
|
||||
\begin{figure}[ht]
|
||||
@@ -64,7 +64,6 @@ class Authentication{
|
||||
private password: string;
|
||||
|
||||
private timestamp: Date;
|
||||
//..
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Authentication Klasse mit einer Leerzeile}
|
||||
@@ -82,20 +81,20 @@ class UserCredentials{
|
||||
}
|
||||
|
||||
class Authentication{
|
||||
private userCredentials: UserCredentials;
|
||||
private timestamp: Date;
|
||||
//..
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Loginmethode mit einer Leerzeile}
|
||||
\caption{Nutzerinformationen in eigener Klasse}
|
||||
\label{fig:LeerzeileFelderErgebnis}
|
||||
\end{figure}\\
|
||||
\newpage
|
||||
\section{Doppelten Code zusammenführen}
|
||||
\section{Ähnlichen Code zusammenführen}
|
||||
Es gibt immer wieder Situationen, in den zwei Methoden oder Klassen nahezu identisch sind, es aber trotzdem einen kleinen Unterschied gibt.
|
||||
In solchen Fällen wird oft auf das Refactoring verzichtet, da der Aufwand zu groß erscheint oder die Entwickler im Programmierfluss keine einfache Lösung finden den Code zusammenzuführen.\citep[S. 326 f.]{fiveLines.2023}\\
|
||||
Das folgende Beispiel zeigt zwei Klassen, die jeweils einen Fromatierer umsetzen.
|
||||
Das folgende Beispiel zeigt zwei Klassen, die jeweils einen Formatierer umsetzen.
|
||||
\begin{figure}[ht]
|
||||
\begin{subfigure}[t]{0.48\textwidth}
|
||||
\begin{subfigure}[t]{0.49\textwidth}
|
||||
\centering
|
||||
\begin{minipage}[t]{\linewidth}
|
||||
\begin{minted}[linenos=false]{typescript}
|
||||
@@ -112,11 +111,11 @@ class XMLFormatter {
|
||||
}
|
||||
\end{minted}
|
||||
\end{minipage}
|
||||
\caption{Klasse eines XML-Formattierers}
|
||||
\caption{Klasse eines XML-Formatierers}
|
||||
\label{fig:XmlAnfang}
|
||||
\end{subfigure}
|
||||
\hfill
|
||||
\begin{subfigure}[t]{0.48\textwidth}
|
||||
\begin{subfigure}[t]{0.49\textwidth}
|
||||
\centering
|
||||
\begin{minipage}[t]{\linewidth}
|
||||
\begin{minted}[linenos=false]{typescript}
|
||||
@@ -134,20 +133,210 @@ class JSONFormatter {
|
||||
}
|
||||
\end{minted}
|
||||
\end{minipage}
|
||||
\caption{Klasse eines JSON-Formattierers}
|
||||
\caption{Klasse eines JSON-Formatierers}
|
||||
\label{fig:JsonAnfang}
|
||||
\end{subfigure}
|
||||
\caption{Ähnliche Formattierer Klassen \citep[S. 327]{fiveLines.2023}}
|
||||
\caption{Ähnliche Formatierer Klassen \citep[S. 327]{fiveLines.2023}}
|
||||
\label{fig:FormatterAnfang}
|
||||
\end{figure}\\
|
||||
Vergleicht man die beiden Klassen \ref{fig:XmlAnfang} und \ref{fig:JsonAnfang}, fällt auf dass diese nahezu identisch sind.
|
||||
Vergleicht man die beiden Klassen \ref{fig:XmlAnfang} und \ref{fig:JsonAnfang}, fällt auf, dass diese nahezu identisch sind.
|
||||
Ein Unterschied ist zum Einen die abweichende Formatierung jedes einzelnen Wertes.
|
||||
Bei XML wird der Wert in ein Tag eingebettet, bei JSON wird ein Objekt mit einem Schlüssel-Wert-Paar in geschweiften Klammern erstellt.
|
||||
Dieser Unterschied liese sich einfach vereinen lassen, da dort nur unterschiedliche Zeichenketten eingefügt werden müssen.
|
||||
Die ausschlaggebende Problematik stellt, in diesem Beispiel, das trennende Komma zwischen einzelnen Werten im JSON Format dar.
|
||||
Dieser Unterschied liese sich einfach vereinen, da dort nur unterschiedliche Zeichenketten eingefügt werden müssen.
|
||||
Die ausschlaggebende Problematik stellt das trennende Komma zwischen einzelnen Werten, im JSON Format, dar.
|
||||
Dies verhindert eine einfache Zusammenführung der beiden Klassen.
|
||||
Im folgenden wird ein möglicher Lösungsansatz gezeigt, solche Probleme trotzdem zu lösen.
|
||||
blabla
|
||||
Da das Refactoring aus mehreren kleinen, aber einfachen Schritten besteht, betrachten wir das in der nachfolgenden Abbildung \ref{fig:FormatterEnde} gezeigte Ergebnis.
|
||||
|
||||
\begin{figure}[ht]
|
||||
\begin{subfigure}[t]{0.49\textwidth}
|
||||
\centering
|
||||
\begin{minipage}[t]{\linewidth}
|
||||
\begin{minted}[linenos=false]{typescript}
|
||||
class XMLFormatter {
|
||||
format(vals:string[]) {
|
||||
return new Formatter(
|
||||
new FormatSingle
|
||||
("<value>", "</value>"),
|
||||
new None()).format(vals);
|
||||
}
|
||||
}
|
||||
class JSONFormatter {
|
||||
format(vals:string[]) {
|
||||
return new Formatter(
|
||||
new FormatSingle
|
||||
("{ value: '", "' }"),
|
||||
new Comma()).format(vals);
|
||||
}
|
||||
}
|
||||
class Formatter {
|
||||
constructor(
|
||||
private single: FormatSingle,
|
||||
private sep: Seperator) { }
|
||||
format(vals: string[]) {
|
||||
let result = "";
|
||||
for (let i=0; i < vals.length; i++)
|
||||
{
|
||||
reuslt =
|
||||
this.sep.put(i,result);
|
||||
result +=
|
||||
this.single.format(vals[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
\end{minted}
|
||||
\end{minipage}
|
||||
\caption{Formattierer Klassen}
|
||||
\label{fig:gleichGut1}
|
||||
\end{subfigure}
|
||||
\hfill
|
||||
\begin{subfigure}[t]{0.49\textwidth}
|
||||
\centering
|
||||
\begin{minipage}[t]{\linewidth}
|
||||
\begin{minted}[linenos=false]{typescript}
|
||||
class FormatSingle {
|
||||
constructor(
|
||||
private before: string,
|
||||
private after: string) { }
|
||||
format(val: string) {
|
||||
return `${prefix}${val}${after}`
|
||||
}
|
||||
}
|
||||
|
||||
class Seperator {
|
||||
put(i: number, result: string): string;
|
||||
}
|
||||
class Comma implements Seperator {
|
||||
put(i: number, result: string) {
|
||||
if (i > 0) result += ",";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
class None implements Seperator {
|
||||
put(i: number, result: string) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
\end{minted}
|
||||
\end{minipage}
|
||||
\caption{Seperator Klassen}
|
||||
\label{fig:gleichGut2}
|
||||
\end{subfigure}
|
||||
\caption{Ergebnis der Formatierer nach Refactoring \citep[S. 329 f.]{fiveLines.2023}}
|
||||
\label{fig:FormatterEnde}
|
||||
\end{figure}
|
||||
\newpage
|
||||
Bei der Betrachtung der Klasse \textit{Formatter} fällt auf, dass diese eine große Ähnlichkeit zu den beiden ursprünglichen Klassen aufweist.
|
||||
Der Unterschied besteht darin, dass im Konstruktor zwei weitere Klassen übergeben werden, die das Formatieren der einzelnen Werte und das Einfügen des trennenden Kommas übernehmen.\\
|
||||
Die erste Klasse \textit{FormatSingle} übernimmt die genereller Formatierung einer Eingabe. Sie kann mit zwei beliebigen Zeichenketten instanziiert werden, die jeweils vor und nach dem eigentlichen Wert eingefügt werden.\\
|
||||
Die Zweite, \textit{Seperator}, übernimmt das Einfügen eines trennenden Zeichens zwischen den einzelnen Werten. Bei der Klasse \textit{Comma} wird ein Komma eingefügt, wenn der Index größer als 0 ist. Die Klasse \textit{None} fügt kein Zeichen ein.\\
|
||||
Abschließend müssen noch die jeweiligen Klassen, \textit{XMLFormatter} und \textit{JSONFormatter}, mit den jeweils richtigen Werten instanziiert werden.\\
|
||||
|
||||
Das Ergebnis dieses Refactorings ist eine deutlich verbesserte Struktur, die die Wartbarkeit und Erweiterbarkeit des Codes deutlich erhöht. Es können ohne Probleme weitere Implementierungen von Formatierern erstellt werden, die nach ähnlichen Mustern arbeiten.
|
||||
\section{Gemeinsame Affixe nutzen}
|
||||
\section{Den Laufzeittyp bearbeiten}
|
||||
Ein weiteres eindeutiges Merkmal, für ein klares Refactoring, ist die Verwendung von gemeinsamen Affixen in Methoden- oder Klassennamen.
|
||||
Dies deutet darauf hin, dass diese Codeteile eine klare Zusammengehörigkeit haben und in eine gemeinsame Struktur gekapselt werden können.
|
||||
Laut der Regel \textbf{\textit{„Vermeide gemeinsame Affixe“}}, können alle Felder und Methoden mit gleichem Präfix oder Suffix in eine eigene Klasse oder ein eigenes Interface extrahiert werden.
|
||||
Das Affix wird dabei zum Namen der neu erstellten Struktur \citep[S. 201 ff.]{fiveLines.2023}.\\
|
||||
Das folgende Beispiel zeigt Code, bei dem sofort auffällt, dass diese Regel verletzt wird.
|
||||
\begin{figure}[ht]
|
||||
\centering
|
||||
\begin{minted}{typescript}
|
||||
interface Protocol {...}
|
||||
class StringProtocol implements Protocol {...}
|
||||
class JSONProtocol implements Protocol {...}
|
||||
class ProtobufProtocol implements Protocol {...}
|
||||
/// ...
|
||||
let p = new StringProtocol();
|
||||
/// ...
|
||||
\end{minted}
|
||||
\caption{Codeausschnitt mit gemeinsamen Suffix \citep[S. 331]{fiveLines.2023}}
|
||||
\label{fig:AffixeSchlecht}
|
||||
\end{figure}\\
|
||||
Wird der Code betrachtet, fällt auf, dass \textit{„Protocol“} in allen Klassennamen vorkommt.
|
||||
Der Grund warum Programmierende dieses Refactoring in diesem Fall nicht durchführen, wird klar, wenn das Ergebnis betrachtet wird.
|
||||
Entfernt man bei der Klasse \textit{StringProtocol} das Suffix, bleibt nur noch \textit{„String“} übrig.
|
||||
Dies steht in Konflikt mit einer Basisklasse der Programmiersprache \citep[S. 330 f.]{fiveLines.2023}.
|
||||
Um diese Codestelle trotzdem zu verbessern, kann das Refactoring in leicht abgeänderter Form durchgeführt werden.
|
||||
Wie im folgenden Codebeispiel \ref{fig:AffixeGut} gezeigt, kann das Problem mithilfe eines \textit{„Namespace“} gelöst werden.
|
||||
\begin{figure}[ht]
|
||||
\centering
|
||||
\begin{minted}{typescript}
|
||||
namespace ptotocol {
|
||||
export interface Protocol {...}
|
||||
export class String implements Protocol {...}
|
||||
export class JSON implements Protocol {...}
|
||||
export class Protobuf implements Protocol {...}
|
||||
}
|
||||
|
||||
/// ...
|
||||
let p = new protocol.String();
|
||||
/// ...
|
||||
\end{minted}
|
||||
\caption{Codeausschnitt mit Namespace \citep[S. 331]{fiveLines.2023}}
|
||||
\label{fig:AffixeGut}
|
||||
\end{figure}\\
|
||||
Dieser Ansatz ist zwar zielführend, trotzdem sollte hier von Fall zu Fall entschieden werden, ob es eine bessere Lösung gibt, dies zu lösen.
|
||||
Da \textit{„String“} eine häufig verwendete Klasse ist, erschwert das gezeigte Beispiel die Nutzbarkeit des Codes weiter und ist nicht sinnvoll.
|
||||
\section{Den Laufzeittyp bearbeiten}
|
||||
Das letzte Beispiel, das betrachtet werden soll, zeigt eine Situation bei der auf den Operator \textit{\textbf{„instanceof“}} zurückgegriffen wird, um abhängig davon verschiedene Aufrufe ausgeführt werden sollen.
|
||||
Die Unterscheidung wird mithilfe von \textit{\textbf{„if“}}-Abfragen durchgeführt. Laut Clausen sollten \textit{\textbf{„if“}}-Anweisungen in Kombination mit \textit{\textbf{„else“}} vermieden werden.
|
||||
Nur Fälle mit \textit{\textbf{„instanceof“}}, \textit{\textbf{„typeof“}} oder Casts werden als Ausnahme genannt. \citep[S. 332]{fiveLines.2023}\\
|
||||
Das folgende Beispiel \ref{fig:LaufzeitSchlecht} zeigt solch eine Situation.
|
||||
|
||||
\begin{figure}[ht]
|
||||
\centering
|
||||
\begin{minted}{typescript}
|
||||
function foo(obj: any) {
|
||||
if (obj instanceof A) {
|
||||
obj.methodA();
|
||||
} else if (obj instanceof B) {
|
||||
obj.methodB();
|
||||
}
|
||||
}
|
||||
class A {
|
||||
methodA() { ... }
|
||||
}
|
||||
class B {
|
||||
methodB() { ... }
|
||||
}
|
||||
\end{minted}
|
||||
\caption{\textit{if}-Anweisung mit \textit{instanceof} \citep[S. 331]{fiveLines.2023}}
|
||||
\label{fig:LaufzeitSchlecht}
|
||||
\end{figure}
|
||||
In der Funktion \textit{foo} wird überprüft, ob das übergebene Objekt eine Instanz der Klasse \textit{A} oder \textit{B} ist.
|
||||
Abhängig vom Ergebnis werden dann ihre jeweiligen Methoden aufgerufen.
|
||||
Dieses Verhalten ist ein Paradebeispiel für die Nutzung eines Interfaces.\\
|
||||
\begin{singlespace}
|
||||
\textit{„Interfaces erlauben es uns, einer Variablen Objekte verschiedener Klassen zuzuweisen. Wenn wir dann eine Methode daran aufrufen, wird der Aufruf zur richtigen Klasse geleitet. Gleichzeitig ist das auch der Weg, wie wir Typenuntersuchungen zur Laufzeit vermeiden können.“} \citep[S. 332]{fiveLines.2023}\\
|
||||
\end{singlespace}
|
||||
\newpage
|
||||
Das folgende Beispiel \ref{fig:LaufzeitGut} zeigt, wie das Problem mit dem erklärten Ansatz gelöst werden kann.
|
||||
\begin{figure}[ht]
|
||||
\centering
|
||||
\begin{minted}{typescript}
|
||||
function foo(obj: Foo) {
|
||||
obj.foo();
|
||||
}
|
||||
class A implements Foo {
|
||||
foo() {
|
||||
this.methodA();
|
||||
}
|
||||
methodA() { ... }
|
||||
}
|
||||
class B implements Foo {
|
||||
foo() {
|
||||
this.methodB();
|
||||
}
|
||||
methodB() { ... }
|
||||
}
|
||||
interface Foo {
|
||||
foo(): void;
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Refactoring mit Interface \citep[S. 332 f.]{fiveLines.2023}}
|
||||
\label{fig:LaufzeitGut}
|
||||
\end{figure}\\
|
||||
Abschließend lässt sich sagen, dass Refactoring in den gezeigten Situationen oft zu einer besseren Codebasis führt. Auch wenn es im Moment des Programmierens nicht sinnvoll wirkt, sollte der kleine Mehraufwand in Kauf genommen werden.
|
||||
Es ist wichtig, dass Entwickler sich immer wieder bewusst machen, dass Code nicht nur für den Moment geschrieben wird, sondern auch in Zukunft noch wartbar und erweiterbar sein sollte.
|
||||
Müssen andere Entwickler im Nachgang den Code anpassen, nimmt dies mehr Zeit in Anspruch, als wenn der Code von Anfang an sauber strukturiert ist.
|
||||
@@ -1,13 +1,16 @@
|
||||
\chapter{Arten, wie Code Verhalten spiegelt}
|
||||
Im nächsten Teil wird beleuchtet, wie Verhalten im Code abgebildet werden kann. Dabei gibt es grundlegend drei verschiedene Arten. Diese werden im folgenden anhand eines einfachen Beispiels erläutert. Des Weiteren soll auf die Erzeugung von Endlosschleifen eingegangen werden, wie Clausen feststellt, einen Sonderfall darstellen. \cite{fiveLines.2023}
|
||||
\begin{tcolorbox}[colback=gray!20!white, colframe=gray!75!black, title=Beispielverhalten]
|
||||
\chapter{Wie kann Verhalten im Code abgebildet werden?}
|
||||
Im nächsten Teil wird beleuchtet, wie Verhalten im Code abgebildet werden kann. Dabei gibt es grundlegend drei verschiedene Arten.
|
||||
Diese werden im Folgenden anhand eines einfachen Beispiels erläutert.
|
||||
Des Weiteren soll auf die Erzeugung von Endlosschleifen eingegangen werden, wie Clausen feststellt, stellt dies einen Sonderfall dar.
|
||||
Viele Refactorings transformieren Code zwischen diesen Darstellungsformen \citep[S. 313]{fiveLines.2023}.
|
||||
\begin{tcolorbox}[colback=gray!20!white, colframe=gray!75!black, title=Beispiel-Verhalten]
|
||||
Es soll bis zu einer bestimmten ganzen Zahl abwechselnd „gerade“ und „ungerade“ in der Konsole ausgegeben werden. „0“ wird hierbei als gerade angesehen.\\
|
||||
Dieses Verhalten ist am von Clausen genutzten Beispiel (FizzBuzz) nachempfunden und so weit wie möglich vereinfacht, um weiterhin alle nötigen Besonderheiten zu veranschaulichen.\cite{fiveLines.2023}
|
||||
\end{tcolorbox}
|
||||
|
||||
\section{Verhalten im Kontrollfluss}
|
||||
Die erste und wohl einfachste Möglichkeit, Verhalten im Code abzubilden, ist der Kontrollfluss. Dieser zeichnet sich durch die Verwendung von Kontrolloperatoren, Methodenaufrufen und der Zeilenabfolge aus. \cite{fiveLines.2023}
|
||||
In folgender Abbildung werden dafür jeweils einfache Beispiele gezeigt. (\ref{fig:Kontrollfluss})
|
||||
Die erste und wohl einfachste Möglichkeit, Verhalten im Code abzubilden, ist der Kontrollfluss. Dieser zeichnet sich durch die Verwendung von Kontrolloperatoren, Methodenaufrufen und der Zeilenabfolge aus. \citep[S. 313 f.]{fiveLines.2023}
|
||||
In folgender Abbildung \ref{fig:Kontrollfluss} werden dafür jeweils einfache Beispiele gezeigt.
|
||||
\begin{figure}[ht]
|
||||
\begin{subfigure}[t]{0.30\textwidth}
|
||||
\centering
|
||||
@@ -60,8 +63,8 @@ foo(4);
|
||||
|
||||
Der Unterschied dieser Unterkategorien wird bei der Betrachtung des Aufrufs „foo(i)“ und dessen Werthereingabe deutlich. Bei \ref{fig:Kontrolloperatoren} wird mithilfe des „while“ Operators die Funktion aufgerufen und die Eingabe erhöht.\\ Das mittlere Beispiel zeigt die Verwendung einer rekursiven Methode, um das Verhalten darzustellen. Der Eingabeparameter dient hier als Wert für den Funktionsaufruf.\\ Das letzte Beispiel \ref{fig:Zeilenabfolge} zeigt das gleiche Verhalten durch einfache Aufrufe. Hier wird die Funktion mit Wert im Klartext aufgerufen.
|
||||
|
||||
\subsection{Eigenes Beispiel}
|
||||
Folgende Abbildung zeigt das Beispiel-Verhalten im Kontrollfluss.
|
||||
\subsubsection{Eigenes Beispiel}
|
||||
Der folgende Codeausschnitt \ref{fig:KontrollflussIstGerade} zeigt das vorher beschriebene Beispiel im Kontrollfluss.
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{minted}{typescript}
|
||||
@@ -78,11 +81,59 @@ function istGerade(n: number) {
|
||||
\caption{Beispiel im Kontrollfluss}
|
||||
\label{fig:KontrollflussIstGerade}
|
||||
\end{figure}\\
|
||||
Um die Unterschiede der verschiedenen Darstellungsformen zu erkennen, ist es sinnvoll die Aufrufe von \textit{„console.log()“} zu betrachten. Diese stellen bei unserem Beispiel die tatsächlich durchzuführende Aktion dar. In Abbildung \ref{fig:KontrollflussIstGerade} lässt sich erkennen, dass diese Aufrufe durch die Kontrolloperatoren \textit{for} und \textit{if} gesteuert werden.
|
||||
\subsection{Vor und Nachteile}
|
||||
Da das Programmieren im Kontrollfluss schnell und einfach funktioniert, eignet sich diese Art gut, um neues Vehalten initial abzubilden.
|
||||
Um die Unterschiede der verschiedenen Darstellungsformen zu erkennen, ist es sinnvoll die Aufrufe von \textit{„console.log()“} zu betrachten.
|
||||
Diese stellen bei unserem Beispiel die tatsächlich durchzuführende Aktion dar.
|
||||
In Abbildung \ref{fig:KontrollflussIstGerade} lässt sich erkennen, dass diese Aufrufe durch die Kontrolloperatoren \textit{for} und \textit{if} gesteuert werden.
|
||||
Dies stellt ein klassisches Beispiel für das Verhalten im Kontrollfluss dar.
|
||||
\subsubsection{Endlosschleife}
|
||||
Im Kontrollfluss gibt es mehrere Möglichkeiten, eine Endlosschleife zu erzeugen.
|
||||
Da sie am weitesten verbreitet sind, sollten sie unter allen Programmierenden bekannt sein.
|
||||
\begin{figure}[ht]
|
||||
\begin{subfigure}[t]{0.49\textwidth}
|
||||
\centering
|
||||
\begin{minipage}[t]{\linewidth}
|
||||
\begin{minted}[linenos=false]{typescript}
|
||||
for (;;){}
|
||||
|
||||
while (true) {}
|
||||
\end{minted}
|
||||
\end{minipage}
|
||||
\caption{Endlosschleife mit Kontrolloperatoren}
|
||||
\label{fig:endloss1}
|
||||
\end{subfigure}
|
||||
\hfill
|
||||
\begin{subfigure}[t]{0.49\textwidth}
|
||||
\centering
|
||||
\begin{minipage}[t]{\linewidth}
|
||||
\begin{minted}[linenos=false]{typescript}
|
||||
function loop() {
|
||||
loop();
|
||||
}
|
||||
\end{minted}
|
||||
\end{minipage}
|
||||
\caption{Endlosschleife mit rekursiver Methode}
|
||||
\label{fig:endloss2}
|
||||
\end{subfigure}
|
||||
\caption{Endlosschleifen im Kontrollfluss \citep[S. 314]{fiveLines.2023}}
|
||||
\label{fig:Endlossschleifen1}
|
||||
\end{figure}\\
|
||||
Die einzige Besonderheit stellt das \textit{„for(;;)“} dar, da es durch eine leere Bedingung niemals terminiert.
|
||||
\subsubsection{Vor und Nachteile}
|
||||
Da das Programmieren im Kontrollfluss schnell und einfach funktioniert, eignet sich diese Art gut, um neues Verhalten initial abzubilden.
|
||||
Umfassende Änderungen sind sofort möglich, da zum Beispiel einfach die Reihenfolge der Aufrufe geändert werden kann und somit sofort ein anderes Verhalten entsteht.
|
||||
Dieser Punkt ist aber nicht zwingend als Vorteil zu sehen, da wir in der Softwareentwicklung Stabilität und gute Wartbarkeit bevorzugen \citep[S. 313 f.]{fiveLines.2023}.
|
||||
|
||||
\section{Verhalten in der Struktur der Daten}
|
||||
\subsection{Eigenes Beispiel}
|
||||
Die Zweite Möglichkeit Verhalten im Code abzubilden, sind Datenstrukturen.
|
||||
Diese zeichnen sich durch die Verwendung von objektorientierter Programmierung aus.
|
||||
Also durch Klassen, Interfaces, Vererbung, etc. \citep[S. 315 f.]{fiveLines.2023}\\
|
||||
Eines der bekanntesten Beispiele für die Verwendung von Datenstrukturen ist die \textit{„binäre Suche“}.
|
||||
Diese setzt einen sortierten Binärbaum voraus, um zu funktionieren.
|
||||
Suchen wir nun nach einem Wert, wird dieser mit dem der Wurzel verglichen und je nach Ergebnis rekursiv in den linken oder rechten Teilbaum abgestiegen.
|
||||
Das Verhalten wird also maßgeblich durch die Struktur des Baumes bestimmt \citep[S. 315 f.]{fiveLines.2023}.
|
||||
|
||||
\subsubsection{Eigenes Beispiel}
|
||||
Das folgende Beispiel \ref{fig:DatenstrukturIstGerade} zeigt das vorher beschriebene Beispiel in einer Datenstruktur.
|
||||
\begin{figure}[ht]
|
||||
\centering
|
||||
\begin{minted}{typescript}
|
||||
@@ -111,11 +162,46 @@ class UngeradeZahl implements Zahl{
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Beispiel in einer Datenstruktur}
|
||||
\label{fig:KontrollflussIstGerade}
|
||||
\end{figure}
|
||||
\label{fig:DatenstrukturIstGerade}
|
||||
\end{figure}\\
|
||||
Um das Verhalten abzubilden, wird auf den Ansatz einer einfach verketteten Liste gesetzt.
|
||||
Es wird ein Interface \textit{„Zahl“} verwendet, welches die Methode \textit{„istGerade()“} vorschreibt.
|
||||
Jede der beiden Klassen \textit{„GeradeZahl“} und \textit{„UngeradeZahl“} implementiert dieses Interface und ruft die Methode der jeweils anderen Klasse auf, solange der Zähler nicht bei 0 angelangt ist.
|
||||
\subsubsection{Endlosschleife}
|
||||
\begin{figure}[ht]
|
||||
\centering
|
||||
\begin{minted}{typescript}
|
||||
class Rec {
|
||||
constructor(public readonly f:(_: Rec) => void) {}
|
||||
}
|
||||
|
||||
function loop() {
|
||||
let helper = (r: Rec) => r.f(r);
|
||||
helper(new Rec(helper));
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Endlossschleife in einer Datenstruktur \citep[S. 316]{fiveLines.2023}}
|
||||
\label{fig:DatenstrukturEndlossschleife}
|
||||
\end{figure}
|
||||
Der Codeausschnitt \ref{fig:DatenstrukturEndlossschleife} zeigt eine Endlossschleife in einer Datenstruktur.
|
||||
Dieses Beispiel ist nicht so einfach zu verstehen, da es sich um eine Klasse handelt, die im Konstruktor eine Methode erwartet, die wiederum die selbe Klasse als Parameter erwartet.
|
||||
In der Funktion \textit{„loop()“} wird auf einer Hilfsvariablen eine solche Methode erstellt.
|
||||
Im Inhalt dieser Methode wird auf dem hereingegebenen Objekt die gleiche Methode erneut aufgerufen.
|
||||
Somit ist das Verhalten nun ähnlich zu einem normalen rekursiven Funktionsaufruf, nur dass hier die Klasse als Parameter übergeben wird.
|
||||
Um die Endlosschleife zu starten, muss die eben erstellte Methode mit einem neuen Objekt der Klasse \textit{„Rec“} aufgerufen werden.
|
||||
Da dieser Ansatz nur schwer zu verstehen ist und vor allem schwer zu lesen ist, sollte er nur in Ausnahmefällen verwendet werden \citep[S. 316]{fiveLines.2023}.
|
||||
\subsubsection{Vor und Nachteile}
|
||||
Das Verhalten in Datenstrukturen umzusetzen, hat mehrere positive Effekte.
|
||||
Zum einen erreichen wir durch den objektorientierten Ansatz typensicherheit, was den Code lesbarer und wartbarer macht.
|
||||
Der vorher erwähnte Wunsch nach Stabilität und der Möglichkeit nur kleinere Anpassungen vorzunehmen, wird ebenfalls erfüllt.
|
||||
Auch in der Performance kann es zu einer Verbesserung kommen, da die Datenstrukturen teilweise im Cache abgespeichert werden können.
|
||||
Allerdings ist der Aufwand, um Verhalten in Datenstrukturen abzubilden, höher als im Kontrollfluss \citep[S.315 ff.]{fiveLines.2023}.
|
||||
\newpage
|
||||
\section{Verhalten in den Daten}
|
||||
\subsection{Eigenes Beispiel}
|
||||
Das Verhalten in den tatsächlichen Daten zu kodieren, ist die letzte Möglichkeit, die Clausen in seinem Buch beschreibt.
|
||||
Diese zeichnet sich vor allem durch die Nutzung von Funktionen und Lambdas, die in Arrays gespeichert werden \citep[S. 319 f.]{fiveLines.2023}.
|
||||
|
||||
\subsubsection{Eigenes Beispiel}
|
||||
\begin{figure}[ht]
|
||||
\centering
|
||||
\begin{minted}{typescript}
|
||||
@@ -130,6 +216,36 @@ function istGerade(n: number) {
|
||||
}
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Beispiel in einer Datenstruktur}
|
||||
\label{fig:KontrollflussIstGerade}
|
||||
\end{figure}
|
||||
\caption{Beispiel in den Daten selbst}
|
||||
\label{fig:DatenIstGerade}
|
||||
\end{figure}
|
||||
Die Umsetzung des Beispielverhaltens in den Daten selbst ist in Abbildung \ref{fig:DatenIstGerade} zu sehen.
|
||||
Hier sind die \textit{„console.log()“}-Aufrufe in einem Array gespeichert.
|
||||
Über dieses Array wird in der Funktion \textit{„istGerade()“} iteriert und die Funktionen darin aufgerufen.
|
||||
Die Besonderheit hierbei ist, dass der Modulo-Operator nicht mit einer festen Zahl, sondern mit der Länge des Arrays verwendet wird.
|
||||
Dadurch wird noch deutlicher, dass das Verhalten in den Daten abgebildet wird.
|
||||
\subsubsection{Endlosschleife}
|
||||
\begin{figure}[ht]
|
||||
\centering
|
||||
\begin{minted}{typescript}
|
||||
function loop() {
|
||||
let a = [() => { }];
|
||||
a[0] = () => a[0]();
|
||||
a[0]();
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Endlossschleife in Daten abgebildet \citep[S. 319]{fiveLines.2023}}
|
||||
\label{fig:DatenEndloss}
|
||||
\end{figure}
|
||||
Die Endlosschleife ist in diesem Fall weider einfacher zu verstehen.
|
||||
Es wird ein Array erstellt, welches Funktionen enthält.
|
||||
Anschließend wird auf dem ersten Feld eine Funktion gespeichert, die dann wieder das erste Feld des Arrays aufruft.
|
||||
Auch hier ist es im Endeffekt eine rekursive Funktion, die aber durch das Array abgebildet wird.
|
||||
Um die Schleife zu starten, wird die Funktion im ersten Feld des Arrays aufgerufen \citep[S. 319]{fiveLines.2023}.
|
||||
\newpage
|
||||
\subsubsection{Vor und Nachteile}
|
||||
Der einzige Vorteil, den Clausen in seinem Buch nennt ist die in manchen Fällen beste Performance, im Vergleich zu den anderen beiden Darstellungsformen.
|
||||
Jedoch muss man einige negative Aspekt in Kauf nehmen. Durch diese Art der Programmierung erhält man nur eine schlechte Unterstützung durch den Compiler.
|
||||
Es gibt keine Erkennung ob ein gültiges Feld aus dem Array aufgerufen wird oder ob die Funktionen im Array überhaupt die selben Parameter erwarten.
|
||||
Außerdem kann es schnell zu Konmsitentzproblemen kommen, wenn die gespeicherten Daten veränderbar sind.
|
||||
Diese Form der Darstellung sollte also nur in Ausnahmefällen verwendet werden, in denen die Performance eine große Rolle spielt und die anderen Nachteile in Kauf genommen werden können \citep[S. 319 f.]{fiveLines.2023}.
|
||||
@@ -5,7 +5,7 @@
|
||||
address = {Bonn},
|
||||
publisher = {{Rheinwerk Verlag}},
|
||||
isbn = {9783836292245},
|
||||
pages = {311--333}
|
||||
pages = {201--203, 311--333}
|
||||
}
|
||||
@article{conway.1968,
|
||||
title={How do committees invent},
|
||||
@@ -50,4 +50,40 @@
|
||||
number={3},
|
||||
pages={14-16},
|
||||
keywords={Production;Servers;Monitoring;Software;Green products;Marine vehicles;Multimedia communication;continuous integration;continuous delivery;testing},
|
||||
doi={10.1109/MS.2014.58}}
|
||||
doi={10.1109/MS.2014.58}
|
||||
}
|
||||
@book{refactoring.2020,
|
||||
author = {Fowler, Martin},
|
||||
year = {2020},
|
||||
edition={2},
|
||||
title = {Refactoring - Wie Sie das Design bestehender Software verbessern},
|
||||
address = {Frechen},
|
||||
publisher = {{mitp Verlags GmbH \& Co. KG}},
|
||||
isbn = {9783958459410},
|
||||
pages = {80--90}
|
||||
}
|
||||
@InProceedings{modernIDEs.2022,
|
||||
author="Alizadehsani, Zakieh
|
||||
and Gomez, Enrique Goyenechea
|
||||
and Ghaemi, Hadi
|
||||
and Gonz{\'a}lez, Sara Rodr{\'i}guez
|
||||
and Jordan, Jaume
|
||||
and Fern{\'a}ndez, Alberto
|
||||
and P{\'e}rez-Lancho, Bel{\'e}n",
|
||||
editor="Corchado, Juan M.
|
||||
and Trabelsi, Saber",
|
||||
title="Modern Integrated Development Environment (IDEs)",
|
||||
booktitle="Sustainable Smart Cities and Territories",
|
||||
year="2022",
|
||||
publisher="Springer International Publishing",
|
||||
address="Cham",
|
||||
pages="274--288",
|
||||
isbn="978-3-030-78901-5"
|
||||
}
|
||||
@online{glinz2004formale,
|
||||
title={Formale Verifikation},
|
||||
author={Glinz, Martin},
|
||||
year={2004},
|
||||
url = {https://files.ifi.uzh.ch/rerg/amadeus/teaching/courses/kvse_ss05/kapitel_04.pdf},
|
||||
urldate = {2024-07-01}
|
||||
}
|
||||
Binary file not shown.
@@ -91,8 +91,10 @@
|
||||
\mainmatter % die einzelnen Kapitel, bei Bedarf weitere *.tex Dateien erzeugen und hier einbinden
|
||||
\input{./Bestandteile/strukturImCode}
|
||||
\input{./Bestandteile/verhaltenImCode}
|
||||
\input{./Bestandteile/immerRefactoren}
|
||||
\input{./Bestandteile/sicherheit}
|
||||
\input{./Bestandteile/ungenutzteStruktur}
|
||||
\input{./Bestandteile/fazit}
|
||||
% ----------------------------------------------
|
||||
\backmatter
|
||||
|
||||
|
||||
Reference in New Issue
Block a user