{"id":1050,"date":"2016-04-26T16:15:09","date_gmt":"2016-04-26T14:15:09","guid":{"rendered":"http:\/\/invidit.de\/blog\/?p=1050"},"modified":"2016-04-26T08:44:26","modified_gmt":"2016-04-26T06:44:26","slug":"das-eckige-muss-in-das-runde-aber-das-passt-doch-gar-nicht","status":"publish","type":"post","link":"https:\/\/invidit.de\/blog\/das-eckige-muss-in-das-runde-aber-das-passt-doch-gar-nicht\/","title":{"rendered":"Das Eckige muss in das Runde &#8230; aber das passt doch gar nicht!"},"content":{"rendered":"<p>Hallo Spa\u00df-Coder.<\/p>\n<p>Weiter geht es mit unserer Reihe \u00fcber <em>Entwurfsmuster, <\/em>heute mit einem weiteren Muster aus der Kategorie der <em>Strukturmuster. <\/em>Wir schauen uns in diesem Artikel den <strong>Adapter<\/strong> (engl. Adapter) an, der auch aus der Sammlung der Gang of Four stammt.<\/p>\n<h1>Was ist der Adapter?<\/h1>\n<p>Im Grunde beschreibt der Adapter mit diesem einen Wort seine Funktion schon sehr gut. Woran denkt ihr beim Wort Adapter?<\/p>\n<p>Wahrscheinlich habt ihr gerade in etwa so was im Kopf:<\/p>\n<p><a href=\"http:\/\/invidit.de\/blog\/wp-content\/uploads\/2016\/04\/Adapter.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-1051 size-full\" src=\"http:\/\/invidit.de\/blog\/wp-content\/uploads\/2016\/04\/Adapter.jpg\" alt=\"Adapter\" width=\"390\" height=\"158\" srcset=\"https:\/\/invidit.de\/blog\/wp-content\/uploads\/2016\/04\/Adapter.jpg 390w, https:\/\/invidit.de\/blog\/wp-content\/uploads\/2016\/04\/Adapter-300x122.jpg 300w\" sizes=\"(max-width: 390px) 100vw, 390px\" \/><\/a><\/p>\n<p>Das ist im Grunde genau das, was das Adapter-Muster uns bietet. Vielleicht nicht mit unterschiedlichen Stromvarianten oder Audio-\/Videosignalen, aber f\u00fcr unseren Code.<\/p>\n<p>Wenn wir eine Klasse verwenden m\u00f6chten, dessen Schnittstelle nicht zu unseren Anforderungen passt und wir die vorhandene Klasse nicht \u00e4ndern k\u00f6nnen oder m\u00f6chten, bietet es sich an, die Schnittstelle \u00fcber einen Adapter an unsere Bed\u00fcrfnisse anzupassen. Dar\u00fcber hinaus kann der Adapter dann sinnvoll sein, wenn die Gegenstelle zum Zeitpunkt der Implementierung noch nicht genau bekannt ist. Dann wird die Schnittstelle festgelegt und sp\u00e4ter \u00fcber einen Adapter an &#8211; ggf. unpassende &#8211; Implementierung angepasst. So muss der eigentliche Produktionscode nicht angepasst werden.<\/p>\n<p>&nbsp;<\/p>\n<h1>Wie funktioniert der Adapter?<\/h1>\n<p>Nachdem wir den <a href=\"http:\/\/invidit.de\/blog\/nicht-nur-huebsch-dekoriert-sondern-auch-funktional-erweitert\/\">Decorator <\/a>bereits kennengelernt haben, erkennen wir im Adapter die gleiche Struktur. Wir packen eine Klasse ein, aber diesmal nicht, um sie um neue Funktionen zu erweitern, sondern vielmehr, um ihre eigentliche Schnittstelle zu verbergen und durch eine neue zu ersetzen.<\/p>\n<p>Nehmen wir einmal an, wir programmieren eine Finanzverwaltung, um etwa unsere Ein- und Ausgaben zu dokumentieren und auswerten zu k\u00f6nnen. Wir k\u00f6nnen dabei unter anderem die Daten von Bankkonten abrufen, also Empf\u00e4nger oder Absender einer Zahlung, Soll\/Haben Kennzeichen, den Betrag und den Text der \u00dcberweisung. Mehr brauchen wir nicht.<\/p>\n<p>Allerdings bieten unterschiedliche Banken unterschiedliche Schnittstellen an. Schauen wir uns z.B. mal die Schnittstellen von zwei Banken an:<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<pre class=\"lang:java decode:true\">public interface CBankAccountService {\r\n     public String[][] retrieveDataForAccount(\r\n        String account);\r\n}<\/pre>\n<\/td>\n<td>\n<pre class=\"lang:java decode:true\">public interface SBankAccountService {\r\n     public String retrieveDataForAccountAsJson(\r\n        String account);\r\n}<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Die eine Schnittstelle arbeitet mit einem Array von String-Arrays und die andere mit einem String (der Doku entnehmen wir noch, dass es sich hierbei um eine JSON-Repr\u00e4sentation der Daten handelt).<\/p>\n<p>Wir m\u00f6chten unseren Code nat\u00fcrlich sauber, objektorientiert strukturieren. Die Schnittstelle, welches unsere Anwendung verwendet, sieht folgenderma\u00dfen aus:<\/p>\n<pre class=\"lang:java decode:true \">public interface AccountEnquiry {\r\n    Collection&lt;Accounting&gt; retrieveAccountingData(String account);\r\n}\r\n<\/pre>\n<p>Wir sehen, das passt nicht zusammen. Aber was tun wir?<\/p>\n<p>Hier kommt uns das Adapter-Muster gerade recht. Wir erstellen f\u00fcr jede Schnittstelle (hier die beiden oben genannten Bank-Services) einen Adapter, der &#8230;<\/p>\n<ul>\n<li>unser Interface implementiert<\/li>\n<li>den Service in Form einer Komposition verwendet<\/li>\n<li>ggf. die Datenformate konvertiert, damit sie in die eine oder andere Schnittstelle passen<\/li>\n<\/ul>\n<p>Wie sieht so ein Adapter aus? Schauen wir uns das am Beispiel einmal an:<\/p>\n<pre class=\"lang:java decode:true\">public class AccountEnquiryCBank implements AccountEnquiry{\r\n\r\n    @Override\r\n    public Collection&lt;Accounting&gt; retrieveAccountingData(String accountNo) {\r\n        String[][] accountings = new CBankAccountService().retrieveDataForAccount(accountNo);\r\n\r\n        return convertAccounting(accountings);\r\n    }\r\n\r\n    private Collection&lt;Accounting&gt; convertAccounting(String[][] accountings) {\r\n        ArrayList&lt;Accounting&gt; convertedAccountings = new ArrayList&lt;&gt;();\r\n\r\n        for (String[] accountingSequence : accountings) {\r\n            Accounting accounting = Accounting.builder().Absender(accountingSequence[0])\r\n                    .Empfaenger(accountingSequence[1])\r\n                    .SollHabenKennzeichen(accountingSequence[2].charAt(0))\r\n                    .Betrag(Double.parseDouble(accountingSequence[3]))\r\n                    .Text(accountingSequence[4])\r\n                    .build();\r\n\r\n            convertedAccountings.add(accounting);\r\n        }\r\n        return convertedAccountings;\r\n    }\r\n}<\/pre>\n<p>Alle Anforderungen sind erf\u00fcllt, wir implementieren unsere Schnittstelle, d.h. unsere Anwendung kann diese Klasse verwenden. Wir nutzen den Service der Bank, indem wir ihn erstellen und bereiten zum Schluss die Daten so auf, dass sie in unsere Schnittstelle passen.<\/p>\n<p>Der zweite Adapter ist nun entsprechend einfach, der Aufbau bleibt gleich:<\/p>\n<pre class=\"lang:java decode:true\">public class AccountEnquirySBank implements AccountEnquiry {\r\n\r\n    @Override\r\n    public Collection&lt;Accounting&gt; retrieveAccountingData(String account) {\r\n        String accountingsJson = new SBankAccountService().retrieveDataForAccountAsJson(account);\r\n\r\n        return convertAccounting(accountingsJson);\r\n    }\r\n\r\n    private Collection&lt;Accounting&gt; convertAccounting(String accountingsJson) {\r\n        \/\/ Json conversion to Accounting collection goes here\r\n        return new HashSet&lt;&gt;();\r\n    }\r\n}<\/pre>\n<p>Sicherlich ist der Code in Summe noch zu verbessern, so k\u00f6nnen wir die Konvertierung in eine eigene Klasse auslagern und die Aufrufe von <span style=\"color: #800080;\">new<\/span> f\u00fcr die Services f\u00fchren zu einer engen Kopplung. F\u00fcr diese Problemstellungen haben wir bereits einen Menge M\u00f6glichkeiten kennengelernt (z.B. Dependency Inversion Principle).<\/p>\n<p>Werfen wir zum guten Schluss noch einen Blick auf unsere Finanzverwaltungssoftware. Wie benutzen wir die Adapter denn nun?<\/p>\n<pre class=\"lang:java decode:true\">public class Main {\r\n    public static void main(String...args) {\r\n\r\n        System.out.println(\"Enquiry CBank:\");\r\n        AccountEnquiry enquiry = new AccountEnquiryCBank();\r\n        Collection&lt;Accounting&gt; accountingsCBank = enquiry.retrieveAccountingData(\"4711\");\r\n\r\n        System.out.println(\"Enquiry SBank:\");\r\n        enquiry = new AccountEnquirySBank();\r\n        Collection&lt;Accounting&gt; accountingsSBank = enquiry.retrieveAccountingData(\"4711\");\r\n    }\r\n}<\/pre>\n<p>Auch hier gibt es im Grunde keine gro\u00dfen \u00dcberraschungen. Wir definieren uns eine Variable f\u00fcr unser Interface und erstellen eine Instanz des Adapters, den wir verwenden m\u00f6chten.<\/p>\n<p>F\u00fcr unsere Anwendung ist es dabei egal, welchen Adapter wir verwenden, die R\u00fcckgabewerte sind immer gleich aufgebaut, darum k\u00fcmmert sich der Adapter. Auch logische \u00c4nderungen (z.B. k\u00f6nnten die Buchungen statt Soll-\/Haben einfach positiv\/negativ sein) kann der Adapter vereinheitlichen. So, wie wir das in unserer Anwendung brauchen.<\/p>\n<p>M\u00f6chten wir nun eine weitere Bank in unsere Anwendung integrieren, brauchen wir nur einen neuen Adapter schreiben und schon kann unsere Anwendung damit arbeiten, ohne dass wir sie gro\u00dfartig anpassen m\u00fcssen.<\/p>\n<p>&nbsp;<\/p>\n<h1>Wann ist der Adapter sinnvoll anzuwenden?<\/h1>\n<p>Aus dem Beispiel sollte gut hervorgehen, dass wir den Adapter dann sinnvoll einsetzen k\u00f6nnen, wenn das Interface einer Klasse nicht zum Interface des Verwenders (des Clients) passt, wir die Klasse selbst aber nicht \u00e4ndern k\u00f6nnen oder d\u00fcrfen. Der Adapter ist dabei eine neue Klasse, die sich zwischen die beiden zu verbindenden Klassen setzt und die Schnittstelle zum Client vereinheitlicht. Ggf. \u00fcbernimmt der Adapter dabei auch Aufgaben f\u00fcr die Umwandlung von Datenformaten.<\/p>\n<p>Der Adapter l\u00e4sst sich mit beliebigen Klassen kombinieren, theoretisch k\u00f6nnte man sogar einen Adapter mit einem Adapter versehen. In wie weit das sinnvoll ist, steht auf einem anderen Blatt. Allerdings l\u00e4sst sich der Adapter z.B. problemlos um eine Klasse legen, die wiederum mit dem Decorator-Muster um neue Funktionen erweitert wurde.<\/p>\n<p>Dabei ergibt sich allerdings die gleiche Problematik, wie schon beim Dekorator. Insbesondere mit steigender Anzahl von verwendeten Adaptern ist die Suche nach einem auftretenden Fehler erschwert. Dar\u00fcber hinaus sollte man sich gut \u00fcberlegen, ob man einen Adapter baut. Dies ist ein Indiz (Smell) f\u00fcr schlechtes Design. Hat man die beiden beteiligten Klassen selbst in der Hand, sollte man \u00fcber ein Refactoring nachdenken, statt mit dem Adapter ein Symptom zu bek\u00e4mpfen.<\/p>\n<p>&nbsp;<\/p>\n<h1>Zusammenfassung?<\/h1>\n<p>Wir haben uns in diesem Artikel ein <em>Strukturmuster<\/em> aus der Liste der Entwurfsmuster angeschaut, mit dem wir die Schnittstelle zu anderen Klassen vereinheitlichen lassen, ohne diese \u00e4ndern zu m\u00fcssen. Damit machen wir &#8211; wie schon beim Decorator &#8211; am bestehenden Code nichts kaputt und habe so auch Einfluss auf Klassen, die eigentlich nicht in unserem Einflussbereich liegen.<\/p>\n<p>Habt ihr auch schon mal ein \u00e4hnliches Problem gehabt? Wie habt ihr es gel\u00f6st? W\u00e4re der Adapter die bessere L\u00f6sung gewesen?<\/p>\n<p>Eure Spa\u00df-Coder<\/p>\n<p>Dieser Artikel basiert neben unseren Erfahrungen auf den Ausf\u00fchrungen aus:<\/p>\n<ul>\n<li>https:\/\/de.wikipedia.org\/wiki\/Adapter_%28Entwurfsmuster%29<\/li>\n<li>https:\/\/dzone.com\/articles\/design-patterns-uncovered-0<\/li>\n<\/ul>\n<p>Code-Beispiele auf Github:<\/p>\n<ul>\n<li>https:\/\/github.com\/invidit\/CodeQuality\/tree\/Adapter\/DesignPattern<\/li>\n<\/ul>\n<p>Bilder:<\/p>\n<ul>\n<li>http:\/\/flickr.com\n<ul>\n<li>hainteractive &#8211; Roxio Easy VHS to DVD Adapter<\/li>\n<li>Cathy Liu &#8211; LVSUN exchangeable plugs<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Hallo Spa\u00df-Coder. Weiter geht es mit unserer Reihe \u00fcber Entwurfsmuster, heute mit einem weiteren Muster aus der Kategorie der Strukturmuster. Wir schauen uns in diesem Artikel den Adapter (engl. Adapter) an, der auch aus der Sammlung der Gang of Four stammt. Was ist der Adapter? Im Grunde beschreibt der Adapter mit diesem einen Wort seine [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1052,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[90],"tags":[138,130],"_links":{"self":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/1050"}],"collection":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/comments?post=1050"}],"version-history":[{"count":16,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/1050\/revisions"}],"predecessor-version":[{"id":1088,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/1050\/revisions\/1088"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media\/1052"}],"wp:attachment":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media?parent=1050"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/categories?post=1050"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/tags?post=1050"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}