CodeGym /Java blog /Tilfældig /Hvad er anti-mønstre? Lad os se på nogle eksempler (del 2...
John Squirrels
Niveau
San Francisco

Hvad er anti-mønstre? Lad os se på nogle eksempler (del 2)

Udgivet i gruppen
Hvad er anti-mønstre? Lad os se på nogle eksempler (del 1) I dag fortsætter vi vores gennemgang af de mest populære anti-mønstre. Hvis du gik glip af første del, så er den her . Hvad er anti-mønstre?  Lad os se på nogle eksempler (del 2) - 1Så designmønstre er bedste praksis. De er med andre ord eksempler på gode, gennemprøvede måder at løse konkrete problemer på. Til gengæld er anti-mønstre deres nøjagtige modsætning, i den forstand, at de er mønstre af faldgruber eller fejl, når man løser forskellige problemer (onde mønstre). Lad os gå videre til det næste anti-mønster for softwareudvikling.

8. Guldhammer

En gylden hammer er et anti-mønster defineret af tillid til, at en bestemt løsning er universelt anvendelig. Eksempler:
  1. Efter at have stødt på et problem og fundet et mønster for den perfekte løsning, forsøger en programmør at fastholde dette mønster overalt og anvende det på nuværende og alle fremtidige projekter i stedet for at lede efter de passende løsninger til specifikke tilfælde.

  2. Nogle udviklere skabte engang deres egen variant af en cache til en specifik situation (fordi intet andet var passende). Senere, på det næste projekt, som ikke involverede nogen speciel cache-logik, brugte de deres variant igen i stedet for at bruge færdige biblioteker (for eksempel Ehcache). Resultatet var en masse fejl og uforeneligheder, samt en masse spildtid og stegte nerver.

    Enhver kan falde for dette anti-mønster. Hvis du er nybegynder, er du muligvis ikke vidende om designmønstre. Dette kan få dig til at prøve at løse alle problemer på den ene måde, du har mestret. Hvis vi taler om professionelle, så kalder vi dette professionel deformation eller nørdsyn. Du har dine egne foretrukne designmønstre, og i stedet for at bruge det rigtige, bruger du din favorit, forudsat at en god pasform i fortiden garanterer samme resultat i fremtiden.

    Denne faldgrube kan give meget triste resultater - fra en dårlig, ustabil og vanskelig at vedligeholde implementering til en fuldstændig fiasko i projektet. Ligesom der ikke findes én pille til alle sygdomme, er der ikke et designmønster til alle lejligheder.

9. For tidlig optimering

For tidlig optimering er et anti-mønster, hvis navn taler for sig selv.
"Programmører bruger enormt meget tid på at tænke og bekymre sig om ikke-kritiske steder i koden og forsøge at optimere dem, hvilket kun påvirker efterfølgende fejlretning og support negativt. Vi bør generelt glemme alt om optimering i f.eks. 97 % af tilfældene. Desuden , for tidlig optimering er roden til alt ondt. Når det er sagt, skal vi være opmærksomme på de resterende 3 %." — Donald Knuth
For eksempel for tidlig tilføjelse af indekser til en database. Hvorfor er det slemt? Nå, det er dårligt, at indekser gemmes som et binært træ. Som et resultat heraf vil træet blive genberegnet, hver gang en ny værdi tilføjes og slettes, og det bruger ressourcer og tid. Derfor bør indekser kun tilføjes, når der er et presserende behov (hvis du har en stor mængde data, og forespørgsler tager for lang tid) og kun for de vigtigste felter (de felter, der oftest spørges til).

10. Spaghettikode

Spaghetti-kode er et anti-mønster defineret af kode, der er dårligt struktureret, forvirrende og svært at forstå, og som indeholder alle former for forgrening, såsom indpakning af undtagelser, betingelser og sløjfer. Tidligere var goto-operatøren dette anti-mønsters vigtigste allierede. Goto-udsagn bruges egentlig ikke længere, hvilket heldigvis eliminerer en række tilhørende vanskeligheder og problemer.

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;
               }
           }
       }
   }
}
Det ser forfærdeligt ud, gør det ikke? Desværre er dette det mest almindelige anti-mønster :( Selv den person, der skriver en sådan kode, vil ikke være i stand til at forstå det i fremtiden. Andre udviklere, der ser koden, vil tænke, "Nå, hvis det virker, så okay — det er bedre ikke at røre ved det". Ofte er en metode i starten enkel og meget gennemsigtig, men efterhånden som nye krav kommer til, besadles metoden gradvist med flere og flere betingede udsagn, hvilket gør den til en monstrøsitet som denne. Hvis en sådan metode vises, skal du omfaktorere det enten fuldstændigt eller i det mindste de mest forvirrende dele. Typisk, når du planlægger et projekt, afsættes der tid til refactoring, f.eks. er 30% af sprinttiden til refactoring og tests. Dette forudsætter selvfølgelig at der ikke er noget hastværk (men hvornår sker det nogensinde).her .

11. Magiske tal

Magiske tal er et anti-mønster, hvor alle slags konstanter bruges i et program uden nogen forklaring på deres formål eller betydning. Det vil sige, at de generelt er dårligt navngivet, eller i ekstreme tilfælde er der ingen kommentar, der forklarer, hvad kommentarerne er eller hvorfor. Ligesom spaghettikode er dette et af de mest almindelige anti-mønstre. En person, der ikke har skrevet koden, har muligvis en anelse om de magiske tal, eller hvordan de virker (og med tiden vil forfatteren ikke selv være i stand til at forklare dem). Som følge heraf vil ændring eller fjernelse af et nummer få koden til på magisk vis at holde op med at fungere sammen. For eksempel 36 og 73. For at bekæmpe dette anti-mønster anbefaler jeg en kodegennemgang. Din kode skal ses på af udviklere, der ikke er involveret i de relevante afsnit af koden. Deres øjne vil være friske, og de vil have spørgsmål: hvad er det her, og hvorfor gjorde du det? Og selvfølgelig skal du bruge forklarende navne eller skrive kommentarer.

12. Kopier-og-indsæt programmering

Copy-and-paste-programmering er et anti-mønster, hvor en andens kode tankeløst kopieres og indsættes, hvilket muligvis resulterer i uventede bivirkninger. For eksempel kopiering og indsættelsesmetoder med matematiske beregninger eller komplekse algoritmer, som vi ikke helt forstår. Det kan virke for vores særlige sag, men under nogle andre omstændigheder kan det føre til problemer. Antag, at jeg har brug for en metode til at bestemme det maksimale antal i et array. Jeg rodede rundt på internettet og fandt denne løsning:

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;
}
Vi får en matrix med tallene 3, 6, 1, 4 og 2, og metoden returnerer 6. Godt, lad os beholde det! Men senere får vi et array bestående af 2,5, -7, 2 og 3, og så er vores resultat -7. Og dette resultat er ikke godt. Problemet her er, at Math.abs() returnerer den absolutte værdi. Uvidenhed om dette fører til katastrofe, men kun i visse situationer. Uden en dybdegående forståelse af løsningen er der mange tilfælde, du ikke vil være i stand til at verificere. Kopieret kode kan også gå ud over applikationens interne struktur, både stilistisk og på et mere grundlæggende, arkitektonisk niveau. En sådan kode vil være sværere at læse og vedligeholde. Og selvfølgelig må vi ikke glemme, at det at kopiere en andens kode er en særlig form for plagiat.

13. Genopfinde hjulet

At genopfinde hjulet er et anti-mønster, også nogle gange kendt som at genopfinde det firkantede hjul. I det væsentlige er denne skabelon det modsatte af kopi-og-indsæt-anti-mønsteret, der er betragtet ovenfor. I dette anti-mønster implementerer udvikleren sin egen løsning på et problem, som der allerede findes løsninger på. Nogle gange er disse eksisterende løsninger bedre end hvad programmøren opfinder. Oftest fører dette kun til tabt tid og lavere produktivitet: programmøren finder måske slet ikke en løsning eller kan finde en løsning, der er langt fra den bedste. Når det er sagt, kan vi ikke udelukke muligheden for at skabe en uafhængig løsning, fordi det er en direkte vej til copy-and-paste-programmering. Programmøren bør vejledes af de konkrete programmeringsopgaver, der opstår for at løse dem kompetent, hvad enten det er ved at bruge færdige løsninger eller ved at skabe skræddersyede løsninger. Meget ofte, grunden til at bruge dette anti-mønster er simpelthen hastværk. Resultatet er en overfladisk analyse af (søg efter) færdige løsninger. Genopfindelse af det firkantede hjul er et tilfælde, hvor det anti-mønster, der overvejes, har et negativt resultat. Det vil sige, at projektet kræver en tilpasset løsning, og udvikleren skaber den, men dårligt. Samtidig eksisterer der allerede en god mulighed, og andre bruger den med succes. Nederste linje: en enorm mængde tid går tabt. Først skaber vi noget, der ikke virker. Så forsøger vi at omstrukturere det, og til sidst erstatter vi det med noget, der allerede eksisterede. Et eksempel er implementering af din egen tilpassede cache, når der allerede findes masser af implementeringer. Uanset hvor talentfuld du er som programmør, skal du huske, at genopfinde et firkantet hjul i det mindste er spild af tid. Og som du ved, er tid den mest værdifulde ressource.

14. Yo-yo problem

Jojo -problemet er et anti-mønster, hvor applikationens struktur er alt for kompliceret på grund af overdreven fragmentering (f.eks. en alt for underopdelt arvekæde). "Jojo-problemet" opstår, når du skal forstå et program, hvis arvehierarki er langt og komplekst, hvilket skaber dybt indlejrede metodekald. Som følge heraf skal programmører navigere mellem mange forskellige klasser og metoder for at inspicere programmets opførsel. Navnet på dette anti-mønster kommer fra navnet på legetøjet. Lad os som et eksempel se på følgende arvekæde: Vi har en teknologigrænseflade:

public interface Technology {
   void turnOn();
}
Transportgrænsefladen arver det:

public interface Transport extends Technology {
   boolean fillUp();
}
Og så har vi en anden grænseflade, GroundTransport:

public interface GroundTransportation extends Transport {
   void startMove();
   void brake();
}
Og derfra udleder vi en abstrakt bilklasse:

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();
}
Dernæst er den abstrakte Volkswagen-klasse:

public abstract class Volkswagen extends Car {
   @Override
   public void startMove() {
       /* some implementation */
   }
   @Override
   public void brake() {
       /* some implementation */
   }
}
Og endelig en specifik model:

public class VolkswagenAmarok extends Volkswagen {
   @Override
   public void fixCar(){
       /* some implementation */
   }
}
Denne kæde tvinger os til at lede efter svar på spørgsmål som:
  1. Hvor mange metoder VolkswagenAmarokhar man?

  2. Hvilken type skal indsættes i stedet for spørgsmålstegnet for at opnå maksimal abstraktion:

    
    ? someObj = new VolkswagenAmarok();
           someObj.brake();
    
Det er svært hurtigt at svare på sådanne spørgsmål - det kræver, at vi kigger og undersøger, og det er nemt at blive forvirret. Og hvad nu hvis hierarkiet er meget større, længere og mere kompliceret, med alle mulige former for overbelastninger og tilsidesættelser? Den struktur, vi ville have, ville blive sløret på grund af overdreven fragmentering. Den bedste løsning ville være at reducere de unødvendige opdelinger. I vores tilfælde ville vi forlade Teknologi → Bil → VolkswagenAmarok.

15. Utilsigtet kompleksitet

Unødvendig kompleksitet er et anti-mønster, hvor unødvendige komplikationer introduceres til en løsning.
"Enhver idiot kan skrive kode, som en computer kan forstå. Gode programmører skriver kode, som mennesker kan forstå." - Martin Fowler
Så hvad er kompleksitet? Det kan defineres som den sværhedsgrad, hvormed hver operation udføres i programmet. Som regel kan kompleksitet opdeles i to typer. Den første form for kompleksitet er antallet af funktioner, som et system har. Det kan kun reduceres på én måde - ved at fjerne en funktion. De eksisterende metoder skal overvåges. En metode bør fjernes, hvis den ikke længere bruges eller stadig bruges, men uden at give nogen værdi. Desuden skal du vurdere, hvordan alle metoderne i applikationen bruges, for at forstå, hvor investeringer kan betale sig (meget kodegenbrug), og hvad du kan sige nej til. Den anden type kompleksitet er unødvendig kompleksitet. Det kan kun helbredes gennem en professionel tilgang. I stedet for at gøre noget "fedt" (unge udviklere er ikke de eneste, der er modtagelige for denne sygdom), du skal tænke på, hvordan du gør det så enkelt som muligt, fordi den bedste løsning altid er enkel. Antag for eksempel, at vi har små relaterede tabeller med beskrivelser af nogle entiteter, såsom en bruger: Hvad er anti-mønstre?  Lad os se på nogle eksempler (del 2) - 3Så vi har brugerens id, id'et for det sprog, beskrivelsen er lavet på, og selve beskrivelsen. På samme måde har vi hjælpebeskrivelser til biler, filer, planer og kundetabeller. Hvordan ville det så se ud at indsætte nye værdier i sådanne tabeller?

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();
   }
}
Og derfor har vi denne enum:

public enum ServiceType {
   CAR(),
   USER(),
   FILE(),
   PLAN(),
   CUSTOMER()
}
Alt ser ud til at være enkelt og godt... Men hvad med de andre metoder? Faktisk vil de også alle have en masse switchudsagn og en masse næsten identiske databaseforespørgsler, som igen vil meget komplicere og blæse vores klasse op. Hvordan kunne alt dette gøres lettere? Lad os opgradere vores enum lidt:

@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;
}
Nu har hver type navnene på dens tabels originale felter. Som et resultat bliver metoden til at oprette en beskrivelse:

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);
   }
Praktisk, enkel og kompakt, synes du ikke? Indikationen af ​​en god udvikler er ikke engang, hvor ofte han eller hun bruger mønstre, men derimod hvor ofte han eller hun undgår anti-mønstre. Uvidenhed er den værste fjende, fordi du skal kende dine fjender af synet. Nå, det er alt, jeg har for i dag. Tak allesammen! :)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION