Table of Contents

OMO - Factory method a Abstract factory

Účelem je vytvořit “továrnu” na instance, která bude za nás generovat objekty podle aktuálních podmínek - pattern Factory method. Abstract factory je rozšíření, které ještě dovoluje vyměňovat “továrny”.

V našem příkladě máme několik typů logovacích objektů, které chceme poskytovat naší aplikaci, ale tak aby se aplikace samotná, resp. její komponenty, nemusely starat o to, který z nich mají použít.

Logger.java

V takto omezeném příkladě to může být i interface, ale řekněme, že v reálu budou ještě nějaké metody, které chceme dědit.

public abstract class Logger {
    abstract void warning(String msg);
}

EmptyLogger.java

Tenhle logger nevypíše vůbec nic …

class EmptyLogger extends Logger {
    void warning(String msg) {
    }
}

FancyLogger.java

public class FancyLogger extends Logger {
 
    @Override
    void warning(String msg) {
        System.out.println(">>> WARNING: "+ msg + " <<<");
    }
 
}
public class SimpleLogger extends Logger{
    public void warning(String msg){
        System.out.println("Warning:" + msg);
    }
}

LogCreator.java

interface LogCreator {
    public Logger getLoggerFor(String component);
}

LogCreatorDebug.java

Factory pro logování v režimu debug.

public class LogCreatorDebug implements LogCreator {
    public Logger getLoggerFor(String component){
        if (component.equals("core")){
            return new FancyLogger();
        } else {
            return new SimpleLogger();
        }
 
    }
}

LogCreatorRelease.java

Factory pro logování v režimu release.

public class LogCreatorRelease implements LogCreator {
    public Logger getLoggerFor(String component){
        if (component.equals("core")){
            return new SimpleLogger();
        } else {
            return new EmptyLogger();
        }
 
    }
}

Main.java

public class Main {
 
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        LogCreator gen = new LogCreatorRelease();
 
        Logger core = gen.getLoggerFor("core");
        Logger web = gen.getLoggerFor("web");
 
        core.warning("z core");
        web.warning("z webu");
 
        //vyměníme továrnu
        gen = new LogCreatorRelease();
 
        Logger core = gen.getLoggerFor("core");
        Logger web = gen.getLoggerFor("web");
 
        core.warning("z core");
        web.warning("z webu");
    }
}

Pak stačí v programu zpřístupnit, např. pomocí statické proměnné, naši továrnu na loggery. Každá komponenta si k ní přistoupí a řekne si o svůj logger. Komponenta nemusí vůbec nic tušit o tom, jestli je aplikace v módu debug nebo release, protože při spuštění aplikace se správně inicializuje LogCreator na správnou instanci. V tom je výhoda vzoru Abstract Factory.

Tohle řešení má jasnou nevýhodu, pokaždé, když se kterákoliv část zeptá na logger pro stejnou komponentu, vytvoří se nový objekt. To může představovat pro loggery, které zapisují do souboru (stejného). Lepším řešením je vytvořit cache pro vytvářené objekty ve formě mapy, kde klíčem bude jméno komponenty a hodnotou instance loggeru. Nebo lze vše řešit (tam kde je to potřeba - tedy například syslog nebo soubor) pomocí singletonů.