Nun möchte ich mit Ihnen Refactoring in einem kleinen Beispiel vorgehen,
durchgehen, um zu zeigen, wie arbeitet man mit Bad Smells?
Wie arbeitet man mit Refactorings? Wie bettet man das in
den Entwicklungsprozess, während das auf der Wartung ein?
Vorab möchte ich sagen, es handelt sich um ein kleines Beispiel.
Das ist immer das bisschen das Problem in solchen Lehrveranstaltungen.
Wir können nur etwas machen,
was in der begrenzten Zeit und an dem begrenzten Platz zum Beispiel
auf einer Folie darstellbar ist
Wir haben hier ein kleines System aus der Universität.
Und dieses System besteht aus drei Klassen, eine Klasse Vorlesung,
die für sehr viele Sachen hier Veranstaltungen, wer macht das?
Gibt es Bewertungen, so was zuständig ist? Dann gibt es die Studierenden,
Mitnahme und so weiter, was sie denn machen.
Die melden sich an und es gibt ein Vorlesungsverzeichnis. Dieses System,
wir nehmen mal an, das ist schon gebaut worden,
es ist im Betrieb und wir haben jetzt ein paar Änderungswünsche.
Diese Änderungswünsche betreffen, ich möchte für die Vorlesung nicht nur eine Bewertung speichern.
Wir sehen hier, es gibt hier eine Gesamtbewertung,
eine Bewertung geht dozent, aber immer nur einmal, je vor Veranstaltung.
Ich möchte da mehr als eine speichern, ja,
und ich möchte den Studenten Bonuspunkte geben, Bundespunkte dafür,
dass sie eine Bewertung machen, ja, oder so etwas.
Und da müssen wir irgendwie etwas tun mit dieser,
mit dieser Klasse Student oder mit den Veranstaltungstypen oder so etwas.
Also das ist das, was wir funktional wollen
Funktional ist meistens der Trigger, weil das ist das,
was der Auftraggeber von uns will. Wir nehmen jetzt mal an,
das will der Auftraggeber von uns und wir sind für das Projekt zuständig.
Und wir wissen, okay, da ist so einiges,
was man doch verbessern könnte, es funktioniert alles,
ja, aber Wartbarkeit ist nicht so gut.
Und ja, die schauen es jetzt an, wie wir,
die mit diesen funktionalen Veränderungen, die wir hier stehen haben,
mit diesen Änderungswünschen des Auftraggebers, wie wir damit,
welche Refactoring-Maßnahmen wir damit sinnvollerweise verbinden und was wir dabei machen.
Hier steht einiges natürlich nicht da, zum Beispiel,
was sind die Hauptprioritäten dieses Projektes,
kommt es vor allen Dingen auf Wartbarkeit an,
kommt es auf Zuverlässigkeit an,
auf Code-Standardeinhaltung oder so etwas
Wir gehen einfach mal davon aus, Wartbarkeit, Verständlichkeit,
Modularität, gute Kapselung, fachliche Zuordnung, diese klassischen Ziele sind das.
Okay, wir kümmern uns, fangen mal mit dem ersten an.
Wir wollen für eine Bewertung für eine Vorlesung mehr als eine Bewertung starten.
Und wir schauen uns dabei die klasse Vorlesung an.
Und diese klasse Vorlesung ist, hat einen Bad Smell,
hat etwas, was nicht so toll ist,
nämlich ist eine sehr große Klasse.
Und diese sehr große Klasse hat zum Beispiel,
wie sie definiert,
Für Bewertungen von Vorlesungen. Und sie hat hier mehrere Bewertungen,
die sie hat. Und wir könnten jetzt mal versuchen,
das irgendwie kleiner zu machen. Wir könnten zum Beispiel
das, was mit Bewertungen zu tun hat, rausziehen.
Weil wir wollen ja auch noch an der Veranstaltungstypen haben,
wissen wir schon später.
Und dadurch wird das schon ein bisschen einfacher. Ja,
also wir können versuchen, eine klasse Bewertung rauszuziehen aus der Vorlesung,
die dann der Vorlesung zugeordnet wird
Wie läuft das ab? Wir nehmen das Ben Small Extract Class,
das Extract Class hat im ersten Schritt, erzeugen wir erstmal eine neue Klasse.
Diese neue Klasse tut nichts weiter. Das ist der erste
von diesen kleinen Schritten, von denen ich sprach.
Wir können testen. Und das einzige,
was hier eigentlich auftreten kann, ist, dass es ein Namenskonflikt gibt,
weil wir diesen Namen schon mal vergeben hatten.
Dann würden wir hier Methoden erzeugen, die eine Kopie der Methoden sind,
die wir hier auch haben und jedenfalls für eine Signatur
her.
Diese Methode haben wir
hier übernommen
Dazu brauchen wir natürlich irgendwie die Felder, die dazugehören.
Zum Beispiel so eine Gesamtbewertung. Ja, so und dazu
müssen wir hier wieder ein Reflectoring bemühen, Move Field.
Ja, Move Field ist ein anderes Reflektoring.
Wir gehen jetzt nicht im Detail drauf ein. So,
damit haben wir schon eine weitere Klasse und wir haben
einiges ausgelagert und jetzt sieht das so aus,
dass wir hier die Klasse Vorlesung mit einer deutlich geringeren Größe haben.
Wir haben diese Klasse Bewertung. Wir haben ein Vorlesungsverzeichnis und
einen Studenten
Damit ist es möglich, das erste Ziel zu erfüllen,
nämlich eine Vorlesung, kann mehr als eine Bewertung haben,
indem wir hier mit bewährter Vorlesung,
haben wir Bewertungen hier drin und da können wir auch mehrere draus machen.
So, jetzt wollen wir, ach so, genau,
jetzt kommt dieses mehrere draus machen, ja,
also wir hatten bis jetzt ja hier nur eine Bewertung und jetzt
wollen wir mehrere draus machen und das ist durchaus auch möglich,
indem wir jetzt hier zum Beispiel so eine Liste machen.
Ja, die Liste ist dann eine Liste von Bewertungen,
wir haben mehrere
Und hier haben wir im Konstruktor, können wir hier noch
angeben, was das für eine Lehrveranstaltung ist.
Also nicht nur der Lehrerkonstruktor, sondern der gleich die Veranstaltung anlegt.
Und da wird eine leere Arraylist angelegt für die Bewertung reinpassen.
Und wenn jemand die eine Bewertung vorgibt, dann wird die zu dieser Realist hinzugefügt.
Soweit so unspektakulär. Also wir haben dann einfach nur statt
wie vorher hier im Vorgehen hat mir eine Bewertung.
Und jetzt haben wir halt diese Arraylist.
Super, oder
Ganz klar zu verstehen, kein Problem, super verständlich.
Und auch in dieser Struktur hier,
man sieht ja sofort, was ist wofür zuständig,
hier gibt es diese Bewertung und das gibt es hier als Klasse.
Und ja, einfach unspektakulär, so sollte es sein. So,
jetzt kommen wir zum ersten Änderungswunsch, haben wir einen Haken dran,
jetzt kommen wir zum zweiten, wir wollen Bonuspunkte vergeben.
Ja, und zwar Bonuspunkte, je nach bewerteter Veranstaltung.
Und bis jetzt gab es aber nur Vorlesungen,
Übungen und jetzt soll es aber noch Seminar geben und vielleicht
noch weitere Veranstaltungen
Vorher gab es nur eine Methode, Bonus, Student,
also der Student kriegt irgendwelche Bonuspunkte und jetzt soll es aber
für mehrere Veranstaltungspunkte unterschiedliche Boni geben.
Und diese unterschiedlichen Boni müssen wir jetzt irgendwie einbauen mit den Veranstaltungstypen.
Und bis jetzt gab es da eigentlich nur zwei Veranstaltungen und
das wurde also nämlich Vorlesung und Übung.
Und da gab es mit so einer netten Switch-Anweisung,
gab es zum Beispiel vor Vorlesungen den zweifachen Bundesfaktor und für eine Übung zweifachen Bonusfaktor plus drei.
So, und Deswegen dieser Typ, Typ der Vorlesung,
der steuert sehr viel, was hier passiert.
Und das wollen wir ein bisschen besser machen.
Und wir wollen deswegen hierzu eine Vererbungshierarchie von Typen einführen,
also die Veranstaltungen, dann runter kommen die Typen.
Und wir wollen diese Switch-Einweisung eliminieren, das passt eigentlich nicht zur objektorientierten Programmieren.
Und wir haben hier zwei Klassen, Replace-Type-Code,
with top classes und replace-conditional with Polymorphism. Die beiden fügen wir jetzt hier ein und dann sieht das so aus,
dass wir also hier den Bonus haben und hier gibt es eine Methode,
berechnet Bonusfaktor und diese Methode, Rechnemethode,
Bonusfaktor hat hier oben drin keinen Quellco-Quelltext, keinen Body,
weil das ist eine abstrakte Klasse und die konkreten Klassen,
die wir drunter hängen haben, die bringen diese Methoden mit,
die implementieren die Methode.
Und damit haben wir erstmal überhaupt das für verschiedene Typen
gemacht und dann könnten wir jetzt weitere Typen dazu finden,
wir können unter Berechnungsverfahren machen oder so und so weiter.
Also deswegen, das sind die nächsten Schritte,
die wir jetzt tun
Achso, hier, replace type code with top classes,
ist das Refactoring, was wir hier tun.
So, jetzt schauen wir uns die Methode Bonus an. Ja,
diese Methode Bonus wird der Student seinen Bonus berechnet, wird,
ist, dass er, man fängt erstmal an mit Null,
wenn der Student, wenn das losgeht, ja,
und dann bewertet der Student,
hat sich deine Liste von Vorlesungen,
dann hat er ein Semester und so weiter und
dann tut er etwas,
berechnet den Bonus
Wenn wir uns anschauen, wo er das überall herholt,
dann ist das ein klarer Fall von Neid.
Also diese Methode kümmert sich um Sachen,
die gar nicht bei ihr sind. Wenn wir schauen,
wo das ist, bei Student, bei Student,
bei Student, bei Student, die macht lauter für solche Sachen,
die eigentlich zu Student gehören.
Also die Methode ist eher an etwas interessiert,
was bei Student ist, also dahin weg mit ihr.
Ja, dort gehört sie eigentlich hin. Das ist das,
was wir brauchen. Und das zuerst mal zu erkennen,
ist natürlich viel einfacher. Man könnte sagen, so und
so viel Prozent oder was auch immer wird eine Metrik ausspucken.
Am besten ist, wir wissen,
das ist das Reflekt, das ist das Ben Smell Night
Feature NV
Okay, wir schieben die dahin und dabei müssen wir auch noch Parameter entfernen.
Ja, und eine lokale Variablen brauchen wir nicht mehr.
Exemplar-Variablen können wir dafür verwenden, Felder also. So,
und dass die lokalen Variablen nehmen wir raus,
dann können wir das so berechnen und fertig.
Eine ganz einfache Methode. Hier ist natürlich nicht alles dargestellt,
aber so schwierig ist das nicht mehr.
So, jetzt haben wir noch ein weiteres Refactoring,
was wir auch noch anbringen können, nämlich Replaced Tamp with Queery.
Wir berechnen dazu, wir lagern die Berechnung aus,
in eine Methode, die das tut.
Also hier zum Beispiel berechnet den Basisbonus oder für den verschiedenen Sachen.
Wenn also jemand informatisch studiert,
dann hat er den Basisbonus und wenn er nicht informatisch studiert,
dann kriegt er zwei Punkte weniger im Bonus.
Oder nicht informatiologisch anderen Studiengängen, die ich informatik sind, bewerten.
Wir sollen den meisten Bonus kriegen für die Bewertung in der Informatik.
Können wir ja machen, ist eine fachliche Festlegung,
vielleicht vom Studiumbüro oder von irgendjemandem,
der Auswertungen gerne für uns selbst hätte
So, es gibt auch die Möglichkeit,
ein anderes Refactoring zu machen, nämlich ein Conditional einzusetzen.
Und dieses Konzept könnte ich nur zu ersetzen, sorry,
zu ersetzen durch Polymophismus. Das wäre nochmal diese Switch-Anweisung,
die wir vorhin hatten, wo wir vorhin diese beiden Typen hatten.
Und wir können das jetzt so machen,
dass wir hier diese Berechnung hier auslagern in eine Methode.
Und dass diese Methode Vorlesung war ja eine Unterklasse von Veranstaltungen.
Das hatten wir vorhin
schon mal gesehen
Ja, wie geht das genau? Also nochmal hier zurück.
Wir hatten ja hier festgestellt, das Ding muss eigentlich verlagert werden,
hier rüber und wir machen das aber in mehreren Schritten.
Wir schaffen eine Methode, berechnen einen Bundesfaktor und da packen wir das dann rein.
Und dann können wir sie auch hier verwenden.
Und hier ist es eine abstrakte Methode und Polymorphismus,
also wir haben mehrere Methoden, die das Gleiche tun,
aber in unterschiedlich, mit unterschiedlichen Parametern.
Das ist dann hier auch der Punkt
So, dann sieht das so aus,
die Klassen sind wesentlich kleiner, es ist geklärt,
wo die Bonusberechnungen passiert, nämlich hier beim Studenten,
ja, die Bewertung ist hier und wir haben verschiedene Veranstaltungstypen
und jetzt könnten wir auch noch weitere Veranstaltungstypen hinzufügen.
Okay, ja, damit ist das Beispiel so erst mal zu Ende,
ich möchte Sie noch auf etwas weiteres hinweisen.
Und zwar darauf hinweisen, dass die Dinge von den Ebenen
her manchmal unterschiedlich wirken. Wir haben konkurrierende Ziele,
ob wir den Quellcode supergut lesbar und verständlich machen wollen oder
ob wir die Strukturen supergut verständlich und sichtbar
machen wollen
Entsprechend haben wir manchmal Bad Smells auf der unteren Ebene,
Quellcode Ebb, oder Bad Smells auf der Entwurfsebene.
Und abhängig davon, was uns wichtiger ist, machen wir das eine oder das andere.
In ganz vielen Fällen geht es nicht beides. Also wenn
ich das eine einfüge, mache ich das andere ein bisschen schlechter.
Und wenn ich das oben einfüge, mache ich das unten ein bisschen schlechter. So,
jetzt nochmal das, was wir, weil wir vorhin dieses Neid,
diesen Neidfaktor hatten, hier könnten wir mal zeigen,
das kann man mit einer Metrik auch ermitteln,
ob das Feature, ob das Ben Smell Knight vorliegt,
indem man zum Beispiel sagt,
man hat ziemlich viele fremde Attribute
Und ein schlechtes Verhältnis zu den eigenen. Das mit den
Ebenen hat aber zur Folge, dass sich das vielleicht,
zum Beispiel, wenn ich das Tradedy Pattern einsetze,
das ist so ähnlich, oder nicht so ähnlich,
haben wir noch nicht behandelt.
Das ist ein Muster, was mir hilft,
wenn ich den Zustand ändern möchte.
Ja, wenn ich also eine Klasse habe,
die verschiedene Sachen machen soll und ich kann ja nicht einfach die Klasse umschalten,
das geht nicht. Wenn ein Objekt erzeugt ist,
dann gehört es von einer bestimmten Klasse und da kann ich nicht umschalten.
Aber über Strategy Pattern kann ich da abhängig von dem Zustand
abhängig von Werten, kann ich die etwas ein anderes Verhalten
ja spendieren
Und da gibt es tatsächlich eine Methode, nämlich diese Methode,
die hier unten das für das wirkliche Tun verantwortlich ist.
Die hat alle Kennzeichen von Neid nach dieser Metrik, die wir hier haben.
Ja, und da könnte man auf die Idee kommen,
oh, die muss ich jetzt zerlegen.
Damit würde man sich aber, das ist Strategy-Pattern zu erschießen.
Und damit wäre Verständlichkeit verloren gegangen. Und da muss man also,
ich habe das als Beispiel gebracht, damit sie wissen,
man muss manchmal abwägen, was ist einem wichtiger.
Verständlichkeit auf dieser Ebene hier, von Entwurf,
wo man sagt, ja,
ich habe hier das in das Pattern oder verständliche Aufdrückheit auf der Ebene des Quelltextes,
dass hier drin ganz klar ist,
das ist eine Methode,
die sich um sich selber kümmert
Ja, ein weiterer Punkt, wo Bets Merls und Refactoring
an ihre Grenzen kommen und wo wir Bets Merz hinnehmen,
sind domänenspezifische Sprachen.
Domänn spezifische Sprachen kommen eigentlich in dieser Lehrveranstaltung nicht vor.
Ich wollte es nur darauf hinweisen, dass dort viel imithematischem
Kurt gearbeitet wird. Kurt ja immer wieder genauso aussieht.
Und da macht man relativ häufig so Methodenketten, geschachtelte Funktionen.
Da muss man sagen, diese Sprachen sind ja so,
dass sie eigentlich vieles übernehmen,
was sonst
der Entwickler macht
Ja, und wir haben hier zum Beispiel solche Methodenketten,
das kommt relativ häufig vor. Und da muss ich mir halt auch überlegen,
will ich lieber diese domänenspezifische Sprache mit Generierung von Code habe?
Oder will ich lieber auf Codequalität super lesbare Resultate erreichen?
Und das wäre hier der Punkt, wo ich mich entscheiden muss.
Entweder die untere Ebene oder die Opener-Ebene,
es wird uns nicht gelingen, Refactoring so zu machen,
dass ich auf allen Ebenen super Resultate habe.
Auch APIs weisen manchmal solche Bands mehr als auf, zum Beispiel,
wenn ich so eine Klasse habe und hier gibt es eine,
mache ich eine Methode und eine Klasse,
die dafür zuständig ist
Die Kommunikation mit anderen Elementen zu machen,
dann wäre das sowas ähnliches wie Shortgun, Sergeury,
also diese Verletzung,
weil ganz viele Sachen hier reinwirken und eine Änderung von hier muss hier geändert werden,
eine Änderung von hier muss hier geändert werden und so weiter.
Es gibt viele Änderungsabhängigkeiten, das wollen wir eigentlich vermeiden,
aber wenn wir stattdessen lieber eine API wollen,
dann nehmen wir das andere in Kauf.
Also deswegen, man muss das abwägen. So,
damit sind wir soweit durch mit diesem Beispiel,
was wir hier gemacht haben, ist zu zeigen,
wie wir Veränderungen vornehmen, wie wir Änderungswünsche schrittweise dazu nutzen,
dass wir dort auch Refactorings machen.
Ja, also verbinden immer erstes Refactoring und dann der der
funktionellen Änderungen, wegen dem Tests vor allen Dingen.
Und aber wir verpacken das auch noch so,
dass es zusammenpasst.