CodeGym /Java-Blog /Random-DE /Was sind Anti-Patterns? Schauen wir uns einige Beispiele ...
John Squirrels
Level 41
San Francisco

Was sind Anti-Patterns? Schauen wir uns einige Beispiele an (Teil 2)

Veröffentlicht in der Gruppe Random-DE
Was sind Anti-Patterns? Schauen wir uns einige Beispiele an (Teil 1). Heute setzen wir unsere Überprüfung der beliebtesten Anti-Patterns fort. Wenn Sie den ersten Teil verpasst haben, finden Sie ihn hier . Was sind Anti-Patterns?  Schauen wir uns einige Beispiele an (Teil 2) - 1Entwurfsmuster sind also Best Practices. Mit anderen Worten: Sie sind Beispiele für gute und bewährte Methoden zur Lösung spezifischer Probleme. Anti-Muster wiederum sind ihr genaues Gegenteil, in dem Sinne, dass sie Muster von Fallstricken oder Fehlern bei der Lösung verschiedener Probleme sind (böse Muster). Fahren wir mit dem nächsten Anti-Pattern für die Softwareentwicklung fort.

8. Goldener Hammer

Ein goldener Hammer ist ein Anti-Muster, das durch die Gewissheit definiert wird, dass eine bestimmte Lösung universell anwendbar ist. Beispiele:
  1. Nachdem ein Programmierer auf ein Problem gestoßen ist und ein Muster für die perfekte Lösung gefunden hat, versucht er, dieses Muster überall festzuhalten und auf aktuelle und alle zukünftigen Projekte anzuwenden, anstatt nach geeigneten Lösungen für bestimmte Fälle zu suchen.

  2. Einige Entwickler haben einmal für eine bestimmte Situation eine eigene Variante eines Caches erstellt (weil nichts anderes geeignet war). Später, beim nächsten Projekt, das keine spezielle Cache-Logik beinhaltete, verwendeten sie ihre Variante erneut, anstatt vorgefertigte Bibliotheken (z. B. Ehcache) zu verwenden. Das Ergebnis war eine Menge Bugs und Inkompatibilitäten sowie viel Zeitverschwendung und verbrannte Nerven.

    Auf dieses Anti-Muster kann jeder hereinfallen. Wenn Sie ein Anfänger sind, kennen Sie sich möglicherweise nicht mit Entwurfsmustern aus. Dies kann dazu führen, dass Sie versuchen, alle Probleme auf die eine Art und Weise zu lösen, die Sie beherrschen. Wenn wir von Profis sprechen, dann nennen wir das professionelle Deformation oder Nerdview. Sie haben Ihre eigenen bevorzugten Designmuster, und anstatt das richtige zu verwenden, verwenden Sie Ihr Lieblingsmuster, vorausgesetzt, dass eine gute Übereinstimmung in der Vergangenheit das gleiche Ergebnis in der Zukunft garantiert.

    Diese Falle kann zu sehr traurigen Ergebnissen führen – von einer schlechten, instabilen und schwer zu wartenden Implementierung bis hin zum völligen Scheitern des Projekts. So wie es keine Pille für alle Krankheiten gibt, gibt es auch kein Designmuster für alle Fälle.

9. Vorzeitige Optimierung

Vorzeitige Optimierung ist ein Anti-Pattern, dessen Name für sich spricht.
„Programmierer verbringen viel Zeit damit, über unkritische Stellen im Code nachzudenken und sich Gedanken darüber zu machen und zu versuchen, sie zu optimieren, was sich nur negativ auf das spätere Debuggen und den Support auswirkt. Wir sollten die Optimierung im Allgemeinen in beispielsweise 97 % der Fälle vergessen. Darüber hinaus.“ „Vorzeitige Optimierung ist die Wurzel allen Übels. Allerdings müssen wir den verbleibenden 3 % unsere volle Aufmerksamkeit widmen.“ – Donald Knuth
Beispielsweise das vorzeitige Hinzufügen von Indizes zu einer Datenbank. Warum ist das schlimm? Das Schlimme daran ist, dass Indizes als Binärbaum gespeichert werden. Dies führt dazu, dass der Baum jedes Mal neu berechnet wird, wenn ein neuer Wert hinzugefügt oder gelöscht wird, was Ressourcen und Zeit verbraucht. Daher sollten Indizes nur bei dringendem Bedarf (wenn Sie über große Datenmengen verfügen und Abfragen zu lange dauern) und nur für die wichtigsten Felder (die Felder, die am häufigsten abgefragt werden) hinzugefügt werden.

10. Spaghetti-Code

Spaghetti-Code ist ein Anti-Pattern, das durch schlecht strukturierten, verwirrenden und schwer verständlichen Code definiert wird und alle Arten von Verzweigungen enthält, z. B. das Umschließen von Ausnahmen, Bedingungen und Schleifen. Zuvor war der Goto-Operator der Hauptverbündete dieses Anti-Patterns. Goto-Anweisungen werden nicht mehr wirklich verwendet, wodurch eine Reihe damit verbundener Schwierigkeiten und Probleme erfreulicherweise beseitigt werden.

public boolean someDifficultMethod(List<String> XMLAttrList) {
           ...
   int prefix = stringPool.getPrefixForQName(elementType);
   int elementURI;
   try {
       if (prefix == -1) {
        ...
           if (elementURI != -1) {
               stringPool.setURIForQName(...);
           }
       } else {
        ...
           if (elementURI == -1) {
           ...
           }
       }
   } catch (Exception e) {
       return false;
   }
   if (attrIndex != -1) {
       int index = attrList.getFirstAttr(attrIndex);
       while (index != -1) {
           int attName = attrList.getAttrName(index);
           if (!stringPool.equalNames(...)){
           ...
               if (attPrefix != namespacesPrefix) {
                   if (attPrefix == -1) {
                    ...
                   } else {
                       if (uri == -1) {
                       ...
                       }
                       stringPool.setURIForQName(attName, uri);
                   ...
                   }
                   if (elementDepth >= 0) {
                   ...
                   }
                   elementDepth++;
                   if (elementDepth == fElementTypeStack.length) {
                   ...
                   }
               ...
                   return contentSpecType == fCHILDRENSymbol;
               }
           }
       }
   }
}
Es sieht schrecklich aus, nicht wahr? Leider ist dies das häufigste Anti-Pattern :( Selbst die Person, die solchen Code schreibt, wird ihn in Zukunft nicht mehr verstehen können. Andere Entwickler, die den Code sehen, werden denken: „Nun, wenn es funktioniert, dann okay – Es ist besser, es nicht anzufassen.“ Oft ist eine Methode zunächst einfach und sehr transparent, aber wenn neue Anforderungen hinzukommen, wird die Methode nach und nach mit immer mehr bedingten Anweisungen überhäuft, wodurch sie zu einer Monstrosität wie dieser wird. Wenn eine solche Methode angezeigt wird, müssen Sie es entweder vollständig oder zumindest in den verwirrendsten Teilen umgestalten. Normalerweise wird bei der Planung eines Projekts Zeit für das Umgestalten eingeplant, beispielsweise 30 % der Sprintzeit für Umgestaltung und Tests. Dies setzt natürlich voraus dass es keine Eile gibt (aber wann passiert das jemals).hier .

11. Magische Zahlen

Magic Numbers ist ein Anti-Pattern, bei dem alle Arten von Konstanten in einem Programm verwendet werden, ohne dass ihr Zweck oder ihre Bedeutung erklärt wird. Das heißt, sie sind im Allgemeinen schlecht benannt oder in extremen Fällen gibt es keinen Kommentar, der erklärt, was die Kommentare sind oder warum. Wie Spaghetti-Code ist dies eines der häufigsten Anti-Patterns. Jemand, der den Code nicht geschrieben hat, hat möglicherweise keine Ahnung von den magischen Zahlen oder ihrer Funktionsweise (und mit der Zeit wird der Autor selbst nicht mehr in der Lage sein, sie zu erklären). Das Ändern oder Entfernen einer Zahl führt dazu, dass der Code auf magische Weise nicht mehr funktioniert. Zum Beispiel 36 und 73. Um diesem Anti-Pattern entgegenzuwirken, empfehle ich eine Codeüberprüfung. Ihr Code muss von Entwicklern geprüft werden, die nicht an den relevanten Abschnitten des Codes beteiligt sind. Ihre Augen werden frisch sein und sie werden Fragen haben: Was ist das und warum haben Sie das getan? Und natürlich müssen Sie erklärende Namen verwenden oder Kommentare hinterlassen.

12. Programmierung durch Kopieren und Einfügen

Bei der Copy-and-Paste-Programmierung handelt es sich um ein Anti-Pattern, bei dem der Code einer anderen Person gedankenlos kopiert und eingefügt wird, was möglicherweise zu unerwarteten Nebenwirkungen führt. Zum Beispiel Kopier- und Einfügemethoden mit mathematischen Berechnungen oder komplexen Algorithmen, die wir nicht vollständig verstehen. In unserem speziellen Fall mag es funktionieren, aber unter anderen Umständen könnte es zu Problemen führen. Angenommen, ich benötige eine Methode, um die maximale Anzahl in einem Array zu bestimmen. Beim Stöbern im Internet bin ich auf diese Lösung gestoßen:

public static int max(int[] array) {
   int max = 0;
   for(int i = 0; i < array.length; i++) {
       if (Math.abs(array[i]) > max){
           max = array[i];
       }
   }
   return max;
}
Wir erhalten ein Array mit den Zahlen 3, 6, 1, 4 und 2, und die Methode gibt 6 zurück. Großartig, behalten wir es! Aber später erhalten wir ein Array bestehend aus 2,5, -7, 2 und 3, und dann ist unser Ergebnis -7. Und dieses Ergebnis ist nicht gut. Das Problem hierbei ist, dass Math.abs() den absoluten Wert zurückgibt. Unwissenheit darüber führt zur Katastrophe, aber nur in bestimmten Situationen. Ohne ein umfassendes Verständnis der Lösung können Sie viele Fälle nicht überprüfen. Kopierter Code kann auch über die interne Struktur der Anwendung hinausgehen, sowohl stilistisch als auch auf einer grundlegenderen, architektonischen Ebene. Ein solcher Code wird schwieriger zu lesen und zu warten sein. Und natürlich dürfen wir nicht vergessen, dass das direkte Kopieren des Codes einer anderen Person ein Plagiat der besonderen Art ist.

13. Das Rad neu erfinden

Das Rad neu zu erfinden ist ein Anti-Pattern, manchmal auch als Neuerfindung des quadratischen Rades bekannt. Im Wesentlichen ist diese Vorlage das Gegenteil des oben betrachteten Anti-Musters zum Kopieren und Einfügen. Bei diesem Anti-Pattern implementiert der Entwickler seine eigene Lösung für ein Problem, für das es bereits Lösungen gibt. Manchmal sind diese vorhandenen Lösungen besser als das, was der Programmierer erfindet. Meistens führt dies nur zu Zeitverlust und geringerer Produktivität: Der Programmierer findet möglicherweise überhaupt keine Lösung oder eine Lösung, die bei weitem nicht die beste ist. Allerdings können wir die Möglichkeit der Erstellung einer unabhängigen Lösung nicht ausschließen, da dies ein direkter Weg zur Copy-and-Paste-Programmierung ist. Der Programmierer sollte sich an den konkret anfallenden Programmieraufgaben orientieren, um diese kompetent zu lösen, sei es durch die Verwendung vorgefertigter Lösungen oder durch die Erstellung individueller Lösungen. Sehr oft, Der Grund für die Verwendung dieses Anti-Musters ist einfach Eile. Das Ergebnis ist eine oberflächliche Analyse (Suche nach) vorgefertigten Lösungen. Die Neuerfindung des quadratischen Rades ist ein Fall, in dem das betrachtete Anti-Muster ein negatives Ergebnis hat. Das heißt, das Projekt erfordert eine benutzerdefinierte Lösung, und der Entwickler erstellt sie, aber schlecht. Gleichzeitig gibt es bereits eine gute Option und andere nutzen sie erfolgreich. Fazit: Es geht viel Zeit verloren. Zuerst erschaffen wir etwas, das nicht funktioniert. Dann versuchen wir, es umzugestalten, und schließlich ersetzen wir es durch etwas, das bereits existiert. Ein Beispiel ist die Implementierung Ihres eigenen benutzerdefinierten Caches, wenn bereits zahlreiche Implementierungen vorhanden sind. Egal wie talentiert Sie als Programmierer sind, Sie sollten bedenken, dass die Neuerfindung eines Vierkantrads zumindest Zeitverschwendung ist. Und wie Sie wissen, ist Zeit die wertvollste Ressource.

14. Jo-Jo-Problem

Das Jo-Jo-Problem ist ein Anti-Pattern, bei dem die Struktur der Anwendung aufgrund übermäßiger Fragmentierung (z. B. einer übermäßig unterteilten Vererbungskette) übermäßig kompliziert ist. Das „Jo-Jo-Problem“ entsteht, wenn Sie ein Programm verstehen müssen, dessen Vererbungshierarchie lang und komplex ist und tief verschachtelte Methodenaufrufe erzeugt. Daher müssen Programmierer zwischen vielen verschiedenen Klassen und Methoden navigieren, um das Verhalten des Programms zu überprüfen. Der Name dieses Anti-Musters leitet sich vom Namen des Spielzeugs ab. Schauen wir uns als Beispiel die folgende Vererbungskette an: Wir haben eine Technologieschnittstelle:

public interface Technology {
   void turnOn();
}
Die Transportschnittstelle erbt es:

public interface Transport extends Technology {
   boolean fillUp();
}
Und dann haben wir noch eine weitere Schnittstelle, GroundTransport:

public interface GroundTransportation extends Transport {
   void startMove();
   void brake();
}
Und daraus leiten wir eine abstrakte Car-Klasse ab:

public abstract class Car implements GroundTransportation {
   @Override
   public boolean fillUp() {
       /* some implementation */
       return true;
   }
   @Override
   public void turnOn() {
       /* some implementation */
   }
   public boolean openTheDoor() {
       /* some implementation */
       return true;
   }
   public abstract void fixCar();
}
Als nächstes kommt die abstrakte Volkswagen-Klasse:

public abstract class Volkswagen extends Car {
   @Override
   public void startMove() {
       /* some implementation */
   }
   @Override
   public void brake() {
       /* some implementation */
   }
}
Und zum Schluss noch ein konkretes Modell:

public class VolkswagenAmarok extends Volkswagen {
   @Override
   public void fixCar(){
       /* some implementation */
   }
}
Diese Kette zwingt uns dazu, nach Antworten auf Fragen zu suchen wie:
  1. Wie viele Methoden gibt VolkswagenAmarokes?

  2. Welcher Typ sollte anstelle des Fragezeichens eingefügt werden, um eine maximale Abstraktion zu erreichen:

    
    ? someObj = new VolkswagenAmarok();
           someObj.brake();
    
Es ist schwierig, solche Fragen schnell zu beantworten – wir müssen einen Blick darauf werfen und Nachforschungen anstellen, und es ist leicht, verwirrt zu werden. Und was wäre, wenn die Hierarchie viel größer, länger und komplizierter wäre, mit allerlei Überladungen und Überschreibungen? Die Struktur, die wir hätten, würde durch übermäßige Fragmentierung verdeckt werden. Die beste Lösung wäre, die unnötigen Unterteilungen zu reduzieren. In unserem Fall würden wir Technik → Auto → VolkswagenAmarok verlassen.

15. Zufällige Komplexität

Unnötige Komplexität ist ein Anti-Muster, bei dem unnötige Komplikationen in eine Lösung eingeführt werden.
„Jeder Narr kann Code schreiben, den ein Computer verstehen kann. Gute Programmierer schreiben Code, den Menschen verstehen können.“ – Martin Fowler
Was ist also Komplexität? Er kann als der Schwierigkeitsgrad definiert werden, mit dem jede Operation im Programm ausgeführt wird. Grundsätzlich lässt sich Komplexität in zwei Typen einteilen. Die erste Art von Komplexität ist die Anzahl der Funktionen, die ein System hat. Es kann nur auf eine Weise reduziert werden – durch das Entfernen einiger Funktionen. Die bestehenden Methoden müssen überwacht werden. Eine Methode sollte entfernt werden, wenn sie nicht mehr verwendet wird oder noch verwendet wird, aber keinen Nutzen bringt. Darüber hinaus müssen Sie beurteilen, wie alle Methoden in der Anwendung verwendet werden, um zu verstehen, wo sich Investitionen lohnen würden (viel Wiederverwendung von Code) und wozu Sie Nein sagen können. Die zweite Art von Komplexität ist unnötige Komplexität. Es kann nur durch einen professionellen Ansatz geheilt werden. Anstatt etwas „Cooles“ zu tun (Junge Entwickler sind nicht die einzigen, die für diese Krankheit anfällig sind.) Sie müssen darüber nachdenken, wie Sie es so einfach wie möglich machen können, denn die beste Lösung ist immer einfach. Angenommen, wir haben kleine zusammengehörige Tabellen mit Beschreibungen einiger Entitäten, beispielsweise eines Benutzers: Was sind Anti-Patterns?  Schauen wir uns einige Beispiele an (Teil 2) – 3Wir haben also die ID des Benutzers, die ID der Sprache, in der die Beschreibung erstellt wurde, und die Beschreibung selbst. Ebenso verfügen wir über Hilfsdeskriptoren für die Tabellen „Autos“, „Dateien“, „Pläne“ und „Kunden“. Wie würde es dann aussehen, neue Werte in solche Tabellen einzufügen?

public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description)throws Exception {
   switch (type){
       case CAR:
           jdbcTemplate.update(CREATE_RELATION_WITH_CAR, languageId, serviceId, description);
       case USER:
           jdbcTemplate.update(CREATE_RELATION_WITH_USER, languageId, serviceId, description);
       case FILE:
           jdbcTemplate.update(CREATE_RELATION_WITH_FILE, languageId, serviceId, description);
       case PLAN:
           jdbcTemplate.update(CREATE_RELATION_WITH_PLAN, languageId, serviceId, description);
       case CUSTOMER:
           jdbcTemplate.update(CREATE_RELATION_WITH_CUSTOMER, languageId, serviceId, description);
       default:
           throw new Exception();
   }
}
Und dementsprechend haben wir diese Aufzählung:

public enum ServiceType {
   CAR(),
   USER(),
   FILE(),
   PLAN(),
   CUSTOMER()
}
Alles scheint einfach und gut zu sein... Aber was ist mit den anderen Methoden? Tatsächlich werden sie alle auch eine Reihe von switchAnweisungen und eine Reihe nahezu identischer Datenbankabfragen enthalten, was wiederum unsere Klasse erheblich verkomplizieren und aufblähen wird. Wie könnte das alles einfacher gemacht werden? Lassen Sie uns unsere Enumeration ein wenig aktualisieren:

@Getter
@AllArgsConstructor
public enum ServiceType {
   CAR("cars_descriptions", "car_id"),
   USER("users_descriptions", "user_id"),
   FILE("files_descriptions", "file_id"),
   PLAN("plans_descriptions", "plan_id"),
   CUSTOMER("customers_descriptions", "customer_id");
   private String tableName;
   private String columnName;
}
Jetzt hat jeder Typ die Namen der ursprünglichen Felder seiner Tabelle. Als Ergebnis sieht die Methode zum Erstellen einer Beschreibung wie folgt aus:

private static final String CREATE_RELATION_WITH_SERVICE = "INSERT INTO %s(language_id, %s, description) VALUES (?, ?, ?)";
public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description) {
   jdbcTemplate.update(String.format(CREATE_RELATION_WITH_SERVICE, type.getTableName(), type.getColumnName()), languageId, serviceId, description);
   }
Praktisch, einfach und kompakt, finden Sie nicht? Ein guter Entwickler erkennt man nicht einmal daran, wie oft er oder sie Muster verwendet, sondern eher daran, wie oft er oder sie Anti-Muster vermeidet. Unwissenheit ist der schlimmste Feind, denn Sie müssen Ihre Feinde vom Sehen her kennen. Nun, das ist alles, was ich für heute habe. Vielen Dank an alle! :) :)
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION