Keri sisuni

Adapter disainimuster

Adapter on disainimuster, mille abil saame muuta erinevad teineteisest sõltumatud klassid ühtselt kasutatavaks hoolimata nende tegelikust tüübist. Mõelge näiteks telefoni laadimisele. Vahet pole, milline on telefon - me saame selle ikka juhtme abil pistikupessa ühendada. Laadimisjuhe telefoni ja pistikupesa vahel on adapter.

Adapter on väga kasulik disainimuster rakenduste juures, kus välised komponendid on isoleeritud rakenduse tuumfunktsionaalsusest ja neid on vajadusel lihtne välja vahetada. Populaarne näide on logimine, kus tavaliselt kasutatakse juba valmis komponente. Kui selgub, et üks komponent ei sobi - on need siis probleemid jõudlusega või midagi muud - siis vahetatakse see välja mõne teise komponendi vastu. On ka võimalik, et tellija kirjutab meile ette, millist logimise komponenti kasutada tuleb. Vahet pole, mis on põhjus, me peame seda võimaldama ilma, et rakenduse enda kood sellest muutuks.

Sõltumatud komponendid

Oletame, et meil on kasutusel logimise komponent LoggerX, mille me soovime vahetada teise ja täiesti sõltumatu komponendi vastu LoggerY. Mõlemad komponendid pakuvad meile Logger nimelise klassi, mis täidavad sama ülesannet. Et olukord päris elule veel lähemale viia, siis kasutavad klassid erinevate nimedega logimise meetode.

public class LoggerX
{
    public void WriteDebug(string message)
    { 
        Console.WriteLine("DEBUG: " + message);
    }

    public void WriteInfo(string message)
    { 
        Console.WriteLine("INFO: " + message);
    }

    public void WriteWarning(string message)
    { 
        Console.WriteLine("WARNING: " + message);
    }

    public void WriteError(string message)
    { 
        Console.WriteLine("ERROR: " + message);
    }
}

public class LoggerY
{
    public void Debug(string message)
    { 
        Console.WriteLine("DEBUG: " + message);
    }

    public void Info(string message)
    { 
        Console.WriteLine("INFO: " + message);
    }

    public void Warn(string message)
    { 
        Console.WriteLine("WARNING: " + message);
    }

    public void Error(string message)
    { 
        Console.WriteLine("ERROR: " + message);
    }
}

Meie ülesanne on need klassid kuidagi ühe mütsi alla saada selliselt, et me saaksime võimalikult valutult ühe klassi asemel teist kasutada.

var logger = new LoggerX()

// siit algab programm ja loggeri muutused siit edasi mõjuda ei tohi

logger.Info("Program started");

// ...

logger.Info("Program closed");

Defineerime logimise liidese

Selleks, et meie programm teaks logimise komponentidest minimaalselt vähe, defineerime liidese loggerite jaoks.

public interface ILogger
{
    void Debug(string message);
    void Info(string message);
    void Warn(string message);
    void Error(string message);
}

See ei lahenda veel probleemi, kuid meie programmi muudab see loggerist sõltumatumaks, sest logimiseks sobib iga klass, mis ILogger liidest kasutab.

ILogger logger = new SomeLoggingClass()

// siit algab programm ja loggeri muutused siit edasi mõjuda ei tohi

logger.Info("Program started");

// ...

logger.Info("Program closed");

Loome adapterid

Järgmiseks loome adapterklassi kummagi loggeri jaoks. Need klassid kasutavad ILogger liidest ja teisendavad selle meetodid konkreetse loggeri omadeks.

public class LoggerXAdapter : ILogger
{
    private readonly LoggerX _logger;

    public LoggerXAdapter(LoggerX logger)
    {
        _logger = logger;
    }

    public void Debug(string message)
    {
        _logger.WriteDebug(message);
    }

    public void Info(string message)
    {
        _logger.WriteDebug(message);
    }

    public void Warn(string message)
    {
        _logger.WriteWarning(message);
    }

    public void Error(string message)
    {
        _logger.WriteError(message);
    }
}

public class LoggerYAdapter : ILogger
{
    private readonly LoggerY _logger;

    public LoggerXAdapter(LoggerY logger)
    {
        _logger = logger;
    }

    public void Debug(string message)
    {
        _logger.Debug(message);
    }

    public void Info(string message)
    {
        _logger.Info(message);
    }

    public void Warn(string message)
    {
        _logger.Warn(message);
    }

    public void Error(string message)
    {
        _logger.Error(message);
    }
}

Kuna mõlemad adapterklassid kasutavad ILogger liidest, siis saame neid mõlemat kasutada oma programmis ilma muid muudatusi tegemata.

ILogger logger = new LoggerXAdapter()
// logger = new LoggerYAdapter();

// siit algab programm ja loggeri muutused siit edasi mõjuda ei tohi

logger.Info("Program started");

// ...

logger.Info("Program closed");