dagblog.
← Archiv

Problém “Božského objektu”


Setkal jsem s ním snad v každé aplikaci, ale teprve teď jsem proto našel ten správný výraz — God object.

a God object is an object that knows too much or does too much

Mluvím o objektu, který plní stěžejní úlohu v celé aplikaci, něco jako centrální mozek lidstva ze seriálu Návštěvníci. Problém tohoto objektu je v tom, že neustále bobtná, výsledkem je, že máme interface s dvaceti a více metodami, což jednoduše nepřidá na přehlednosti našeho API a to už ani nemluvím o jeho implementaci.

To, že v aplikaci existuje nějaká business fasáda, která zastřešuje vícero operací je myslím v pořádku. To co většinou nebývá v pořádku je jakým způsobem je tato fasáda vytvořena a především naimplementována. Pomůžeme si praktickým příkladem.

public interface ObjednavkovaSluzba {

 public void vytvorObjednavku(Objednavka objednavka);

 public void smazObjednavku(Objednavka objednavka);

 public void aktualizujObjednavku(Objednavka objednavka);

 public Objednavka nactiObjednavku(Serializable identifikator);

 

 public void zpracujObjednavku(Serializable identifikator);

 public void zrusObjednavku(Serializable identifikator); 

}

Jak je vidět z rozhraní, míchají se nám tam dvě odpovědnosti (většinou to bývá i více odpovědností nežli dvě). Jednak samotné CRUD (Create, Read, Update, Delete) operace pro vytvoření objednávky a jednak operace pro zpracování objednávky. Logika věci nám velí rozhraní roztrhnout, ale jak to udělat, aby zde byla stále možnost jednoho centralizovaného rozhraní pro práci s objednávkami.

Je to velice prosté. Opravdu to roztrhneme na dvě rozhraní podle odpovědností, ale rozhraní ObjednavkovaSluzba zachováme. A zachováme ho tak, že bude složením právě těchto dvou rozhraní.

public interface CRUDOperace {

 public void vytvorObjednavku(Objednavka objednavka);

 public void smazObjednavku(Objednavka objednavka);

 public void aktualizujObjednavku(Objednavka objednavka);

 public Objednavka nactiObjednavku(Serializable identifikator); 

} 



public interface ProcesniOperace { 

 public void zpracujObjednavku(Serializable identifikator);

 public void zrusObjednavku(Serializable identifikator); 

} 

 

public interface ObjednavkovaSluzba extends CRUDOperace, ProcesniOperace {

 

}

Díky tomuto roztržení umožníme klientům využít nejen různé pohledy na jednu fasádu (CRUD, procesní operace a nebo celek), ale umožníme mít implementaci náležející plně dané odpovědnosti. Výslednou funkcionalitu můžeme dosáhnout například složením a delegací těchto implementací.

public class CRUDOperaceImpl implements CRUDOperace {

 //...implementace vynechána

} 



public class ProcesniOperaceImpl implements ProcesniOperace {

 //...implementace vynechána

}



public class ObjednavkovaSluzbaImpl implements ObjednavkovaSluzba {

 private CRUDOperace crudDelegate;

 private ProcesniOperace procesniDelegate;

 

 public void vytvorObjednavku(Objednavka objednavka) {

 crudDelegate.vytvorObjednavku(objednavka); 

 } 

 

 public Objednavka nactiObjednavku(Serializable identifikator) {

 procesniDelegate.nactiObjednavku(identifikator);

 } 

 

 //...zbytek vynechán

}

Tímto velice jednoduchým cvičením jsme náš Božský objekt dekomponovali na menší části, které můžeme lépe spravovat, ale zároveň jsme zachovali jednotné rozhraní, které zaštiťuje práci s centrální funkcionalitou. Takto zrefaktorované rozhraní je navíc pro klienty API zpětně kompatibilní, takže se nemusíme obávat, že bychom něco rozbili.

Verze na Medium

Související články

Související epizody CZ Podcastu