CLEAN CODE 04
REINŽENJERING INFORMACIONIH SISTEMA
2
• Uvod
• Nazivi (Names)
• Metode/Funkcije (Functions)
• Komentari (Comments)
• Formatiranje (Formatting)
• Objekti i strukture podataka (Objects and Data Structures)
• Upravljanje greškama (Error Handling)
• Testiranje (Unit Tests)
• Klase (Classes)
• Code Smells
• Junit Internals (primer)
• Refactoring SerialData (primer)
Reinženjering informacionih sistema 2020/2021
3
Klase (Classes)
Organizacija klase:
• Počinju sa listom promenljivih:
• public static
• private static
• private
• public ?
• public metode
• private metode
Reinženjering informacionih sistema 2020/2021
4
Klase (Classes)
Klase treba da budu kratke.
Veličina klase ne meri brojem linija, nego sa odgovornošću koju preuzima.
Velike klase mogu da imaju veliki broj metoda.
„Male” klase, mogu da imaju nekoliko metoda, ali mnogo odgovornosti, pa postaju „velike” klase.
Reinženjering informacionih sistema 2020/2021
5
Klase (Classes)
public class DrawingController {
//1692 linije
private DrawingFrame view;
private DrawingModel model = new DrawingModel();
public static final int SELECTION = 1;
public static final int POINT = 2;
public static final int LINE = 3;
public static final int CIRCLE = 4;
public static final int SQUARE = 5;
public static final int RECTANGLE = 6;
public static final int HEXAGON = 7;
private int status;
private LinkedList<Command> commands = new LinkedList<Command>();
private int lastCmdIndex = -1;
…
Reinženjering informacionih sistema 2020/2021
6
Klase (Classes)
public class DrawingController {
//1692 linije
…
private LinkedList<String> commandsLog = new LinkedList<String>();
private int commandsLogIndex = 0;
private String commandsLogString;
private String[] commandsLogStringParts;
private String[] commandsLogStringPartsInner;
private String[] commandsLogStringPartsInnerParams;
private Point t;
private Line l;
private Circle k;
private Square kv;
private Rectangle p;
private HexagonAdapter h;
…
Reinženjering informacionih sistema 2020/2021
7
Klase (Classes)
public class DrawingController {
//1692 linije
…
private Point lt1;
private Point lt2;
private int desni = 1;
private int tackobrojac = 1;
private Point pointStart = new Point(-1, -1);
private Point pointEnd = new Point(-1, -1);
Reinženjering informacionih sistema 2020/2021
8
Klase (Classes)
public class DrawingController {
//1692 linije
…
public int getStatus(){…}
public void setStatus(int status) {…}
public void btnWhite_mouseClicked(MouseEvent e){…} // 7
public void btnBlue_mouseClicked(MouseEvent e) {…} // 7
public void btnRed_mouseClicked(MouseEvent e) {…} // 7
public void btnYellow_mouseClicked(MouseEvent e) {…} // 7
public void btnGreen_mouseClicked(MouseEvent e) {…} // 7
public void btnBlack_mouseClicked(MouseEvent e) {…} // 7
public void pnlPapir_mouseClicked(MouseEvent e) {…} //280
public void pnlPapir_mouseMoved(MouseEvent e1) {…} //33
public void pnlPapir_mouseDragged(MouseEvent ee) {…} //85
Reinženjering informacionih sistema 2020/2021
9
Klase (Classes)
public class DrawingController {
//1692 linije
…
public void pnlPapir_mouseDragged(MouseEvent ee) {…} //80
public void btnSet_actionPerformed(ActionEvent e) {…} //90
public void btnDelete_actionPerformed(ActionEvent e) {…} //40
public void btnOpen_actionPerformed(ActionEvent arg0)
throws FileNotFoundException, IOException, ClassNotFoundException {…} //50
public void btnSave_actionPerformed(ActionEvent arg0) {…} //50
public void btnToFront_actionPerformed(ActionEvent arg0) {…} //10
public void btnToBack_actionPerformed(ActionEvent arg0) {…} //10
private String colorTranslate(Color colorEnglish) {…} //10
private void paintOutline(Color colorOutline) {…} //60
}
Reinženjering informacionih sistema 2020/2021
10
Klase (Classes)
public class DrawingController {
//1692 linije
…
public void paintFill(Color colorFill) {…} //40
public void doCmd(Command c) {…} //30
public void undoCmd() {…} //10
public void redoCmd() {…} //10
public void runLog() {…} //610
Reinženjering informacionih sistema 2020/2021
11
Klase (Classes)
public class SuperDashboard extends JFrame implements MetaDataUser
public Component getLastFocusedComponent()
public void setLastFocused(Component lastFocused)
public int getMajorVersionNumber()
public int getMinorVersionNumber()
public int getBuildNumber()
}
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
Reinženjering informacionih sistema 2020/2021
12
Klase (Classes)
public class Version {
public int getMajorVersionNumber()
public int getMinorVersionNumber()
public int getBuildNumber()
}
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
Reinženjering informacionih sistema 2020/2021
13
Klase (Classes)
Kohezija (The Single Responsibility Principle)
• Klase treba da imaju mali broj varijabli instance.
• Svaka metoda klase treba da manipuliše sa jednom ili više od tih varijabli instance.
• Što je veći broj varijabli instance, od broja dostupnih, sa kojim metode manipulišu to je veća
kohezija između metoda i klasa.
• Klase u kojima sve metode manipulišu sa svim varijablama instance ima maksimalnu koheziju.
Reinženjering informacionih sistema 2020/2021
14
Klase (Classes)
Kohezija
• Ukoliko su metode kratke i manipulišu sa malim brojem varijabli instance, to može dovesti do
toga da je određene metode moguće grupisati po osnovu varijabli sa kojima manipulišu i to
može biti dobar pokazatelj da se te metode, zajedno sa varijablama instance mogu ekstrahovati
u novu klasu.
• Na taj način i klasa iz koje se vrši ekstrakcija, kao i novonastale klase imaju veću koheziju, što je
poželjna osobina za klase, jer prikazuje da su metode i klasa međusobno zavisne i da
predstavljaju jednu logičnu celinu.
Reinženjering informacionih sistema 2020/2021
15
Klase (Classes)
Kohezija
• Ukoliko imate veliku metodu u kojoj ste definisali veliki broj varijabli, trebalo bi da razmišljate o
tome da se deo metode možda može izdvojiti u neku drugu, posebnu metodu.
• Potencijalni problem je u tome što ta nova metoda treba da koristi deo varijabli definisanih u
staroj, velikoj metodi, pa može izgledati da to nije dobro rešenje i da ćete morati novoj metodi da
prosleđujete sve varijable nad kojima je potrebno vršiti operacije.
• U tom slučaju, dobra praksa bi bila da potrebne promenljive budu „promovisane“ u varijable
instanci. Tada novoj metodi nećete morati da prosleđujete bilo kakve promenljive.
Reinženjering informacionih sistema 2020/2021
16
Klase (Classes)
Kohezija
• Posledica je da imate veliki broj kratkih metoda.
• Posmatranjem tih metoda mogu se prepoznati grupe metoda koje vrše operacije nad istim, ali ne
svim, varijablama instance.
• Ta grupa metoda i varijable instanci nad kojima se vrše operacije su kandidati za izdvajanje u
neku novu klasu.
Reinženjering informacionih sistema 2020/2021
17
Klase (Classes)
Kohezija
• Ukoliko ne možete da opišete šta klasa radi u 25 reči, bez upotrebe „i“, „ili“, „ako“ i „ali“, klasa
verovatno ima previše odgovornosti.
• Napisati kod koji radi i kod koji je čist i pogodan za održavanje su dve različite stvari.
• Kada se napiše kod koji radi, to ne predstavlja završetak rada na kodu.
Reinženjering informacionih sistema 2020/2021
18
Klase (Classes)
package literatePrimes;
public class PrintPrimes {
public static void main(String[] args) {
final int M = 1000;
final int RR = 50;
final int CC = 4;
final int WW = 10;
final int ORDMAX = 30;
int P[] = new int[M + 1];
int PAGENUMBER;
int PAGEOFFSET;
int ROWOFFSET;
int C;
Reinženjering informacionih sistema 2020/2021
19
Klase (Classes)
int J;
int K;
boolean JPRIME;
int ORD;
int SQUARE;
int N;
int MULT[] = new int[ORDMAX + 1];
J = 1;
K = 1;
P[1] = 2;
ORD = 2;
SQUARE = 9;
while (K < M) {
do {
J = J + 2;
…
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
Reinženjering informacionih sistema 2020/2021
20
Klase (Classes)
if (J == SQUARE) {
ORD = ORD + 1;
SQUARE = P[ORD] * P[ORD];
MULT[ORD - 1] = J;
}
N = 2;
JPRIME = true;
while (N < ORD && JPRIME) {
while (MULT[N] < J)
MULT[N] = MULT[N] + P[N] + P[N];
if (MULT[N] == J)
JPRIME = false;
N = N + 1;
}
} while (!JPRIME);
K = K + 1;
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
P[K] = J;
}
Reinženjering informacionih sistema 2020/2021
21
Klase (Classes)
{
PAGENUMBER = 1;
PAGEOFFSET = 1;
while (PAGEOFFSET <= M) {
[Link]("The First " + M + " Prime Numbers --- Page " + PAGENUMBER);
[Link]("");
for (ROWOFFSET = PAGEOFFSET; ROWOFFSET < PAGEOFFSET + RR;
ROWOFFSET++){
for (C = 0; C < CC;C++)
if (ROWOFFSET + C * RR <= M)
[Link]("%10d", P[ROWOFFSET + C * RR]);
[Link]("");
}
[Link]("\f");
PAGENUMBER = PAGENUMBER + 1;
PAGEOFFSET = PAGEOFFSET + RR * CC;
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
}
}
}
Reinženjering informacionih sistema 2020/2021
22
Klase (Classes)
public class PrimePrinter {
public static void main(String[] args) {
final int NUMBER_OF_PRIMES = 1000;
int[] primes = [Link](NUMBER_OF_PRIMES);
final int ROWS_PER_PAGE = 50;
final int COLUMNS_PER_PAGE = 4;
RowColumnPagePrinter tablePrinter =
new RowColumnPagePrinter(ROWS_PER_PAGE, COLUMNS_PER_PAGE,
"The First " + NUMBER_OF_PRIMES + " Prime Numbers");
[Link](primes);
}
}
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
Reinženjering informacionih sistema 2020/2021
23
Klase (Classes)
import [Link];
public class RowColumnPagePrinter {
private int rowsPerPage;
private int columnsPerPage;
private int numbersPerPage;
private String pageHeader;
private PrintStream printStream;
public RowColumnPagePrinter(int rowsPerPage, int columnsPerPage, String pageHeader) {
[Link] = rowsPerPage;
[Link] = columnsPerPage;
[Link] = pageHeader;
numbersPerPage = rowsPerPage * columnsPerPage;
printStream = [Link];
}
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
Reinženjering informacionih sistema 2020/2021
24
Klase (Classes)
public void print(int data[]) {
int pageNumber = 1;
for (int firstIndexOnPage = 0;
firstIndexOnPage < [Link]; firstIndexOnPage += numbersPerPage) {
int lastIndexOnPage =
[Link](firstIndexOnPage + numbersPerPage - 1, [Link] - 1);
printPageHeader(pageHeader, pageNumber);
printPage(firstIndexOnPage, lastIndexOnPage, data);
[Link]("\f");
pageNumber++;
}
}
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
Reinženjering informacionih sistema 2020/2021
25
Klase (Classes)
private void printPage(int firstIndexOnPage, int lastIndexOnPage, int[] data) {
int firstIndexOfLastRowOnPage =
firstIndexOnPage + rowsPerPage - 1;
for (int firstIndexInRow = firstIndexOnPage;
firstIndexInRow <= firstIndexOfLastRowOnPage;
firstIndexInRow++) {
printRow(firstIndexInRow, lastIndexOnPage, data);
[Link]("");
}
}
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
Reinženjering informacionih sistema 2020/2021
26
Klase (Classes)
private void printRow(int firstIndexInRow, int lastIndexOnPage, int[] data) {
for (int column = 0; column < columnsPerPage; column++) {
int index = firstIndexInRow + column * rowsPerPage;
if (index <= lastIndexOnPage)
[Link]("%10d", data[index]);
}
}
private void printPageHeader(String pageHeader, int pageNumber) {
[Link](pageHeader + " --- Page " + pageNumber);
[Link]("");
}
public void setOutput(PrintStream printStream) {
[Link] = printStream;
}
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
}
Reinženjering informacionih sistema 2020/2021
27
Klase (Classes)
import [Link];
public class PrimeGenerator {
private static int[] primes;
private static ArrayList<Integer> multiplesOfPrimeFactors;
protected static int[] generate(int n) {
primes = new int[n];
multiplesOfPrimeFactors = new ArrayList<Integer>();
set2AsFirstPrime();
checkOddNumbersForSubsequentPrimes();
return primes;
}
private static void set2AsFirstPrime() {
primes[0] = 2;
[Link](2);
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
}
Reinženjering informacionih sistema 2020/2021
28
Klase (Classes)
private static void checkOddNumbersForSubsequentPrimes() {
int primeIndex = 1;
for (int candidate = 3; primeIndex < [Link]; candidate += 2) {
if (isPrime(candidate))
primes[primeIndex++] = candidate;
}
}
private static boolean isPrime(int candidate) {
if (isLeastRelevantMultipleOfNextLargerPrimeFactor(candidate)) {
[Link](candidate);
return false;
}
return isNotMultipleOfAnyPreviousPrimeFactor(candidate);
}
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
Reinženjering informacionih sistema 2020/2021
29
Klase (Classes)
private static boolean isLeastRelevantMultipleOfNextLargerPrimeFactor(int candidate) {
int nextLargerPrimeFactor = primes[[Link]()];
int leastRelevantMultiple = nextLargerPrimeFactor * nextLargerPrimeFactor;
return candidate == leastRelevantMultiple;
}
private static boolean isNotMultipleOfAnyPreviousPrimeFactor(int candidate) {
for (int n = 1; n < [Link](); n++) {
if (isMultipleOfNthPrimeFactor(candidate, n))
return false;
}
return true;
}
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
Reinženjering informacionih sistema 2020/2021
30
Klase (Classes)
private static boolean isMultipleOfNthPrimeFactor(int candidate, int n) {
return candidate == smallestOddNthMultipleNotLessThanCandidate(candidate, n);
}
private static int smallestOddNthMultipleNotLessThanCandidate(int candidate, int n) {
int multiple = [Link](n);
while (multiple < candidate)
multiple += 2 * primes[n];
[Link](n, multiple);
return multiple;
}
}
* Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship
Reinženjering informacionih sistema 2020/2021
31
Klase (Classes)
Navedeni primer ne predstavlja novo rešenje problema.
Kreirani su testovi koji pokrili tačno ponašanje aplikacije.
Potom su rađene male izmene i nakon svake male izmene pokretani su testovi.
Reinženjering informacionih sistema 2020/2021
32
Klase (Classes)
Četiri pravila jednostavnog dizajna (Kent Beck)
• Svi testovi prolaze
• Nema dupliciranog koda
• Izražava nameru programera
• Minimizira broj klasa i metoda
Reinženjering informacionih sistema 2020/2021
33
Klase (Classes)
Svi testovi prolaze
• Rezultat uspešnog dizajna je aplikacija koja radi ono što joj je bila namera.
• Možda je sistem u teoriji odlično dizajniran, ali ako ne postoji jednostavan način da se testira
dizajn ne možemo biti sigurni u i dizajn.
• Samo sistem koji je detaljno testiran i koji ima testove koji prolaze i svim uslovima predstavlja
sistem koji se jednostavno može testirati.
• Kreiranje sistema koji je jednostavan za testiranje za posledicu ima kreiranje klasa koje su male,
imaju samo jednu namenu (u skladu sa Single Responsibility Principle).
Reinženjering informacionih sistema 2020/2021
34
Klase (Classes)
Svi testovi prolaze
• Visoka sprega čini sistem teškom za testiranjem, a kreiranje testova i njihovo stalno pokretanje
vodi do niske sprege i visoke kohezije.
Refaktoring
• Kada je kod pokriven testovima, možete se posvetiti čišćenju koda.
• Svakih nekoliko linija možete uraditi refaktoring u kojem treba da primenite sve što znate o
dobrom dizajnu.
• Povećajte koheziju, smanjite spregu, preimenujte klase i metode, smanjite metode i klase itd.
• Radeći refaktoring biće isprunjena tri preosala zahteva dobrog dizajna.
Reinženjering informacionih sistema 2020/2021