Sök…


Adaptermönster (PHP)

Ett verkligt exempel med ett vetenskapligt experiment där vissa rutiner utförs på olika vävnadstyper. Klassen innehåller som standard två funktioner för att få vävnaden eller rutinen separat. I en senare version har vi sedan anpassat den med en ny klass för att lägga till en funktion som får båda. Detta innebär att vi inte har redigerat den ursprungliga koden och därför inte riskerar att bryta vår befintliga klass (och inte göra någon ny test).

class Experiment {
    private $routine;
    private $tissue;
    function __construct($routine_in, $tissue_in) {
        $this->routine = $routine_in;
        $this->tissue  = $tissue_in;
    }
    function getRoutine() {
        return $this->routine;
    }
    function getTissue() {
        return $this->tissue;
    }
}

class ExperimentAdapter {
    private $experiment;
    function __construct(Experiment $experiment_in) {
        $this->experiment = $experiment_in;
    }
    function getRoutineAndTissue() {
        return $this->experiment->getTissue().' ('. $this->experiment->getRoutine().')';
    }
}

Adapter (Java)

Låt oss anta att det finns ett MyLogger gränssnitt i din nuvarande kodbas:

interface MyLogger {
    void logMessage(String message);
    void logException(Throwable exception);
}

Låt oss säga att du har skapat några konkreta implementationer av dessa, till exempel MyFileLogger och MyConsoleLogger .

Du har beslutat att du vill använda ett ramverk för att kontrollera din applikations Bluetooth-anslutning. Detta ramverk innehåller en BluetoothManager med följande konstruktör:

class BluetoothManager {
    private FrameworkLogger logger;

    public BluetoothManager(FrameworkLogger logger) {
        this.logger = logger;
    }
}

BluetoothManager accepterar också en logger, vilket är fantastiskt! Men det förväntar sig en logger vars gränssnitt definierades av ramverket och de har använt överbelastning av metoder istället för att namnge sina funktioner annorlunda:

interface FrameworkLogger {
    void log(String message);
    void log(Throwable exception);
}

Du har redan ett gäng MyLogger implementationer som du vill återanvända, men de passar inte gränssnittet för FrameworkLogger . Det är här adapterns designmönster kommer in:

class FrameworkLoggerAdapter implements FrameworkLogger {
    private MyLogger logger;

    public FrameworkLoggerAdapter(MyLogger logger) {
        this.logger = logger;
    }

    @Override
    public void log(String message) {
        this.logger.logMessage(message);
    }

    @Override
    public void log(Throwable exception) {
        this.logger.logException(exception);
    }
}

Genom att definiera en adapterklass som implementerar FrameworkLogger gränssnittet och accepterar en MyLogger implementering kan funktionaliteten kartläggas mellan de olika gränssnitten. Nu är det möjligt att använda BluetoothManager med alla MyLogger implementationer som så:

FrameworkLogger fileLogger = new FrameworkLoggerAdapter(new MyFileLogger());
BluetoothManager manager = new BluetoothManager(fileLogger);

FrameworkLogger consoleLogger = new FrameworkLoggerAdapter(new MyConsoleLogger());
BluetoothManager manager2 = new BluetoothManager(consoleLogger);

Java-exempel

Ett bra existerande exempel på Adapter-mönstret finns i klasserna SWT MouseListener och MouseAdapter .

MouseListener-gränssnittet ser ut enligt följande:

public interface MouseListener extends SWTEventListener {
    public void mouseDoubleClick(MouseEvent e);
    public void mouseDown(MouseEvent e);
    public void mouseUp(MouseEvent e);
}

Föreställ dig nu ett scenario där du bygger ett användargränssnitt och lägger till dessa lyssnare, men för det mesta bryr du dig inte om någonting annat än när något är klickat med ett enda mus (musUp). Du vill inte ständigt skapa tomma implementationer:

obj.addMouseListener(new MouseListener() {

    @Override
    public void mouseDoubleClick(MouseEvent e) {
    }

    @Override
    public void mouseDown(MouseEvent e) {
    }

    @Override
    public void mouseUp(MouseEvent e) {
        // Do the things
    }

});

Istället kan vi använda MouseAdapter:

public abstract class MouseAdapter implements MouseListener {
    public void mouseDoubleClick(MouseEvent e) { }
    public void mouseDown(MouseEvent e) { }
    public void mouseUp(MouseEvent e) { }
}

Genom att tillhandahålla tomma standardimplementeringar kan vi endast åsidosätta de metoder som vi bryr oss om från adaptern. Följande från ovanstående exempel:

obj.addMouseListener(new MouseAdapter() {

    @Override
    public void mouseUp(MouseEvent e) {
        // Do the things
    }

});

Adapter (UML & exempel situation)

För att använda adaptermönstret och typ av situation när det kan tillämpas mer tänkbart ges här ett litet, enkelt och mycket konkret exempel. Det kommer inte att finnas någon kod här, bara UML och en beskrivning av exempelsituationen och dess problem. Visserligen är UML-innehållet skrivet som Java. (Tja, antydningen av texten "Bra exempel är mestadels kod", jag tror att designmönster är tillräckligt abstrakta för att också kunna introduceras på ett annat sätt.)

Generellt sett är adaptermönstret en tillräcklig lösning för en situation där du har inkompatibla gränssnitt och ingen av dem kan skrivas om direkt.

Föreställ dig att du driver en fin liten pizza-leveransservice. Kunder kan beställa online på din webbplats och du har ett litet system som använder en klass Pizza att representera dina pizzor och beräkna räkningar, skatterapporter och mer. Priset på dina pizzor anges som ett heltal som representerar priset i cent (av valfri valuta).

ange bildbeskrivning här

Din leveransservice fungerar bra, men vid någon tidpunkt kan du inte hantera det växande antalet kunder på egen hand men du vill fortfarande expandera. Du bestämmer dig för att lägga till dina pizzor i menyn för en stor metatjänstleverans online. De erbjuder många olika måltider - inte bara pizzor - så deras system utnyttjar mer abstraktion och har ett gränssnitt IMeal representerar måltider som kommer tillsammans med en klass MoneyAmount representerar pengar.

ange bildbeskrivning här

MoneyAmount består av två heltal som ingång, ett för beloppet (eller någon slumpmässig valuta) före komma, och ett för centbeloppet från 0 till 99 efter komma;

ange bildbeskrivning här

På grund av att priset på din Pizza är ett enda heltal som representerar det totala priset som ett belopp av cent (> 99), är det inte kompatibelt med IMeal . Detta är punkten där adaptermönstret spelas in: Om det skulle kräva för mycket ansträngning att ändra ditt eget system eller skapa ett nytt och du måste implementera ett inkompatibelt gränssnitt, kanske du vill applicera adapterpatten.

Det finns två sätt att applicera mönstret: klassadapter och objektadapter.

Båda har gemensamt att en adapter ( PizzaAdapter ) fungerar som någon form av översättare mellan det nya gränssnittet och adapten ( Pizza i det här exemplet). Adaptern implementerar det nya gränssnittet ( IMeal ) och IMeal antingen från Pizza och konverterar sitt eget pris från ett heltal till två (klassadapter)

ange bildbeskrivning här

eller har ett objekt av typen Pizza som ett attribut och konverterar värdena på det (objektadapter).

ange bildbeskrivning här

Genom att använda adaptermönstret kommer du att "översätta" mellan inkompatibla gränssnitt.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow