CodeGym /Java-blogg /Tilfeldig /Hva er anti-mønstre? La oss se på noen eksempler (del 2)
John Squirrels
Nivå
San Francisco

Hva er anti-mønstre? La oss se på noen eksempler (del 2)

Publisert i gruppen
Hva er anti-mønstre? La oss se på noen eksempler (del 1) I dag fortsetter vi vår gjennomgang av de mest populære anti-mønstrene. Hvis du gikk glipp av den første delen, her er den. Hva er anti-mønstre?  La oss se på noen eksempler (del 2) - 1Så designmønstre er beste praksis. De er med andre ord eksempler på gode, utprøvde måter å løse konkrete problemer på. I sin tur er anti-mønstre deres stikk motsatte, i den forstand at de er mønstre av fallgruver eller feil når man løser ulike problemer (onde mønstre). La oss gå videre til neste programvareutviklingsantimønster.

8. Gullhammer

En gullhammer er et anti-mønster definert av tillit til at en bestemt løsning er universelt anvendelig. Eksempler:
  1. Etter å ha støtt på et problem og funnet et mønster for den perfekte løsningen, prøver en programmerer å feste dette mønsteret overalt, bruke det på nåværende og alle fremtidige prosjekter, i stedet for å lete etter de riktige løsningene for spesifikke tilfeller.

  2. Noen utviklere laget en gang sin egen variant av en cache for en spesifikk situasjon (fordi ingenting annet var passende). Senere, på det neste prosjektet som ikke innebar noen spesiell cache-logikk, brukte de varianten deres igjen i stedet for å bruke ferdige biblioteker (for eksempel Ehcache). Resultatet var en haug med feil og inkompatibiliteter, samt mye bortkastet tid og stekte nerver.

    Hvem som helst kan falle for dette antimønsteret. Hvis du er nybegynner, kan det hende du ikke har kunnskap om designmønstre. Dette kan føre til at du prøver å løse alle problemer på den ene måten du mestrer. Hvis vi snakker om profesjonelle, så kaller vi dette profesjonell deformasjon eller nerdview. Du har dine egne foretrukne designmønstre, og i stedet for å bruke det rette, bruker du din favoritt, forutsatt at en god passform i fortiden garanterer samme resultat i fremtiden.

    Denne fallgruven kan gi svært triste resultater - fra en dårlig, ustabil og vanskelig å opprettholde implementering til en fullstendig fiasko i prosjektet. Akkurat som det ikke finnes én pille for alle sykdommer, finnes det ikke ett designmønster for alle anledninger.

9. For tidlig optimalisering

For tidlig optimalisering er et antimønster hvis navn taler for seg selv.
"Programmører bruker enormt mye tid på å tenke og bekymre seg for ikke-kritiske steder i koden og prøve å optimalisere dem, noe som bare påvirker påfølgende feilsøking og støtte negativt. Vi bør generelt glemme optimalisering i for eksempel 97 % av tilfellene. Dessuten , for tidlig optimalisering er roten til alt ondt. Når det er sagt, må vi ta hensyn til de resterende 3 %." — Donald Knuth
For eksempel å legge til indekser for tidlig i en database. Hvorfor er det ille? Vel, det er dårlig ved at indekser lagres som et binært tre. Som et resultat, hver gang en ny verdi legges til og slettes, vil treet bli beregnet på nytt, og dette bruker ressurser og tid. Derfor bør indekser bare legges til når det er et presserende behov (hvis du har en stor mengde data og spørringer tar for lang tid) og kun for de viktigste feltene (feltene som er hyppigst spurt).

10. Spaghettikode

Spaghettikode er et antimønster definert av kode som er dårlig strukturert, forvirrende og vanskelig å forstå, og inneholder alle slags forgreninger, for eksempel innpakningsunntak, betingelser og løkker. Tidligere var goto-operatøren dette anti-mønsterets viktigste allierte. Goto-utsagn brukes egentlig ikke lenger, noe som heldigvis eliminerer en rekke tilhørende vanskeligheter 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 forferdelig ut, gjør det ikke? Dessverre er dette det vanligste antimønsteret :( Selv personen som skriver slik kode vil ikke kunne forstå det i fremtiden. Andre utviklere som ser koden vil tenke: "Vel, hvis det fungerer, så ok — det er bedre å ikke røre det." Ofte er en metode i utgangspunktet enkel og veldig gjennomsiktig, men etter hvert som nye krav kommer til, blir metoden gradvis belemret med flere og flere betingede utsagn, noe som gjør den til en monstrositet som dette. Hvis en slik metode vises, må du refaktorere den enten fullstendig eller i det minste de mest forvirrende delene. Vanligvis, når du planlegger et prosjekt, tildeles det tid til refaktorisering, for eksempel er 30 % av sprinttiden til refaktorering og tester. Dette forutsetter selvfølgelig at det ikke er noe hastverk (men når skjer det noen gang).her .

11. Magiske tall

Magiske tall er et antimønster der alle slags konstanter brukes i et program uten noen forklaring på deres formål eller betydning. Det vil si at de generelt er dårlig navngitt eller i ekstreme tilfeller er det ingen kommentar som forklarer hva kommentarene er eller hvorfor. Som spaghettikode er dette et av de vanligste antimønstrene. Noen som ikke har skrevet koden kan ha eller ikke ha en anelse om de magiske tallene eller hvordan de fungerer (og med tiden vil ikke forfatteren selv være i stand til å forklare dem). Som et resultat vil endring eller fjerning av et tall føre til at koden på magisk vis slutter å fungere sammen. For eksempel 36 og 73. For å bekjempe dette antimønsteret anbefaler jeg en kodegjennomgang. Koden din må ses på av utviklere som ikke er involvert i de relevante delene av koden. Øynene deres vil være friske og de vil ha spørsmål: hva er dette og hvorfor gjorde du det? Og selvfølgelig må du bruke forklarende navn eller legge igjen kommentarer.

12. Kopier og lim inn programmering

Kopier-og-lim-programmering er et anti-mønster der andres kode blir tankeløst kopiert og limt inn, noe som muligens resulterer i uventede bivirkninger. For eksempel kopiering og limingsmetoder med matematiske beregninger eller komplekse algoritmer som vi ikke helt forstår. Det kan fungere for vårt spesielle tilfelle, men i andre tilfeller kan det føre til problemer. Anta at jeg trenger en metode for å bestemme det maksimale antallet i en matrise. Når jeg rotet rundt på Internett fant jeg denne løsningen:

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 matrise med tallene 3, 6, 1, 4 og 2, og metoden returnerer 6. Flott, la oss beholde det! Men senere får vi en matrise bestående av 2,5, -7, 2 og 3, og da er resultatet -7. Og dette resultatet er ikke bra. Problemet her er at Math.abs() returnerer den absolutte verdien. Uvitenhet om dette fører til katastrofe, men bare i visse situasjoner. Uten en grundig forståelse av løsningen er det mange tilfeller du ikke vil kunne bekrefte. Kopiert kode kan også gå utover applikasjonens interne struktur, både stilistisk og på et mer grunnleggende, arkitektonisk nivå. Slik kode vil være vanskeligere å lese og vedlikeholde. Og selvfølgelig må vi ikke glemme at rett opp kopiering av andres kode er en spesiell form for plagiering.

13. Å finne opp hjulet på nytt

Å finne opp hjulet på nytt er et antimønster, også noen ganger kjent som å finne opp det firkantede hjulet på nytt. I hovedsak er denne malen det motsatte av kopier-og-lim-anti-mønsteret vurdert ovenfor. I dette antimønsteret implementerer utvikleren sin egen løsning for et problem som det allerede finnes løsninger for. Noen ganger er disse eksisterende løsningene bedre enn det programmereren finner på. Oftest fører dette bare til tapt tid og lavere produktivitet: programmereren finner kanskje ikke en løsning i det hele tatt eller kan finne en løsning som er langt fra den beste. Når det er sagt, kan vi ikke utelukke muligheten for å lage en uavhengig løsning, fordi å gjøre det er en direkte vei til kopier-og-lim-programmering. Programmereren bør veiledes av de spesifikke programmeringsoppgavene som oppstår for å løse dem kompetent, enten ved å bruke ferdige løsninger eller ved å lage tilpassede løsninger. Veldig ofte, grunnen til å bruke dette anti-mønsteret er rett og slett hastverk. Resultatet er en grunn analyse av (søk etter) ferdige løsninger. Å finne opp det firkantede hjulet på nytt er et tilfelle der antimønsteret som vurderes har et negativt resultat. Det vil si at prosjektet krever en tilpasset løsning, og utvikleren lager den, men dårlig. Samtidig eksisterer det allerede et godt alternativ, og andre bruker det med hell. Bunnlinjen: en enorm mengde tid går tapt. Først lager vi noe som ikke fungerer. Så prøver vi å refaktorisere det, og til slutt erstatter vi det med noe som allerede fantes. Et eksempel er å implementere din egen tilpassede cache når mange implementeringer allerede eksisterer. Uansett hvor talentfull du er som programmerer, bør du huske at det å finne opp et firkantet hjul på nytt er i det minste bortkastet tid. Og som du vet, er tid den mest verdifulle ressursen.

14. Jojo-problem

Jojo -problemet er et anti-mønster der applikasjonens struktur er altfor komplisert på grunn av overdreven fragmentering (for eksempel en overdreven delt arvekjede). "Jojo-problemet" oppstår når du trenger å forstå et program hvis arvehierarki er langt og komplekst, og skaper dypt nestede metodekall. Som et resultat må programmerere navigere mellom mange forskjellige klasser og metoder for å inspisere oppførselen til programmet. Navnet på dette anti-mønsteret kommer fra navnet på leken. La oss som et eksempel se på følgende arvekjede: Vi har et teknologigrensesnitt:

public interface Technology {
   void turnOn();
}
Transport-grensesnittet arver det:

public interface Transport extends Technology {
   boolean fillUp();
}
Og så har vi et annet grensesnitt, GroundTransport:

public interface GroundTransportation extends Transport {
   void startMove();
   void brake();
}
Og derfra henter 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();
}
Neste er den abstrakte Volkswagen-klassen:

public abstract class Volkswagen extends Car {
   @Override
   public void startMove() {
       /* some implementation */
   }
   @Override
   public void brake() {
       /* some implementation */
   }
}
Og til slutt, en spesifikk modell:

public class VolkswagenAmarok extends Volkswagen {
   @Override
   public void fixCar(){
       /* some implementation */
   }
}
Denne kjeden tvinger oss til å lete etter svar på spørsmål som:
  1. Hvor mange metoder VolkswagenAmarokhar?

  2. Hvilken type bør settes inn i stedet for spørsmålstegnet for å oppnå maksimal abstraksjon:

    
    ? someObj = new VolkswagenAmarok();
           someObj.brake();
    
Det er vanskelig å raskt svare på slike spørsmål – det krever at vi tar en titt og undersøker, og det er lett å bli forvirret. Og hva om hierarkiet er mye større, lengre og mer komplisert, med alle slags overbelastninger og overstyringer? Strukturen vi ville ha ville bli skjult på grunn av overdreven fragmentering. Den beste løsningen vil være å redusere de unødvendige splittelsene. I vårt tilfelle ville vi forlate Teknologi → Bil → VolkswagenAmarok.

15. Tilfeldig kompleksitet

Unødvendig kompleksitet er et anti-mønster der unødvendige komplikasjoner introduseres til en løsning.
"Enhver idiot kan skrive kode som en datamaskin kan forstå. Gode programmerere skriver kode som mennesker kan forstå." – Martin Fowler
Så hva er kompleksitet? Det kan defineres som vanskelighetsgraden som hver operasjon utføres med i programmet. Som regel kan kompleksitet deles inn i to typer. Den første typen kompleksitet er antallet funksjoner som et system har. Den kan reduseres på bare én måte - ved å fjerne en funksjon. De eksisterende metodene må overvåkes. En metode bør fjernes hvis den ikke lenger brukes eller fortsatt brukes, men uten å gi noen verdi. Dessuten må du vurdere hvordan alle metodene i applikasjonen brukes, for å forstå hvor investeringer vil lønne seg (mye kodegjenbruk) og hva du kan si nei til. Den andre typen kompleksitet er unødvendig kompleksitet. Det kan bare kureres gjennom en profesjonell tilnærming. I stedet for å gjøre noe "kult" (unge utviklere er ikke de eneste som er mottakelige for denne sykdommen), du må tenke på hvordan du gjør det så enkelt som mulig, fordi den beste løsningen alltid er enkel. Anta for eksempel at vi har små relaterte tabeller med beskrivelser av noen enheter, for eksempel en bruker: Hva er anti-mønstre?  La oss se på noen eksempler (del 2) - 3Så vi har ID-en til brukeren, ID-en til språket som beskrivelsen er laget på, og selve beskrivelsen. På samme måte har vi hjelpebeskrivelser for biler, filer, planer og kundetabeller. Hvordan ville det så se ut å sette inn nye verdier i slike 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 følgelig har vi denne enumen:

public enum ServiceType {
   CAR(),
   USER(),
   FILE(),
   PLAN(),
   CUSTOMER()
}
Alt ser ut til å være enkelt og bra... Men hva med de andre metodene? Faktisk vil de også alle ha en haug med switchutsagn og en haug med nesten identiske databasespørringer, som igjen vil komplisere og blåse opp klassen vår. Hvordan kunne alt dette gjøres enklere? La oss oppgradere opptellingen vår litt:

@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;
}
Nå har hver type navnene på tabellens opprinnelige felt. Som et resultat blir metoden for å lage 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? Indikasjonen på en god utvikler er ikke engang hvor ofte han eller hun bruker mønstre, men heller hvor ofte han eller hun unngår anti-mønstre. Uvitenhet er den verste fienden, fordi du trenger å kjenne fiendene dine ved synet. Vel, det er alt jeg har for i dag. Takk alle sammen! :)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION