CodeGym /Java Blog /Random /Ano ang mga anti-pattern? Tingnan natin ang ilang halimba...
John Squirrels
Antas
San Francisco

Ano ang mga anti-pattern? Tingnan natin ang ilang halimbawa (Bahagi 2)

Nai-publish sa grupo
Ano ang mga anti-pattern? Tingnan natin ang ilang mga halimbawa (Bahagi 1) Ngayon ay ipinagpapatuloy namin ang aming pagsusuri sa mga pinakasikat na anti-pattern. Kung napalampas mo ang unang bahagi, narito ito. Ano ang mga anti-pattern?  Tingnan natin ang ilang halimbawa (Bahagi 2) - 1Kaya, ang mga pattern ng disenyo ay pinakamahusay na kasanayan. Sa madaling salita, ang mga ito ay mga halimbawa ng mahusay, nasubok sa oras na mga paraan ng paglutas ng mga partikular na problema. Sa turn, ang mga anti-pattern ay ang kanilang eksaktong kabaligtaran, sa diwa na ang mga ito ay mga pattern ng mga pitfalls o pagkakamali sa paglutas ng iba't ibang mga problema (masasamang pattern). Magpatuloy tayo sa susunod na software development anti-pattern.

8. Gintong martilyo

Ang ginintuang martilyo ay isang anti-pattern na tinukoy ng kumpiyansa na ang isang partikular na solusyon ay nalalapat sa pangkalahatan. Mga halimbawa:
  1. Matapos makatagpo ng isang problema at makahanap ng isang pattern para sa perpektong solusyon, sinusubukan ng isang programmer na ilagay ang pattern na ito sa lahat ng dako, ilapat ito sa kasalukuyan at lahat ng mga proyekto sa hinaharap, sa halip na maghanap ng mga naaangkop na solusyon para sa mga partikular na kaso.

  2. Ang ilang mga developer ay minsang lumikha ng kanilang sariling variant ng isang cache para sa isang partikular na sitwasyon (dahil wala nang iba pa ang angkop). Nang maglaon, sa susunod na proyekto na walang espesyal na lohika ng cache, muli nilang ginamit ang kanilang variant sa halip na gumamit ng mga handa na aklatan (halimbawa, Ehcache). Ang resulta ay isang grupo ng mga bug at hindi pagkakatugma, pati na rin ang maraming nasayang na oras at pinirito na nerbiyos.

    Kahit sino ay maaaring mahulog sa anti-pattern na ito. Kung ikaw ay isang baguhan, maaaring wala kang kaalaman tungkol sa mga pattern ng disenyo. Ito ay maaaring humantong sa iyo upang subukang lutasin ang lahat ng mga problema sa isang paraan na iyong pinagkadalubhasaan. Kung ang pinag-uusapan natin ay tungkol sa mga propesyonal, kung gayon tinatawag natin itong propesyonal na pagpapapangit o nerdview. Mayroon kang sariling ginustong mga pattern ng disenyo, at sa halip na gamitin ang tama, ginagamit mo ang iyong paborito, sa pag-aakalang ang isang mahusay na akma sa nakaraan ay ginagarantiyahan ang parehong resulta sa hinaharap.

    Ang pitfall na ito ay maaaring magbunga ng napakalungkot na resulta — mula sa isang masama, hindi matatag, at mahirap na mapanatili ang pagpapatupad hanggang sa isang kumpletong pagkabigo ng proyekto. Kung paanong walang isang tableta para sa lahat ng mga sakit, walang isang pattern ng disenyo para sa lahat ng okasyon.

9. Napaaga ang pag-optimize

Ang premature optimization ay isang anti-pattern na ang pangalan ay nagsasalita para sa sarili nito.
"Ang mga programmer ay gumugugol ng malaking oras sa pag-iisip at pag-aalala tungkol sa mga hindi kritikal na lugar sa code at sinusubukang i-optimize ang mga ito, na negatibong nakakaapekto lamang sa kasunod na pag-debug at suporta. Sa pangkalahatan, dapat nating kalimutan ang tungkol sa pag-optimize sa, halimbawa, 97% ng mga kaso. Bukod dito , ang maagang pag-optimize ang ugat ng lahat ng kasamaan. Sabi nga, dapat nating bigyang-pansin ang natitirang 3%." - Donald Knuth
Halimbawa, maagang pagdaragdag ng mga index sa isang database. Bakit masama iyon? Well, ito ay masama sa na, index ay naka-imbak bilang isang binary tree. Bilang resulta, sa tuwing may idaragdag at tatanggalin na bagong halaga, muling kakalkulahin ang puno, at ito ay kumukonsumo ng mga mapagkukunan at oras. Samakatuwid, ang mga index ay dapat na idagdag lamang kapag may apurahang pangangailangan (kung mayroon kang malaking halaga ng data at masyadong mahaba ang mga query) at para lamang sa pinakamahahalagang field (ang mga field na pinakamadalas itanong).

10. Spaghetti code

Ang spaghetti code ay isang anti-pattern na tinukoy ng code na hindi maganda ang pagkakaayos, nakakalito, at mahirap unawain, na naglalaman ng lahat ng uri ng pagsasanga, gaya ng mga pagbubukod sa pambalot, kundisyon, at mga loop. Dati, ang goto operator ang pangunahing kaalyado nitong anti-pattern. Ang mga pahayag ng Goto ay hindi na talaga ginagamit, na masayang nag-aalis ng ilang nauugnay na paghihirap at problema.

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;
               }
           }
       }
   }
}
Mukhang kakila-kilabot, hindi ba? Sa kasamaang palad, ito ang pinakakaraniwang anti-pattern :( Kahit na ang taong nagsusulat ng naturang code ay hindi mauunawaan ito sa hinaharap. Iisipin ng ibang mga developer na nakakakita ng code, "Well, kung ito ay gumagana, pagkatapos ay okay — mas mabuting huwag mo itong hawakan". Kadalasan, ang isang pamamaraan ay sa simula ay simple at napakalinaw, ngunit habang ang mga bagong kinakailangan ay idinagdag, ang pamamaraan ay unti-unting nababalot ng mas maraming kondisyonal na mga pahayag, na ginagawa itong isang napakapangit na tulad nito. Kung ang gayong pamamaraan lilitaw, kailangan mong i-refactor ito nang buo o hindi bababa sa pinakanakalilito na mga bahagi. Kadalasan, kapag nag-iiskedyul ng isang proyekto, ang oras ay inilalaan para sa refactoring, halimbawa, 30% ng oras ng sprint ay para sa refactoring at mga pagsubok. Siyempre, ipinapalagay nito na walang anumang pagmamadali (ngunit kailan mangyayari iyon).dito .

11. Mga magic na numero

Ang mga magic number ay isang anti-pattern kung saan ang lahat ng uri ng mga constant ay ginagamit sa isang programa nang walang anumang paliwanag sa kanilang layunin o kahulugan. Ibig sabihin, sa pangkalahatan ay hindi maganda ang pangalan nila o sa matinding kaso, walang komentong nagpapaliwanag kung ano ang mga komento o bakit. Tulad ng spaghetti code, isa ito sa mga pinakakaraniwang anti-pattern. Ang isang taong hindi sumulat ng code ay maaaring magkaroon o walang ideya tungkol sa mga magic number o kung paano gumagana ang mga ito (at sa kalaunan, ang may-akda mismo ay hindi maipaliwanag ang mga ito). Bilang resulta, ang pagpapalit o pag-alis ng isang numero ay nagiging sanhi ng mahiwagang paghinto ng code sa paggana nang magkakasama. Halimbawa, 36 at 73. Upang labanan ang anti-pattern na ito, inirerekomenda ko ang pagsusuri ng code. Ang iyong code ay kailangang tingnan ng mga developer na hindi kasangkot sa mga nauugnay na seksyon ng code. Magiging sariwa ang kanilang mga mata at magkakaroon sila ng mga katanungan: ano ito at bakit mo ginawa iyon? At siyempre, kailangan mong gumamit ng mga paliwanag na pangalan o mag-iwan ng mga komento.

12. Copy-and-paste programming

Ang copy-and-paste na programming ay isang anti-pattern kung saan ang code ng ibang tao ay hindi pinag-iisipan na kinopya at i-paste, na posibleng magresulta sa hindi inaasahang epekto. Halimbawa, ang pagkopya at pag-paste ng mga pamamaraan na may mga mathematical na kalkulasyon o kumplikadong algorithm na hindi namin lubos na nauunawaan. Maaari itong gumana para sa aming partikular na kaso, ngunit sa ilang iba pang mga pangyayari maaari itong humantong sa problema. Ipagpalagay na kailangan ko ng isang paraan upang matukoy ang maximum na numero sa isang array. Naghahalungkat sa Internet, nakita ko ang solusyong ito:

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;
}
Makakakuha tayo ng array na may mga numerong 3, 6, 1, 4, at 2, at ang pamamaraan ay nagbabalik ng 6. Mahusay, panatilihin natin ito! Ngunit sa paglaon ay nakakakuha kami ng array na binubuo ng 2.5, -7, 2, at 3, at pagkatapos ay ang aming resulta ay -7. At hindi maganda ang resultang ito. Ang problema dito ay ang Math.abs() ay nagbabalik ng absolute value. Ang kamangmangan dito ay humahantong sa sakuna, ngunit sa ilang mga sitwasyon lamang. Kung walang malalim na pag-unawa sa solusyon, maraming kaso na hindi mo mabe-verify. Ang kinopyang code ay maaari ding lumampas sa panloob na istraktura ng application, parehong istilo at sa isang mas pangunahing, antas ng arkitektura. Ang ganitong code ay magiging mas mahirap basahin at panatilihin. At siyempre, hindi natin dapat kalimutan na ang direktang pagkopya ng code ng ibang tao ay isang espesyal na uri ng plagiarism.

13. Muling pag-imbento ng gulong

Ang muling pag-imbento ng gulong ay isang anti-pattern, na kung minsan ay kilala rin bilang muling pag-imbento ng square wheel. Sa esensya, ang template na ito ay kabaligtaran ng copy-and-paste na anti-pattern na isinasaalang-alang sa itaas. Sa anti-pattern na ito, ipinapatupad ng developer ang kanyang sariling solusyon para sa isang problema kung saan mayroon nang mga solusyon. Minsan ang mga umiiral na solusyon na ito ay mas mahusay kaysa sa iniimbento ng programmer. Kadalasan, ito ay humahantong lamang sa nawawalang oras at mas mababang produktibidad: ang programmer ay maaaring hindi makahanap ng solusyon sa lahat o maaaring makahanap ng solusyon na malayo sa pinakamahusay. Iyon ay sinabi, hindi namin maaaring ibukod ang posibilidad ng paglikha ng isang independiyenteng solusyon, dahil ang paggawa nito ay isang direktang daan patungo sa pagkopya at pag-paste ng programming. Ang programmer ay dapat magabayan ng mga partikular na gawain sa programming na lumitaw upang malutas ang mga ito nang may kakayahan, maging sa pamamagitan ng paggamit ng mga handa na solusyon o sa pamamagitan ng paglikha ng mga custom na solusyon. Madalas, ang dahilan ng paggamit ng anti-pattern na ito ay pagmamadali lamang. Ang resulta ay isang mababaw na pagsusuri ng (paghahanap para sa) mga handa na solusyon. Ang muling pag-imbento ng square wheel ay isang kaso kung saan ang anti-pattern na isinasaalang-alang ay may negatibong resulta. Iyon ay, ang proyekto ay nangangailangan ng isang pasadyang solusyon, at ang developer ay lumikha nito, ngunit masama. Kasabay nito, mayroon nang magandang opsyon at matagumpay na ginagamit ito ng iba. Bottom line: isang malaking halaga ng oras ang nawala. Una, gumawa kami ng isang bagay na hindi gumagana. Pagkatapos ay sinubukan naming i-refactor ito, at sa wakas ay pinapalitan namin ito ng isang bagay na mayroon na. Ang isang halimbawa ay ang pagpapatupad ng iyong sariling custom na cache kapag marami nang pagpapatupad. Gaano ka man katalento bilang isang programmer, dapat mong tandaan na ang muling pag-imbento ng isang parisukat na gulong ay hindi bababa sa isang pag-aaksaya ng oras. At, tulad ng alam mo, ang oras ay ang pinakamahalagang mapagkukunan.

14. Yo-yo problema

Ang problema sa yo-yo ay isang anti-pattern kung saan ang istraktura ng application ay labis na kumplikado dahil sa labis na pagkapira-piraso (halimbawa, isang labis na hinati-hati na chain ng mana). Ang "problema sa yo-yo" ay lumalabas kapag kailangan mong maunawaan ang isang programa na ang hierarchy ng mana ay mahaba at kumplikado, na lumilikha ng malalim na nested method call. Bilang resulta, kailangan ng mga programmer na mag-navigate sa pagitan ng maraming iba't ibang klase at pamamaraan upang masuri ang pag-uugali ng programa. Ang pangalan ng anti-pattern na ito ay nagmula sa pangalan ng laruan. Bilang halimbawa, tingnan natin ang sumusunod na inheritance chain: Mayroon kaming Technology interface:

public interface Technology {
   void turnOn();
}
Ang interface ng Transport ay nagmamana nito:

public interface Transport extends Technology {
   boolean fillUp();
}
At pagkatapos ay mayroon kaming isa pang interface, GroundTransport:

public interface GroundTransportation extends Transport {
   void startMove();
   void brake();
}
At mula doon, nakukuha namin ang abstract na klase ng Kotse:

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();
}
Susunod ay ang abstract na klase ng Volkswagen:

public abstract class Volkswagen extends Car {
   @Override
   public void startMove() {
       /* some implementation */
   }
   @Override
   public void brake() {
       /* some implementation */
   }
}
At sa wakas, isang partikular na modelo:

public class VolkswagenAmarok extends Volkswagen {
   @Override
   public void fixCar(){
       /* some implementation */
   }
}
Pinipilit kami ng chain na ito na maghanap ng mga sagot sa mga tanong tulad ng:
  1. Ilang mga pamamaraan ang VolkswagenAmarokmayroon?

  2. Anong uri ang dapat ipasok sa halip na tandang pananong upang makamit ang maximum na abstraction:

    
    ? someObj = new VolkswagenAmarok();
           someObj.brake();
    
Mahirap mabilis na sagutin ang mga ganoong tanong — kailangan nitong tingnan at imbestigahan, at madaling malito. At paano kung ang hierarchy ay mas malaki, mas mahaba, at mas kumplikado, na may lahat ng uri ng mga overload at override? Ang istraktura na mayroon kami ay natatakpan dahil sa labis na pagkapira-piraso. Ang pinakamahusay na solusyon ay upang bawasan ang hindi kinakailangang mga dibisyon. Sa aming kaso, aalis kami sa Teknolohiya → Kotse → VolkswagenAmarok.

15. Hindi sinasadyang pagiging kumplikado

Ang hindi kinakailangang kumplikado ay isang anti-pattern kung saan ang mga hindi kinakailangang komplikasyon ay ipinakilala sa isang solusyon.
"Ang sinumang tanga ay maaaring magsulat ng code na naiintindihan ng isang computer. Ang magaling na programmer ay sumulat ng code na naiintindihan ng mga tao." — Martin Fowler
Kaya ano ang pagiging kumplikado? Maaari itong tukuyin bilang ang antas ng kahirapan kung saan ang bawat operasyon ay ginanap sa programa. Bilang isang patakaran, ang pagiging kumplikado ay maaaring nahahati sa dalawang uri. Ang unang uri ng pagiging kumplikado ay ang bilang ng mga function na mayroon ang isang system. Maaari itong bawasan sa isang paraan lamang — sa pamamagitan ng pag-alis ng ilang function. Ang mga umiiral na pamamaraan ay kailangang subaybayan. Dapat tanggalin ang isang paraan kung hindi na ito ginagamit o ginagamit pa ngunit walang anumang halaga. Higit pa rito, kailangan mong tasahin kung paano ginagamit ang lahat ng pamamaraan sa application, upang maunawaan kung saan magiging kapaki-pakinabang ang mga pamumuhunan (maraming muling paggamit ng code) at kung ano ang maaari mong tumanggi. Ang pangalawang uri ng pagiging kumplikado ay hindi kinakailangang kumplikado. Maaari lamang itong pagalingin sa pamamagitan ng isang propesyonal na diskarte. Sa halip na gumawa ng isang bagay na "cool" (Ang mga batang developer ay hindi lamang ang mga madaling kapitan sa sakit na ito), kailangan mong isipin kung paano ito gagawin nang simple hangga't maaari, dahil ang pinakamahusay na solusyon ay palaging simple. Halimbawa, ipagpalagay na mayroon kaming maliliit na nauugnay na mga talahanayan na may mga paglalarawan ng ilang entity, gaya ng isang user: Ano ang mga anti-pattern?  Tingnan natin ang ilang halimbawa (Bahagi 2) - 3Kaya, mayroon kaming id ng gumagamit, ang id ng wika kung saan ginawa ang paglalarawan, at ang paglalarawan mismo. Katulad nito, mayroon kaming mga pantulong na deskriptor para sa mga talahanayan ng mga kotse, file, plano, at mga customer. Kung gayon, ano ang magiging hitsura ng pagpasok ng mga bagong halaga sa naturang mga talahanayan?

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();
   }
}
At ayon dito, mayroon kaming enum na ito:

public enum ServiceType {
   CAR(),
   USER(),
   FILE(),
   PLAN(),
   CUSTOMER()
}
Ang lahat ay tila simple at mabuti... Ngunit paano ang iba pang mga pamamaraan? Sa katunayan, lahat sila ay magkakaroon din ng isang bungkos ng switchmga pahayag at isang bungkos ng halos magkaparehong mga query sa database, na kung saan naman ay lubos na magpapalubha at magpapalaki sa ating klase. Paano magiging mas madali ang lahat ng ito? I-upgrade natin nang kaunti ang ating enum:

@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;
}
Ngayon ang bawat uri ay may mga pangalan ng orihinal na mga field ng talahanayan nito. Bilang resulta, ang paraan para sa paglikha ng isang paglalarawan ay nagiging:

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);
   }
Maginhawa, simple, at compact, hindi ba? Ang indikasyon ng isang mahusay na developer ay hindi kahit gaano kadalas siya gumamit ng mga pattern, ngunit kung gaano kadalas niya iniiwasan ang mga anti-pattern. Ang kamangmangan ay ang pinakamasamang kaaway, dahil kailangan mong malaman ang iyong mga kaaway sa pamamagitan ng paningin. Well, iyon lang ang mayroon ako para sa araw na ito. Salamat, lahat! :)
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION