Zoeken…


Adapterpatroon (PHP)

Een voorbeeld uit de echte wereld met behulp van een wetenschappelijk experiment waarbij bepaalde routines worden uitgevoerd op verschillende soorten weefsel. De klasse bevat standaard twee functies om het weefsel of de routine afzonderlijk op te halen. In een latere versie hebben we het vervolgens aangepast met behulp van een nieuwe klasse om een functie toe te voegen die beide krijgt. Dit betekent dat we de oorspronkelijke code niet hebben bewerkt en daarom geen risico lopen onze bestaande klasse te doorbreken (en niet opnieuw te testen).

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)

Laten we aannemen dat er in uw huidige codebase een MyLogger interface bestaat zoals deze:

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

Laten we zeggen dat u een paar concrete implementaties hiervan hebt gemaakt, zoals MyFileLogger en MyConsoleLogger .

U hebt besloten dat u een framework wilt gebruiken voor het beheren van de Bluetooth-connectiviteit van uw applicatie. Dit framework bevat een BluetoothManager met de volgende constructor:

class BluetoothManager {
    private FrameworkLogger logger;

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

De BluetoothManager accepteert ook een logger, wat geweldig is! Het verwacht echter een logger waarvan de interface is gedefinieerd door het framework en ze hebben methodeoverbelasting gebruikt in plaats van hun functies anders te benoemen:

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

U hebt al een aantal MyLogger implementaties die u wilt hergebruiken, maar deze passen niet in de interface van FrameworkLogger . Hier komt het ontwerppatroon van de adapter naar voren:

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);
    }
}

Door een adapterklasse te definiëren die de FrameworkLogger interface implementeert en een MyLogger implementatie accepteert, kan de functionaliteit worden toegewezen tussen de verschillende interfaces. Het is nu mogelijk om BluetoothManager te gebruiken met alle MyLogger implementaties zoals:

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

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

Java-voorbeeld

Een goed bestaand voorbeeld van het adapterpatroon is te vinden in de klassen SWT MouseListener en MouseAdapter .

De interface van MouseListener ziet er als volgt uit:

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

Stel je nu een scenario voor waarin je een gebruikersinterface bouwt en deze luisteraars toevoegt, maar meestal geef je niets om iets anders dan wanneer er op een enkele muisklik wordt geklikt (mouseUp). U wilt niet constant lege implementaties maken:

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
    }

});

In plaats daarvan kunnen we MouseAdapter gebruiken:

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

Door lege, standaardimplementaties te bieden, zijn we vrij om alleen die methoden te negeren waar de adapter om geeft. Naar aanleiding van het bovenstaande voorbeeld:

obj.addMouseListener(new MouseAdapter() {

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

});

Adapter (UML & voorbeeldsituatie)

Om het gebruik van het adapterpatroon en het soort situatie waarin het kan worden toegepast, denkbaarder te maken, wordt hier een klein, eenvoudig en zeer concreet voorbeeld gegeven. Hier zit geen code, alleen UML en een beschrijving van de voorbeeldsituatie en het probleem. Toegegeven, de UML-inhoud is geschreven als Java. (Nou, de hinttekst zei: "Goede voorbeelden zijn meestal code", ik denk dat ontwerppatronen abstract genoeg zijn om ook op een andere manier te worden geïntroduceerd.)

Over het algemeen is het adapterpatroon een adequate oplossing voor een situatie waarin u incompatibele interfaces hebt en geen van deze direct kan worden herschreven.

Stel je voor dat je een leuke kleine pizzabezorgservice hebt. Klanten kunnen online bestellen op uw website en u hebt een klein systeem met behulp van een klasse Pizza om uw pizza's te vertegenwoordigen en rekeningen, belastingrapporten en meer te berekenen. De prijs van uw pizza's wordt gegeven als een geheel getal dat de prijs in cent vertegenwoordigt (van de valuta van uw keuze).

voer hier de afbeeldingsbeschrijving in

Uw bezorgservice werkt geweldig, maar op een gegeven moment kunt u het groeiende aantal klanten niet meer alleen aan, maar u wilt nog steeds uitbreiden. U besluit uw pizza's toe te voegen aan het menu van een grote online meta-bezorgservice. Ze bieden veel verschillende maaltijden - niet alleen pizza's - dus hun systeem maakt meer gebruik van abstractie en heeft een Interface IMeal die maaltijden vertegenwoordigt en een klasse MoneyAmount die geld vertegenwoordigt.

voer hier de afbeeldingsbeschrijving in

MoneyAmount bestaat uit twee gehele getallen als invoer, één voor het bedrag (of een willekeurige valuta) voor de komma en één voor het centbedrag van 0 tot 99 na de komma;

voer hier de afbeeldingsbeschrijving in

Omdat de prijs van uw Pizza een geheel getal is dat de totale prijs als een cent (> 99) vertegenwoordigt, is deze niet compatibel met IMeal . Dit is het punt waarop het adapterpatroon een rol speelt: als het te veel moeite kost om uw eigen systeem te wijzigen of een nieuw systeem te maken en u een incompatibele interface moet implementeren, wilt u misschien de adapterpattern toepassen.

Er zijn twee manieren om het patroon toe te passen: klasse-adapter en object-adapter.

Beide hebben gemeen dat een adapter ( PizzaAdapter ) werkt als een soort vertaler tussen de nieuwe interface en de adaptee ( Pizza in dit voorbeeld). De adapter implementeert de nieuwe interface ( IMeal ) en erft vervolgens van Pizza en converteert zijn eigen prijs van één geheel getal naar twee (klasse-adapter)

voer hier de afbeeldingsbeschrijving in

of heeft een object van het type Pizza als attribuut en converteert de waarden daarvan (objectadapter).

voer hier de afbeeldingsbeschrijving in

Door het adapterpatroon toe te passen, "vertaalt" u een beetje tussen niet-compatibele interfaces.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow