Dobrá praxe - from Effective Java by Josh Bloch

Dobré praktiky vytváření objektů

  1. Consider static factory method instead of constructors

  2. Consider a builder instead of constructor with many parameters

  3. Enforce singleton by private constructors (or enums)

  4. Prevent instantiation by private constructor

  5. Prefer dependency injection over hardwiring dependencies

  6. Avoid unnecessary objects

  7. Eliminate obsolete object references

  8. Avoid finalizers and cleaners

Tovární metoda namísto konstruktoru

  • Consider static factory method instead of constructors "Java efektivně, Tip 1: Používejme statickou tovární ("výrobní") metodu namísto přímého volání konstruktoru (evt. s parametry)" — Josh Bloch

  • Takových metod může být více a mohou se jmenovat různě (výstižněji) - viz Person createMale()

  • Nemusí vrátit objekt za všech okolností, při všech možných voláních s různými parametry - někdy mohou vrátit null

  • Metoda může vrátit již existující instanci - to je klíčová výhoda, např. u singletonu nebo u skladiště (poolu) objektů - skladiště objektů šetří čas a výkon, singleton je bezpečný

Tovární metoda: variabilita vracených typů

  • Metoda nemusí vrátit objekt přesně stejného typu, jako je deklarován, může vrátit i objekt podtřídy, potomka - např. metoda static Person createEmployee(…​) vrátí objekt třídy Employee, jež je podtřídou Person

  • Dokonce se skutečný vrácený typ může lišit dle předaných parametrů: static Person createEmployee(boolean manager) může dle vrátit Manager nebo Employee

  • Tovární metoda někdy může vracet objekt třídy neznámé v době překladu, tzn. objekt pomocí reflexe dynamicky zavede - tato technika umožňuje modularitu a doplňování kódu a funkcionality dokonce za běhu

  • u statických továrních metod se držte běžného očekávaného pojmenování, například: newPerson, createPerson, Person.create, pro přístup k skladišti nebo singletonu třeba Person.getInstance()

Namísto složitého konstruktoru použijme vzor "Výrobce"

  • Consider a builder instead of constructor with many parameters

  • Někdy máme složitější objekty s mnoha atributy

  • Konstrukce pak obnáší volat konstruktor s mnoha parametry

  • Příklad new Person("Jan Novák", true, 22000), kde true značí, že jde o muže, a 22000 je plat.

  • V Javě jsou parametry poziční a nepojmenované (na rozdíl od novějšího Kotlinu např.), je pak problém vidět, co kde nastavujeme -

  • zejména když jsou parametry stejného typu - new Line(0.0, 0.0, 1.0, 2.0) znamená přesně co?

  • Pro tyto případy se namísto složitého konstruktoru hodí tzv. builder ("výrobce").

Příklad "výrobce"

public class PersonBuilder {
    private String name;
    //... male, salary
    public void setName(String name) {...}
    public void setGender(boolean male) {...}
    public void setSalary(double salary) {...}
    public Person getPerson() {
        // it can perform necessary checks
        // and refuse to create unless fulfilled
        return new Person(name, male, salary);
    }
}

Použití "výrobce"

PersonBuilder builder = new PersonBuilder();
builder.setName("Jan Novák");
builder.setGender(true);
builder.setSalary(22000.0);
// person can immediately be used
Person person = builder.getPerson();

Protipříklad - nepoužití "výrobce"

public class Person {
    private String name;
    //... male, salary
    public void setName(String name) {
        // set name here
    }
    public void setGender(boolean male) {...}
    public void setSalary(double salary) {...}
    public Person() {}
}

Bez použití "výrobce"

Person person = new Person();
// now the person is NOT complete
// it is dangerous to use, cannot be used
person.setName("Jan Novák");
person.setGender(true);
person.setSalary(22000.0);
// now it is OK

Privátní konstruktory

  • Ve výše uvedených tipech s tovární metodou i výrobcem jsme zamlčeli podstatnou věc:

  • uvedené mechanismy vytvoření objektu se daly obejít jeho přímou konstrukcí new Person(…​).

  • Aby toto nebylo možné, můžeme všechny konstruktory označit jako private

  • Pak zvenčí nelze instanci vytvořit pomocí new Person(…​), ale musí např. být statická tovární metoda Person.newInstance() nebo výrobce Builder builder = Person.builder().

Privátní nebo chráněné konstruktory?

  • Rozdíl mezi private a protected

  • Kdybychom konstruktory označili jako private, ale některé přesto ponechali protected, dovolíme tím dědění třídy, např. z Person můžeme podědit do Employee.

  • Kdyby úplně všechny byly private, máme smůlu a z Person nikdy nic nepodědíme.

  • Je to proto, že každá podtřída musí mít konstruktor, který jako první příkaz volá konstruktor předka - a ten kdyby byl soukromý, nebylo by možné.

Singletony

  • Jsou klasickým návrhovým vzorem popsaným jinde.

  • V souvislosti s konstrukcí platí, že objekt singletonu se konstruuje buďto předem nebo až je potřeba

  • Nesmí být zkonstruovatelný jinak, např. přímo přes new (konstruktorem)

  • Enforce singleton by private constructors (or enums)

  • Prevent instantiation by private constructor

Příklad singletonu

public class DataManager {
    private static DataManager manager;
    private DataManager() {
        //... initialize data manager
    }
    public static DataManager getInstance() {
        if(manager == null) manager = new DataManager();
        return manager;
    }
}

Prefer dependency injection over hardwiring dependencies

public

Avoid unnecessary objects

Eliminate obsolete object references

Avoid finalizers and cleaners

  • Finalizér, tzn. metoda finalize() vlastní všem objektům, může teoreticky být překryta a tím umožněn adekvátní "likvidační" postup při zániku objektu - sestávající obvykle z uvolnění systémových zdrojů - síťové sokety, spojení na databázi atd.

  • V Javě nicméně není zaručeno, že se finalizér skutečně zavolá - JVM jej nemusí volat, pokud nepotřebuje fyzicky uvolnit paměť obsazenou (již nepoužívaným) objektem.

  • I kdyby finalizér zavolán byl, zůstává problém s určením okamžiku, kdy je volán a v jakém pořadí jsou finalizéry na mrtvých objektech volány.

  • Celkově tedy na finalize() nespoléhat a nepoužívat je - zdroje uvolňovat explicitním zavoláním vhodné metody.