Showing posts with label code smell. Show all posts
Showing posts with label code smell. Show all posts

Friday, 30 May 2014

Code Smells - Object Orientation Abusers

Object Orientation Abusers je još jedna klasa code smell-a. Neki od code smell-ova koji potpadaju ovde su:
  • Switch Statements
  • Temporary Field
  • Refused Interface
  • Inappropriate Static

Switch Statements

Standardan code smell u dizajnu je česta upotreba switch case-a. Ako se switch-case naredba upotrebljava više puta za jednu promenljivu (flag), onda se ona može zameniti polimorfizmom, gde bi data promenljiva (obično enumeracija) mogla biti konvertovana u zasebnu strukturu klasa, gde bi vrednosti te enumeracije predstavljale zasebne klase. Sve metode koje su do tada bile definisane za datu klasu i koje su uzimale u obzir enum promenljivu, potrebno je prebaciti u zasebne klase i telo svake od njih predstavlja telo case dela za datu enumeraciju u datoj (staroj) metodi.
Postoje situacije koje dovode do naglog povećanja klasa kod zamene switch-case-a, polimorfizmom, a ne postoji puto switch-case-eva u telu date klase, pa bi trebalo u ovom slučaju razmisliti da li je potrebno sprovesti ovo refaktorisanje.

Temporary Field

Ako se neki atribut klase koristi samo da bi prenosio neko stanje izmedju metoda, to je code smell (npr. u okviru jedne klase, postoje dve metode, koje se moraju pozvati u odredjenom redosledu, da bi atribut(i) te klase bili validni)
Ovaj code smell se refaktoriše u:
Primer:
class Employee
{
  private decimal _earningsForBonus;
  // other fields and methods
  private decimal CalculateBonus()
  {
    return _earningsForBonus * BonusPercentage();
  }
  private void CalculateEarningsForBonus()
  {
    _earningsForBonus = YearToDateEarnings() + OvertimeEarnings() * 2;
  }
}
se može refaktorisati u:
class BonusCalculator
{
  private decimal _earningsForBonus;
  private decimal _bonusPercentage;
  public BonusCalculator(Employee employee)
  {
    CalculateEarningsForBonus(employee.YearToDateEarnings(),
    employee.OvertimeEarnings());
    _bonusPercentage = employee.BonusPercentage();
  }
  public decimal CalculateBonus()
  {
    return _earningsForBonus * _bonusPercentage;
  }
  private void CalculateEarningsForBonus(decimal ytdEarnings, decimal ytdOvertimeEarnings)
  {
    _earningsForBonus = ytdEarnings + ytdOvertimeEarnings * 2;
  }
}

Refused Inheritance

Ako klasa koja nasledjuje parent klasu i override-uje većinu public metoda parent klase, onda je to code smell i ta izvedena klasa verovatno ne treba da bude u tom delu hijerarhije. U tom slučaju, veza između izvedene i parent klase, najverovatnije treba da bude kompozicija (izvedena klasa -> parent klasa), umesto nasleđivanja. Takođe, moguće je da te public metode parent klase, koje override-uje izvedena, treba prebaciti u izvedenu.

Inappropriate Static

Statičke metode se ne mogu naslediti i dovode to tight couplinga sa klijentskim kodom, pa ih treba pažljivo koristiti. Njihovo korišćenje treba rezervisati za:
  • ponašanje koje nikad neće biti potrebno redefinisati i
  • za stateless operacije
Primeri su:
  • Proste matematičke operacije
  • Globalne konstante

Tuesday, 27 May 2014

Code Smells - Obfuscated code

Obfuscated kod predstavlja kod koji je namerno napisan da bi bio nečitljiv. Definiciju obfuscated koda (koji bi se mogao prevesti kao nerazumljiv) možete videti ovde. Postoji opravdan razlog zbog kojeg bi se kod namerno mogao pisati tako da bude nečitljiv, ali sa aspekta refaktoringa, ovo predstavlja code smell koji predstavlja kandidata za refaktorisanje. Zašto? Zato što od svih aktivnosti vezanih za životni ciklus softvera (dizajn, implementacija, testiranje, održavanje itd.), 70% vremena ode na održavanje koda. Održavanje može predstavljati rešavanje bug-a, a u nekim slučajevima i dodavanje sitnih funkcionalnosti. Iz ovog razloga je bitno da kod bude što čitljiviji, jer će se najviše vremena potrošiti na čitanje i razumevanje datog koda. Ako uzmemo u obzir činjenicu da se na jednom projektu, u toku njegovog života, jako često menjaju ljudi koji održavaju kod, potrebno je da kod bude što čitljiviji i razumljiviji.

Regioni

Jedan od obfuscatora i code smell-a su regioni (C#) i to iz sledećih razloga:
  • Ako se koriste u okviru jedne metode, onda im je svrha da sakriju detalje predugačke metode koja je sama po sebi code smell
  • Predstavlja tepih ispod kojeg se krije smelly code, jer ako je kod klase toliko dugačak da postoji potreba da se zbog preglednosti podeli u regije, onda je to code smell i ta klasa bi trebalo da se podeli u više klasa  

Komentari

Komentari su jedan od najčešćih code smell-ova i jako često se zloupotrebljavaju.
Komentari:
  • Treba da objasne zašto je kod napisan tako kako je napisan, a ne šta i kako je urađeno. Ako se objašnjava kako je nešto urađeno, to znači da je taj komentar napisan zato što je, zbog nedovoljno jasnog i čitljivog koda, postojala potreba da se taj kod dodatno objasni, što predstavlja code smell.
  • Mogu biti zastareli i generalno im se ne može verovati, jer pri svakom nailasku na komentar postoji potreba da se proveri da li on ažurno opisuje deo koda, za koji je postojala potreba da se napiše
  • Kod treba pisati tako da su komentari redudantni, što znači da ne bi trebalo uopšte da postoje, već bi kod trebalo da bude samoobjašnjiv. Da ponovim, validan razlog za korišćenje komentara je kada je potrebno da objasni ZAŠTO je neki kod napisan tako kako je napisan

Method names

Kada se postavlja pitanje koliko dugačko treba biti ime metode, generalno pravilo je da treba preferirati duža imena metoda u odnosu na kraća. Dužim imenom metode, dovoljno jasno opisujemo šta data metoda radi. Kod davanja imena metodama (a i promenljivama), potrebno je staviti se u ulogu klijentskom programera i kako bi on očekivao da se zove metoda, koja radi to što radi. Ovaj princip se zove principle of least surprise

Imena metoda bi trebalo da budu dovoljno deskriptivna da iskomuniciraju klijentskom programeru, kada bi trebalo da je koristi (pozove).

Imena metoda ne bi trebalo da otkrivaju previše implementacionih detalja u svom imenu. Mogli bi pomisliti da se ovaj savet, u neku ruku, kosi sa prvim savetom za davanje imena metodama, ali prvi savet kaže da bi ime metode trebalo biti dovoljno deskriptivno da se može shvatiti šta metoda tačno radi, dok se ovaj savet tiče otkrivanje načina implementacije funkcionalnosti metode (kako metoda radi), koji ne bi trebalo otkrivati i koji bi trebalo abstrahovati od klijentskog koda.

Takođe, potrebno je da imena metoda opisuju bočne efekte. Primer:


public User GetUser(string userName)
{
 User user = GetUserFromDatabase(userName);
 if (user == null)
 {
   user = new User();
 }
 return user;
}

Dakle, ova metoda vraća user-a ako postoji u bazi, a ako ne postoji kreira novog. Ovo je neophodno iskomunicirati klijentu tako da bi, definitivno, prikladnije bilo nazvati metodu GetOrCreateUser.
Takođe, što se tiče imena, potrebno je biti konzistentan u davanju imena. Dakle, ako se koristi određeni prefiks ili sufiks u imenu jedne metode iz nekog razloga i ako se kreira neka druga metoda koja ima iste osobine kao prva metoda koja ima dati prefiks ili sufiks, potrebno je ostati konzistentan i kod imena druge metode, koristiti isti prefiks ili sufiks. Svako odstupanje u konzistentnosti zbunjuje ili dovodi čitaoca u sumnju vezano za to zašto se druga metoda zove drugačije, ako radi na isti način kao i prva metoda.

Tell don't ask design principle


Razlika između proceduralnog i objektno orijentisanog programiranja je, između ostalog i ta da je kod OOP ponašanje (metode) enkapsulirano zajedno sa podacima (atributi klase), dok isto ne važi za proceduralno programiranje. Iz ovog razloga se, kod proceduralnog programiranja, prvo dobavljaju podaci, a zatim nad njima vrši procesiranje, dok je, kod OOP, dozvoljeno procesiranje nad datim podacima enkapsulirano u istu klasu u kojoj se nalaze dati podaci. I pored ovoga postoji mogućnost da se definiše i eksterna logika za obradu podataka neke klase, ali ovo je nešto što treba izbegavati jer za ove potrebe postoji sama klasa. Na ovaj način (enkapsulacijom ponašanja zajedno sa podacima) je moguće na jednom mestu definisati dozvoljene obrade nad podacima (public interfejs klase), a takođe ovo povećava i reusability date logike, obzirom da je ona centralizovana. Ovo je ilustrovano na sledećoj slici i ovaj princip se naziva Tell don't ask principle, tj. potrebno je reći datoj klasi šta treba da odradi nad podacima, a ne dovlačiti njene podatke i nad njima vršiti neku obradu.