CodeGym /Java blogg /Slumpmässig /Vad är antimönster? Låt oss titta på några exempel (del 2...
John Squirrels
Nivå
San Francisco

Vad är antimönster? Låt oss titta på några exempel (del 2)

Publicerad i gruppen
Vad är antimönster? Låt oss titta på några exempel (del 1) Idag fortsätter vi vår recension av de mest populära anti-mönstren. Om du missade den första delen, här är den. Vad är antimönster?  Låt oss titta på några exempel (del 2) - 1Så designmönster är bästa praxis. De är med andra ord exempel på bra, beprövade sätt att lösa specifika problem. I sin tur är antimönster deras raka motsats, i den meningen att de är mönster av fallgropar eller misstag när man löser olika problem (onda mönster). Låt oss gå vidare till nästa antimönster för mjukvaruutveckling.

8. Guldhammare

En guldhammare är ett antimönster som definieras av förtroende för att en viss lösning är universellt tillämplig. Exempel:
  1. Efter att ha stött på ett problem och hittat ett mönster för den perfekta lösningen, försöker en programmerare att fästa detta mönster överallt och applicera det på nuvarande och alla framtida projekt, istället för att leta efter lämpliga lösningar för specifika fall.

  2. Vissa utvecklare skapade en gång sin egen variant av en cache för en specifik situation (eftersom inget annat var lämpligt). Senare, på nästa projekt som inte involverade någon speciell cachelogik, använde de sin variant igen istället för att använda färdiga bibliotek (till exempel Ehcache). Resultatet blev ett gäng buggar och inkompatibiliteter, samt mycket bortkastad tid och stekta nerver.

    Vem som helst kan falla för detta antimönster. Om du är nybörjare kanske du inte är kunnig om designmönster. Detta kan leda till att du försöker lösa alla problem på ett sätt du bemästrat. Om vi ​​pratar om proffs, då kallar vi detta för professionell deformation eller nördsyn. Du har dina egna föredragna designmönster, och istället för att använda rätt använder du din favorit, förutsatt att en bra passform i det förflutna garanterar samma resultat i framtiden.

    Denna fallgrop kan ge mycket tråkiga resultat - från en dålig, instabil och svår att underhålla implementering till ett fullständigt misslyckande av projektet. Precis som det inte finns ett enda piller för alla sjukdomar, finns det inget designmönster för alla tillfällen.

9. För tidig optimering

För tidig optimering är ett antimönster vars namn talar för sig självt.
"Programmerare spenderar enormt mycket tid på att tänka och oroa sig över icke-kritiska platser i koden och försöka optimera dem, vilket bara påverkar efterföljande felsökning och support negativt. Vi bör generellt glömma bort optimering i till exempel 97 % av fallen. Dessutom , för tidig optimering är roten till allt ont. Som sagt, vi måste ägna all uppmärksamhet åt de återstående 3 %." — Donald Knuth
Till exempel lägga till index i förtid till en databas. Varför är det dåligt? Tja, det är dåligt i det att index lagras som ett binärt träd. Som ett resultat kommer trädet att räknas om varje gång ett nytt värde läggs till och tas bort, och detta tar resurser och tid. Därför bör index endast läggas till när det finns ett akut behov (om du har en stor mängd data och frågor tar för lång tid) och endast för de viktigaste fälten (de fält som är vanligast efterfrågade).

10. Spaghettikod

Spaghettikod är ett antimönster som definieras av kod som är dåligt strukturerad, förvirrande och svår att förstå, som innehåller alla typer av förgreningar, såsom inslagningsundantag, villkor och loopar. Tidigare var goto-operatören detta antimönsters främsta allierade. Goto-satser används egentligen inte längre, vilket lyckligtvis eliminerar ett antal tillhörande svårigheter och problem.

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 hemskt ut, eller hur? Tyvärr är detta det vanligaste anti-mönstret :( Inte ens den som skriver sådan kod kommer inte att kunna förstå det i framtiden. Andra utvecklare som ser koden kommer att tänka, "Ja, om det fungerar, då okej — det är bättre att inte röra det". Ofta är en metod till en början enkel och mycket transparent, men i takt med att nya krav läggs till bestraffas metoden gradvis med fler och fler villkorliga uttalanden, vilket gör den till en monstrositet som denna. Om en sådan metod visas, måste du refaktorera det antingen helt eller åtminstone de mest förvirrande delarna. Vanligtvis, när du schemalägger ett projekt, tilldelas tid för refactoring, till exempel är 30 % av sprinttiden för refactoring och tester. Detta förutsätter naturligtvis att det inte är någon brådska (men när händer det någonsin).här .

11. Magiska siffror

Magiska siffror är ett antimönster där alla typer av konstanter används i ett program utan någon förklaring av deras syfte eller betydelse. Det vill säga, de är i allmänhet dåligt namngivna eller i extrema fall finns det ingen kommentar som förklarar vad kommentarerna är eller varför. Precis som spagettikoden är detta ett av de vanligaste antimönstren. Någon som inte skrev koden kanske inte har en aning om de magiska siffrorna eller hur de fungerar (och med tiden kommer författaren själv inte att kunna förklara dem). Som ett resultat av att ändra eller ta bort ett nummer får koden magiskt att sluta fungera tillsammans. Till exempel 36 och 73. För att bekämpa detta antimönster rekommenderar jag en kodgranskning. Din kod måste granskas av utvecklare som inte är involverade i de relevanta avsnitten av koden. Deras ögon kommer att vara fräscha och de kommer att ha frågor: vad är det här och varför gjorde du det? Och naturligtvis måste du använda förklarande namn eller lämna kommentarer.

12. Kopiera och klistra in programmering

Kopiera-och-klistra-programmering är ett antimönster där någon annans kod tanklöst kopieras och klistras in, vilket kan resultera i oväntade biverkningar. Till exempel kopiering och inklistringsmetoder med matematiska beräkningar eller komplexa algoritmer som vi inte helt förstår. Det kan fungera för vårt specifika fall, men under vissa andra omständigheter kan det leda till problem. Anta att jag behöver en metod för att bestämma det maximala antalet i en array. När jag letade runt på Internet hittade jag den här 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 array med siffrorna 3, 6, 1, 4 och 2, och metoden returnerar 6. Bra, låt oss behålla det! Men senare får vi en array som består av 2,5, -7, 2 och 3, och då är vårt resultat -7. Och det här resultatet är inte bra. Problemet här är att Math.abs() returnerar det absoluta värdet. Okunskap om detta leder till katastrof, men bara i vissa situationer. Utan en djupgående förståelse för lösningen finns det många fall som du inte kommer att kunna verifiera. Kopierad kod kan också gå utanför applikationens interna struktur, både stilistiskt och på en mer fundamental, arkitektonisk nivå. Sådan kod kommer att vara svårare att läsa och underhålla. Och naturligtvis får vi inte glömma att det är en speciell typ av plagiat att direkt kopiera någon annans kod.

13. Uppfinna hjulet på nytt

Att återuppfinna hjulet är ett antimönster, ibland även känt som att återuppfinna det fyrkantiga hjulet. I grund och botten är denna mall motsatsen till kopiera-och-klistra-anti-mönstret ovan. I detta antimönster implementerar utvecklaren sin egen lösning för ett problem som det redan finns lösningar för. Ibland är dessa befintliga lösningar bättre än vad programmeraren hittar på. Oftast leder detta bara till förlorad tid och lägre produktivitet: programmeraren kanske inte hittar en lösning alls eller kan hitta en lösning som är långt ifrån den bästa. Som sagt, vi kan inte utesluta möjligheten att skapa en oberoende lösning, eftersom att göra det är en direkt väg till kopiera-och-klistra-programmering. Programmeraren bör vägledas av de specifika programmeringsuppgifter som uppstår för att kunna lösa dem på ett kompetent sätt, antingen genom att använda färdiga lösningar eller genom att skapa skräddarsydda lösningar. Väldigt ofta, anledningen till att använda detta antimönster är helt enkelt brådska. Resultatet är en grund analys av (sök efter) färdiga lösningar. Att återuppfinna det fyrkantiga hjulet är ett fall där anti-mönstret som övervägs har ett negativt resultat. Det vill säga projektet kräver en anpassad lösning, och utvecklaren skapar den, men dåligt. Samtidigt finns det redan ett bra alternativ och andra använder det framgångsrikt. Summa summarum: en enorm mängd tid går förlorad. Först skapar vi något som inte fungerar. Sedan försöker vi omstrukturera det, och slutligen ersätter vi det med något som redan fanns. Ett exempel är att implementera din egen anpassade cache när många implementeringar redan finns. Oavsett hur begåvad du är som programmerare, bör du komma ihåg att att återuppfinna ett fyrkantigt hjul åtminstone är ett slöseri med tid. Och som ni vet är tid den mest värdefulla resursen.

14. Jojo-problem

Jojo -problemet är ett antimönster där programmets struktur är alltför komplicerad på grund av överdriven fragmentering (till exempel en alltför uppdelad arvskedja). "Jojo-problemet" uppstår när du behöver förstå ett program vars arvshierarki är lång och komplex, vilket skapar djupt kapslade metodanrop. Som ett resultat måste programmerare navigera mellan många olika klasser och metoder för att inspektera programmets beteende. Namnet på detta antimönster kommer från namnet på leksaken. Låt oss som ett exempel titta på följande arvskedja: Vi har ett teknikgränssnitt:

public interface Technology {
   void turnOn();
}
Transportgränssnittet ärver det:

public interface Transport extends Technology {
   boolean fillUp();
}
Och så har vi ett annat gränssnitt, GroundTransport:

public interface GroundTransportation extends Transport {
   void startMove();
   void brake();
}
Och därifrån härleder vi en abstrakt bilklass:

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();
}
Nästa är den abstrakta Volkswagen-klassen:

public abstract class Volkswagen extends Car {
   @Override
   public void startMove() {
       /* some implementation */
   }
   @Override
   public void brake() {
       /* some implementation */
   }
}
Och slutligen en specifik modell:

public class VolkswagenAmarok extends Volkswagen {
   @Override
   public void fixCar(){
       /* some implementation */
   }
}
Denna kedja tvingar oss att leta efter svar på frågor som:
  1. Hur många metoder VolkswagenAmarokhar man?

  2. Vilken typ ska infogas istället för frågetecknet för att uppnå maximal abstraktion:

    
    ? someObj = new VolkswagenAmarok();
           someObj.brake();
    
Det är svårt att snabbt svara på sådana frågor – det kräver att vi tar en titt och undersöker, och det är lätt att bli förvirrad. Och vad händer om hierarkin är mycket större, längre och mer komplicerad, med alla möjliga överbelastningar och åsidosättanden? Den struktur vi skulle ha skulle skymmas på grund av överdriven fragmentering. Den bästa lösningen vore att minska de onödiga splittringarna. I vårt fall skulle vi lämna Teknik → Bil → VolkswagenAmarok.

15. Oavsiktlig komplexitet

Onödig komplexitet är ett antimönster där onödiga komplikationer introduceras till en lösning.
"Varje dåre kan skriva kod som en dator kan förstå. Bra programmerare skriver kod som människor kan förstå." – Martin Fowler
Så vad är komplexitet? Det kan definieras som den svårighetsgrad med vilken varje operation utförs i programmet. Som regel kan komplexitet delas in i två typer. Den första typen av komplexitet är antalet funktioner som ett system har. Den kan reduceras på bara ett sätt — genom att ta bort någon funktion. De befintliga metoderna måste övervakas. En metod bör tas bort om den inte längre används eller fortfarande används men utan att ge något värde. Dessutom måste du bedöma hur alla metoder i applikationen används, för att förstå var investeringar skulle löna sig (mycket kodåteranvändning) och vad du kan säga nej till. Den andra typen av komplexitet är onödig komplexitet. Det kan bara botas genom ett professionellt tillvägagångssätt. Istället för att göra något "coolt" (unga utvecklare är inte de enda som är mottagliga för denna sjukdom), du måste tänka på hur du gör det så enkelt som möjligt, eftersom den bästa lösningen alltid är enkel. Anta till exempel att vi har små relaterade tabeller med beskrivningar av vissa enheter, till exempel en användare: Vad är antimönster?  Låt oss titta på några exempel (del 2) - 3Så vi har användarens id, id för språket som beskrivningen är gjord på och själva beskrivningen. På samma sätt har vi extra beskrivningar för bilar, filer, planer och kundtabeller. Hur skulle det då se ut att infoga nya värden i sådana 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();
   }
}
Och följaktligen har vi denna uppräkning:

public enum ServiceType {
   CAR(),
   USER(),
   FILE(),
   PLAN(),
   CUSTOMER()
}
Allt verkar vara enkelt och bra... Men hur är det med de andra metoderna? Faktum är att de alla kommer att ha ett gäng switchuttalanden och ett gäng nästan identiska databasfrågor, vilket i sin tur kommer att komplicera och svälla upp vår klass. Hur kunde allt detta göras enklare? Låt oss uppgradera vår enum lite:

@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 varje typ namnen på sin tabells ursprungliga fält. Som ett resultat blir metoden för att skapa en beskrivning:

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);
   }
Bekvämt, enkelt och kompakt, tycker du inte? Indikationen på en bra utvecklare är inte ens hur ofta han eller hon använder mönster, utan snarare hur ofta han eller hon undviker antimönster. Okunnighet är den värsta fienden, eftersom du behöver känna dina fiender genom synen. Tja, det är allt jag har för idag. Tack alla! :)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION