{"id":1110,"date":"2016-07-26T16:15:54","date_gmt":"2016-07-26T14:15:54","guid":{"rendered":"http:\/\/invidit.de\/blog\/?p=1110"},"modified":"2020-09-21T11:45:14","modified_gmt":"2020-09-21T09:45:14","slug":"so-leicht-wie-eine-fliege","status":"publish","type":"post","link":"https:\/\/invidit.de\/blog\/so-leicht-wie-eine-fliege\/","title":{"rendered":"So leicht wie eine Fliege"},"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>Fliegengewicht <\/strong>(engl. Flyweight) an, der auch aus der Sammlung der Gang of Four stammt.<\/p>\n<h1>Da fliege ich voll drauf<\/h1>\n<p>Das Entwurfsmuster Fliegengewicht ist im heutigen Alltag weiter verbreitet, als m\u00f6glicherweise viele vermuten. Bei diesem Muster geht es darum, ein Objekt mit hohem Ressourcenbedarf in zwei Teile zu gliedern. Ein Teil enth\u00e4lt dabei unver\u00e4nderliche Daten, welche von allen konkreten Auspr\u00e4gungen des zweiten Teils genutzt werden. Der zweite Teil enth\u00e4lt die spezifischen ver\u00e4nderlichen Daten eines konkreten Kontextes.<\/p>\n<h1>Wie funktioniert das Fliegengewicht?<\/h1>\n<p>Stellen wir uns eine Webseite vor, auf der viele Bilder angezeigt werden und dabei auch einige Bilder mehrfach dargestellt werden. Wenn ein Bild &#8211; sagen wir &#8211; 5 mal auf der Seite angezeigt wird, wird es dieses Bild dann 5 mal runter geladen und im Speicher vorr\u00e4tig gehalten? Nein, das l\u00e4uft hier viel leichter. Jedes Bild, egal wie oft auf der Webseite dargestellt, wird nur einmal von der Quelle heruntergeladen und ist nur einmal im Speicher. Dies sind die unver\u00e4nderlichen Daten. Ver\u00e4nderlich hingegen ist die Position, an der das Bild angezeigt wird.<\/p>\n<h1>Ein Beispiel zur Verdeutlichung<\/h1>\n<p>Schauen wir uns wie immer eine Beispielimplementierung an. Zun\u00e4chst ohne das Fliegengewicht Muster und anschlie\u00dfend wenden wir dieses Muster an und pr\u00fcfen die daraus resultierende Ver\u00e4nderung. Messen werden wir die Ver\u00e4nderung anhand des Speicherbedarfs der Testanwendung.<\/p>\n<h2>Ein Schwergewicht<\/h2>\n<p>Erstellen wir eine Klasse f\u00fcr ein Bild mit Positionsangaben anhand von X- und Y-Koordinaten.<\/p>\n<pre class=\"lang:java decode:true\" title=\"Klasse PositionedImage\">@Data\n@Builder\npublic class PositionedImage {\n    private Image image;\n    private int positionX;\n    private int positionY;\n\n    public void print() {\n        \/\/ print image on specified position\n    }\n}<\/pre>\n<p>Bei diesen POJOs oder Entit\u00e4ten bietet sich wieder einmal die Verwendung von Projekt Lombok zur Generierung der \u00fcblichen Methoden an. Mehr dazu findet ihr im Artikel <a href=\"http:\/\/invidit.de\/blog\/lombok-macht-das-schon\/\">http:\/\/invidit.de\/blog\/lombok-macht-das-schon\/<\/a>.<\/p>\n<p>Das Bild muss von der Platte geladen werden womit sich die Erstellung von Objekten der Klasse <em>PositionedImage<\/em> aufwendiger gestaltet. Aus diesen Grund bauen wir eine Fabrik, welche uns fertige Objekte baut.<\/p>\n<pre class=\"lang:java decode:true  \" title=\"Klasse PositionedImageFactory \">public class PositionedImageFactory {\n    public PositionedImage createPositionedImage(String pathToImage, int positionX, int positionY) {\n        BufferedImage image = null;\n        try {\n            image = ImageIO.read(this.getClass().getClassLoader().getResourceAsStream(pathToImage));\n        } catch (IOException e) {\n            System.out.println(\"Image not found\");\n        }\n        return PositionedImage\n                .builder()\n                .image(image)\n                .positionX(positionX)\n                .positionY(positionY)\n                .build();\n    }\n}<\/pre>\n<p>Wir \u00fcbergeben den Pfad zur Bilddatei und die beiden Koordinaten f\u00fcr die Positionierung. Das Bild wird geladen und das fertige Objekt zur\u00fcckgegeben. Konnte das Bild nicht geladen werden, bleibt es Null, was in diesem Beispiel v\u00f6llig in Ordnung ist.<\/p>\n<p>Zur \u00dcberpr\u00fcfung der Speichernutzung ben\u00f6tigen wir noch einen Berichterstatter. Dieser sieht wie folgt aus:<\/p>\n<pre class=\"lang:java decode:true \" title=\"Klasse MemoryUsageReporter \">public class MemoryUsageReporter {\n    private static final long MEGABYTE = 1024L * 1024L;\n\n    public void ReportTo(Consumer&lt;String&gt; reportTo) {\n        Runtime runtime = Runtime.getRuntime();\n        long memory = runtime.totalMemory() - runtime.freeMemory();\n        reportTo.accept(\"Used memory: \" + bytesToMegabytes(memory) + \" MB.\");\n    }\n\n    private long bytesToMegabytes(long bytes) {\n        return bytes \/ MEGABYTE;\n    }\n}<\/pre>\n<p>Dieser bekommt eine Methode, welche einen <em>String<\/em> erwartet und gibt den aktuellen Speicherbedarf dort aus. Der Bedarf wird anhand der Runtime ermittelt und in MB umgerechnet.<\/p>\n<p>In einem Testprogramm f\u00fcr die Kommandozeile stecken wir alle Einzelteile zusammen und laden 100 Bilder in eine Liste. Vorher und nachher geben wir den Speicherbedarf auf die Konsole aus.<\/p>\n<pre class=\"lang:java decode:true \" title=\"Klasse MainWithNonFlyweight \">public class MainWithNonFlyweight {\n    public static void main(String...args) {\n        MemoryUsageReporter memoryUsageReporter = new MemoryUsageReporter();\n        memoryUsageReporter.ReportTo(System.out::println);\n\n        List&lt;PositionedImage&gt; positionedImages = new ArrayList&lt;&gt;();\n        PositionedImageFactory positionedImageFactory = new PositionedImageFactory();\n\n        System.out.println(\"Loading 100 images.\");\n        for (int i = 0; i &lt; 100; i++) {\n            ImagePosition imagePosition = ImagePosition.builder()\n                    .positionX(i)\n                    .positionY(i)\n                    .build();\n            PositionedImage positionedImage = positionedImageFactory.createPositionedImage(\"images\/Fliege.jpg\", imagePosition);\n            positionedImage.print();\n            positionedImages.add(positionedImage);\n        }\n        memoryUsageReporter.ReportTo(System.out::println);\n    }\n}<\/pre>\n<p>So weit zum Schwergewicht.<\/p>\n<h2>Und jetzt mit Fliegengewicht<\/h2>\n<p>Zu Beginn trennen wir den Kontext vom Bild, welches in unserem Beispiel immer gleich bleibt. Dazu erstellen wir die Klasse <em>ImagePosition<\/em>.<\/p>\n<pre class=\"lang:java decode:true \" title=\"Klasse ImagePosition \">@Data\n@Builder\npublic class ImagePosition {\n    private int positionX;\n    private int positionY;\n}<\/pre>\n<p>Das zuk\u00fcnftige Fliegenwicht, also unser Bild, wird \u00fcblicherweise gegen eine abstrakte Klasse oder eine Schnittstelle implementiert und erh\u00e4lt den jeweiligen Kontext. Wir erstellen dazu eine Schnittstelle <em>FlyweightImage<\/em>.<\/p>\n<pre class=\"lang:java decode:true \" title=\"Interface FlyweightImage \">public interface FlyweightImage {\n    void print(ImagePosition imagePosition);\n}<\/pre>\n<p>Unser konkretes Fliegengewicht implementiert nun dieses Interface und enth\u00e4lt das Bild.<\/p>\n<pre class=\"lang:java decode:true \" title=\"Klasse PositionedFlyweightImage \">@Data\n@Builder\npublic class PositionedFlyweightImage implements FlyweightImage {\n    private Image image;\n\n    @Override\n    public void print(ImagePosition imagePosition) {\n        \/\/ print image on specified position\n    }\n}<\/pre>\n<p>Auch in diesem Fall lassen wir eine Fabrik wieder die Arbeit zum Erstellen von Objekten der Klasse <em>PositionedFlyweightImage<\/em> \u00fcbernehmen.<\/p>\n<pre class=\"lang:java decode:true \" title=\"Klasse PositionedFlyweightImageFactory \">public class PositionedFlyweightImageFactory {\n    private Map&lt;String, PositionedFlyweightImage&gt; stringImageMap = new HashMap&lt;&gt;();\n\n    public PositionedFlyweightImage createPositionedFlyweightImage(String pathToImage) {\n        PositionedFlyweightImage flyweightImage = this.stringImageMap.get(pathToImage);\n\n        if (flyweightImage == null) {\n            BufferedImage bufferedImage = null;\n            try {\n                bufferedImage = ImageIO.read(this.getClass().getClassLoader().getResourceAsStream(pathToImage));\n            } catch (IOException e) {\n                System.out.println(\"Image not found\");\n            }\n            flyweightImage = PositionedFlyweightImage\n                    .builder()\n                    .image(bufferedImage)\n                    .build();\n            this.stringImageMap.put(pathToImage, flyweightImage);\n        }\n        return flyweightImage;\n    }\n}<\/pre>\n<p>Jetzt k\u00f6nnte die Frage aufkommen &#8222;Warum hei\u00dft die Klasse denn <em>PositionedFlyweightImageFactory<\/em>? Es wird doch keine Position gesetzt!&#8220;. Genau, die Position wird nicht direkt gesetzt, kommt aber sp\u00e4ter als Kontext des Bildes dazu. Da die Methode <em>print()<\/em> der Klasse <em>PositionedFlyweightImage<\/em> eben genau diesen Kontext erwartet.<\/p>\n<p>Die Besonderheit in dieser Fabrik ist eine HashMap mit bisher geladenen Bildern. Der Schl\u00fcssel ist dabei der vollst\u00e4ndige Dateipfad. Somit wird gew\u00e4hrleistet, dass jedes Bild einmalig geladen wird und nur ein einziges Objekt mit diesem Bild im Speicher existiert.<\/p>\n<p>Die Klasse f\u00fcr den Test des Fliegengewichts sieht sehr \u00e4hnlich zu der ersten Testklasse aus. Die Abweichung resultiert hierbei aus der Trennung der Position und des Bildes. Beide Informationen werden nun separat erzeugt und dann \u00fcber die Methode <em>print()<\/em> kombiniert.<\/p>\n<pre class=\"lang:java decode:true \" title=\"Klasse MainWithFlyweight \">public class MainWithFlyweight {\n    public static void main(String...args) {\n        MemoryUsageReporter memoryUsageReporter = new MemoryUsageReporter();\n        memoryUsageReporter.ReportTo(System.out::println);\n\n        List&lt;PositionedFlyweightImage&gt; positionedFlyweightImages = new ArrayList&lt;&gt;();\n        PositionedFlyweightImageFactory positionedFlyweightImageFactory = new PositionedFlyweightImageFactory();\n\n        System.out.println(\"Loading 100 images.\");\n        for (int i = 0; i &lt; 100; i++) {\n            PositionedFlyweightImage positionedFlyweightImage = positionedFlyweightImageFactory.createPositionedFlyweightImage(\"images\/Fliege.jpg\");\n            ImagePosition imagePosition = ImagePosition.builder()\n                    .positionX(i)\n                    .positionY(i)\n                    .build();\n            positionedFlyweightImage.print(imagePosition);\n            positionedFlyweightImages.add(positionedFlyweightImage);\n        }\n\n        memoryUsageReporter.ReportTo(System.out::println);\n    }\n}<\/pre>\n<p>Und was bringt die Trennung nun tats\u00e4chlich?<\/p>\n<h1>Ergebnisauswertung<\/h1>\n<p>Lasen wir die beiden Main-Klassen laufen und ein paar Objekte erzeugen. Ohne Fliegengewicht sehen wir auf der Konsole diese Speichernutzung:<\/p>\n<pre class=\"lang:default decode:true\" title=\"MainWithNonFlyweight\">Used memory: 4 MB.\nLoading 100 images.\nUsed memory: 1727 MB.\n\nProcess finished with exit code 0<\/pre>\n<p>Wie ihr bei der Ausf\u00fchrung sicherlich bemerkt, dauert das Laden der 100 Bilder recht lange. Da nun 100 Bilder im Speicher gehalten werden ist auch die Speichernutzung entsprechend hoch.<\/p>\n<p>Werfen wir einen Blick auf die Speichernutzung mit dem Fliegengewicht.<\/p>\n<pre class=\"lang:default decode:true\" title=\"MainWithFlyweight\">Used memory: 4 MB.\nLoading 100 images.\nUsed memory: 20 MB.\n\nProcess finished with exit code 0<\/pre>\n<p>Nicht nur die Ausf\u00fchrung geht deutlich schneller, sondern auch der Speicherverbrauch ist signifikant geringer. Warum? Durch die Entkopplung von geladenem Bild (internem Zustand) und der Position (externer Kontext) muss das Bild nur einmalig geladen werden. Wir kombinieren dies dann zur Laufzeit mit den verschiedenen Positionen. Gro\u00dfartig!<\/p>\n<h1>Zusammenfassung<\/h1>\n<p>Wir haben uns das Entwurfsmuster Fliegengewicht angeschaut, mit dessen Hilfe der Ressourcenverbrauch von Software gesenkt werden kann. Die unver\u00e4nderlichen Bestandteile (interner Zustand) wird von den ver\u00e4nderlichen Daten (externer Kontext) getrennt und kann dann wiederverwendet werden. Dazu werden beide Teile zur Laufzeit kombiniert.<\/p>\n<p>Das Fliegengewicht Muster ist in gewisser Weise speziell. Wo seht ihr noch M\u00f6glichkeiten zum Einsatz dieses Musters?<\/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><a href=\"https:\/\/de.wikipedia.org\/wiki\/Fliegengewicht_%28Entwurfsmuster%29\">https:\/\/en.wikipedia.org\/wiki\/Flyweight_pattern<\/a><\/li>\n<\/ul>\n<p>Code-Beispiele auf Github:<\/p>\n<ul>\n<li>https:\/\/github.com\/invidit\/CodeQuality.git<\/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 Fliegengewicht (engl. Flyweight) an, der auch aus der Sammlung der Gang of Four stammt. Da fliege ich voll drauf Das Entwurfsmuster Fliegengewicht ist im heutigen Alltag weiter verbreitet, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1266,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[90],"tags":[129,147,146,141],"_links":{"self":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/1110"}],"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\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/comments?post=1110"}],"version-history":[{"count":29,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/1110\/revisions"}],"predecessor-version":[{"id":1267,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/1110\/revisions\/1267"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media\/1266"}],"wp:attachment":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media?parent=1110"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/categories?post=1110"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/tags?post=1110"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}