TDD_I_08: Tvorba objektů
$5 + 10 CHF = $10 je-li kurz 2:1
$5 * 2 = $10
Převést položku mnozstvi na soukromou
Vedlejší účinky třídy Dolar?
Zaokrouhlování peněz?
Metoda equals()
Metoda hashCode()
Rovnost s null
Rovnost objektů
5 CHF * 2 = 10 CHF
Dolar / Frank duplicita
Společná metoda equals()
Společné násobení
Porovnání franků a dolarů
Obě implementace metody krat() jsou si nápadně podobné:
Frank
Frank krat(int nasobek) { return new Frank(mnozstvi * nasobek); }
Dolar
Dolar krat(int nasobek) { return new Dolar(mnozstvi * nasobek); }
Můžeme pokročit k jejich sjednocení tím, že obě budou vracet objekt Penize.
Frank
Penize krat(int nasobek) { return new Frank(mnozstvi * nasobek); }
Dolar
Penize krat(int nasobek) { return new Frank(mnozstvi * nasobek); }
Další krok už není tak snadný. Obě podtřídy Penize nemají tolik úkolů, aby stálo za to se jimi zabývat a tak bychom se jich rádi zbavili. To však nelze udělat v jediném velkém kroku, protože bychom si tak nemohli názorně předvést TDD.
Tak dobře. čím méně přímých odkazů na obě podtřídy bude existovat, tím blíže budeme k jejich odstranění. Ve třídě Penize můžeme zavést tovární metodu, která vrací objekt Dolar. Použili bychom ji takto:
public void testNasobeni() { Dolar pet = Penize.dolar(5); assertEquals(new Dolar(10), pet.krat(2)); assertEquals(new Dolar(15), pet.krat(3)); }
Iplementace vytváří a vrací objekt Dolar:
Penize
static Dolar dolar(int mnozstvi) { return new Dolar(mnozstvi); }
My však chceme, aby reference na objekty Dolar zmizely. Proto musíme v testu změnit deklaraci:
public void testNasobeni() { Penize pet = Penize.dolar(5); assertEquals(new Dolar(10), pet.krat(2)); assertEquals(new Dolar(15), pet.krat(3)); }
Překladač nás úslužně informuje, že metoda krat() není ve třídě Penize definována. V tomto okamžiku jěště nejsme připraveni ji implementovat a proto musí být třída Penize abstraktní (myslím, že tím jsme měli začít) a deklarovat metodu Penize.krat():
Penize
abstract class Penize { abstract Penize krat(int nasobek); }
Teď můžeme změnit deklaraci tovární metody:
Penize
static Penize dolar(int mnozstvi) { return new Dolar(mnozstvi); }
Všechny testy fungují, takže jsme alespoň nic nepokazili. Nyní můžeme použít naši tovární metodu všude v testech:
public void testNasobeni() { Dolar pet = Penize.dolar(5); assertEquals(Penize.dolar(10), pet.krat(2)); assertEquals(Penize.dolar(15), pet.krat(3)); } public void testRovnosti() { assertTrue(Penize.dolar(5).equals(Penize.dolar(5))); assertFalse(Penize.dolar(5).equals(Penize.dolar(6))); assertTrue(new Frank(5).equals(new Frank(5))); assertFalse(new Frank(5).equals(new Frank(6))); ssertFalse(new Frank(5).equals(Penize.dolar(5))); }
Teď jsme na tom trochu lépe než předtím. Žádný klientský kód neví, že existuje podtřída nazvaná Dolar Jelikož jsme oddělili testy od podtříd, můžeme nyní volně měnit dědičnost, aniž by to mělo nějaký vliv na výkonný kód.
Než však začneme slepě měnit test testNasobeniFranku, všimenem si, že netestuje žádnou logiku, která by nebyla testována testem pro násobení v objektu Dolar. Pokud smažeme tento test, ztratíme snad důvěru ve výsledný kód? jen trochu, a tak ho tam necháme. Ale je to podezřelé.
public void testRovnosti() { assertTrue(Penize.dolar(5).equals(Penize.dolar(5))); assertFalse(Penize.dolar(5).equals(Penize.dolar(6))); assertTrue(Penize.frank(5).equals(Penize.frank(5))); assertFalse(Penize.frank(5).equals(Penize.frank(6))); ssertFalse(Penize.frank(5).equals(Penize.dolar(5))); } public void testNasobeniFranku() { Penize pet = Penize.frank(5); assertEquals(Penize.frank(10), pet.krat(2)); assertEquals(Penize.frank(15), pet.krat(3)); }
Implementace je stejná jako v případě metody Penize.dolar():
Penize
static Penize frank(int mnozstvi) { return new Frank(mnozstvi); }
$5 + 10 CHF = $10 je-li kurz 2:1
$5 * 2 = $10
Převést položku mnozstvi na soukromou
Vedlejší účinky třídy Dolar?
Zaokrouhlování peněz?
Metoda equals()
Metoda hashCode()
Rovnost s null
Rovnost objektů
5 CHF * 2 = 10 CHF
Dolar / Frank duplicita
Společná metoda equals()
Společné násobení
Porovnání franků a dolarů
Měna?
Smazat metodu testNasobeniFranku?
Dále se zbavíme duplicity v metodě krat(). Abychom si to zopakovali:
- Pokročili jsme k odstranění duplicity tím, že jsme sjednotili zápisy dvou variant téže metody - krat().
- Přesunuli jsme alespoň deklaraci metody do společné nadřazené třídy.
- Oddělili jsme kód testu od konkrétní podtřídy tím, že jsme zavedli tovární metody.
- Všimli jsme si, že když podtřídy zmizí, některé testy budou nadbytečné, ale nechali jsme to být.
Vývoj řízený testy, Kapitola 7 | Dr3dweRkZ | Vývoj řízený testy, Kapitola 9