{"id":261,"date":"2015-04-09T19:45:19","date_gmt":"2015-04-09T17:45:19","guid":{"rendered":"http:\/\/invidit.de\/blog\/?p=261"},"modified":"2022-02-02T09:01:54","modified_gmt":"2022-02-02T07:01:54","slug":"basics-trockenes-einzelstueck","status":"publish","type":"post","link":"https:\/\/invidit.de\/blog\/basics-trockenes-einzelstueck\/","title":{"rendered":"Basics &#8211; Trockenes Einzelst\u00fcck"},"content":{"rendered":"<p>Hallo Spa\u00df-Coder,<\/p>\n<p>im Artikel <a title=\"Basics? Kenn\u2019 ich doch schon\" href=\"http:\/\/invidit.de\/blog\/basics-kenn-ich-doch-schon\/\">Basics? Kenn\u2019 ich doch schon!<\/a> haben wir begr\u00fcndet, warum grundlegende Programmierpraktiken sinnvoll und hilfreich sind.<\/p>\n<p>Eine wichtiger Punkt in der objektorientierten Programmierung ist die Wiederverwendbarkeit unseres Codes. Dar\u00fcber m\u00f6chten wir im Folgenden ein wenig sprechen.<\/p>\n<h3 id=\"don-t-repeat-yourself\">Don&#8217;t Repeat Yourself<\/h3>\n<p>Anders als beim Lernen, wo st\u00e4ndige Wiederholung das zu Lernende festigt, ist es bei der Programmierung so, dass jede Art der Wiederholung sch\u00e4dlich f\u00fcr unseren Code ist. Genauso wie beim Lernen manifestiert sich bei Wiederholungen der Code in den tiefsten Winkeln unserer Projekte. Allerdings nicht mit dem charmanten Vorteil, dass dieses Wissen schnell hervorgeholt werden kann, sondern vielmehr vergessen und explosiv vergraben im Untergrund.<\/p>\n<p>Dieser Code ist gef\u00e4hrlich. Ist es euch nicht auch schonmal passiert, dass ihr ein Programm umstrukturieren musstet um dabei den gleichen Code wieder und wieder anzupassen? Wie viele Stellen habt ihr dabei \u00fcbersehen, die dann sp\u00e4ter zur Laufzeit zu Problemen f\u00fchrten (na komm, seid ehrlich zu euch selbst \ud83d\ude09 )?<\/p>\n<p>Das Prinzip <em>Don&#8217;t Repeat Yourself<\/em> setzt genau an dieser Stelle an. Es besagt, dass eine Umsetzung im Code nur genau einmal (\u201eOnce and only once\u201c) erfolgen soll.<\/p>\n<p><strong>Duplizierter Code == b\u00f6ser Code<\/strong><\/p>\n<p>Das hei\u00dft, dass wir duplizierten Code vermeiden sollen. Wenn wir also Stellen in unserem Code haben, die wir an unterschiedlichen Stellen aber im Grunde genau gleich implementiert haben, sollten wir uns einen Moment Zeit nehmen, und diesen Code refakturisieren, damit wie \u00c4nderungen an diesem Code ebenfalls nur genau einmal durchf\u00fchren m\u00fcssen.<\/p>\n<p>Ich habe mal ein Beispiel aus einem seeeehr alten Projekt von uns ausgegraben. Es handelt sich um die Initialisierungsroutine unseres allerersten Spiels, <strong>Break<span style=\"color: #ff0000;\" data-darkreader-inline-color=\"\"><em>X<\/em><\/span>Press<\/strong> (allerdings die dritte Implementierung, diesmal in .Net und mit neuer Engine).<\/p>\n<pre class=\"wp-block-preformatted\">public override void Initialize()\n{\n\t\/\/ Initialize background sprite\n\tbackground = SpriteManager.AddSprite(@\"Content\\MainScreen\");\n\tbackground.Name = \"Background\";\n\tbackground.Position = new Vector3(camera.X, camera.Y, 0);\n\tthis.mSprites.Add(background);\n\n\tText player1Text = TextManager.AddText(PlayerManager.GetPlayerByIndex(0).Name);\n\tplayer1Text.Position = new Vector3(14f, 13, 0);\n\tplayer1Text.Font = fontKristen;\n\tplayer1Text.Scale = .75f;\n\tplayer1Text.Spacing = .75f;\n\tplayer1Text.ColorOperation = ColorOperation.Modulate;\n\tplayer1Text.Red = PlayerManager.GetPlayerByIndex(0).Color.R;\n\tplayer1Text.Green = PlayerManager.GetPlayerByIndex(0).Color.G;\n\tplayer1Text.Blue = PlayerManager.GetPlayerByIndex(0).Color.B;\n\tthis.mTexts.Add(player1Text);\n\n\tscores[0] = TextManager.AddText(PlayerManager.GetPlayerByIndex(0).Score.ToString());\n\tscores[0].Position = new Vector3(20.5f, 11.5f, 0);\n\tscores[0].Font = fontArial;\n\tscores[0].Scale = .65f;\n\tscores[0].Spacing = .65f;\n\tscores[0].HorizontalAlignment = HorizontalAlignment.Right;\n\tscores[0].ColorOperation = ColorOperation.Modulate;\n\tscores[0].Red = 0;\n\tscores[0].Green = 1;\n\tscores[0].Blue = 0;\n\tthis.mTexts.Add(scores[0]);\n\n\tif (PlayerManager.NumberOfPlayers &gt; 1)\n\t{\n\t\tText player2Text = TextManager.AddText(PlayerManager.GetPlayerByIndex(1).Name);\n\t\tplayer2Text.Position = new Vector3(14f, 6, 0);\n\t\tplayer2Text.Font = fontKristen;\n\t\tplayer2Text.Scale = .75f;\n\t\tplayer2Text.Spacing = .75f;\n\t\tplayer2Text.ColorOperation = ColorOperation.Modulate;\n\t\tplayer2Text.Red = PlayerManager.GetPlayerByIndex(1).Color.R;\n\t\tplayer2Text.Green = PlayerManager.GetPlayerByIndex(1).Color.G;\n\t\tplayer2Text.Blue = PlayerManager.GetPlayerByIndex(1).Color.B;\n\t\tthis.mTexts.Add(player2Text);\n\n\t\tscores[1] = TextManager.AddText(PlayerManager.GetPlayerByIndex(1).Score.ToString());\n\t\tscores[1].Position = new Vector3(20.5f, 4.5f, 0);\n\t\tscores[1].Font = fontArial;\n\t\tscores[1].Scale = .55f;\n\t\tscores[1].Spacing = .55f;\n\t\tscores[1].HorizontalAlignment = HorizontalAlignment.Right;\n\t\tscores[1].ColorOperation = ColorOperation.Modulate;\n\t\tscores[1].Red = 0;\n\t\tscores[1].Green = 1;\n\t\tscores[1].Blue = 0;\n\t\tthis.mTexts.Add(scores[1]);\n\t}\n[...]\n}<\/pre>\n<p>Nehmt euch mal eine oder zwei Minuten um den Code zu lesen und zu verstehen? Was f\u00e4llt euch auf, wenn ihr euch diesen Code anschaut? Richtig, hier ist sehr viel doppelt.<\/p>\n<p>Es wird ein Spieler initialisiert, dann ein Score-Text f\u00fcr diesen Spieler. Und wenn es einen weiteren Spieler gibt, wird der gesamte Code nochmal wiederholt. Lediglich einzelne Positionsangaben sind anders.<\/p>\n<p>F\u00e4llt euch auch der versteckte Fehler auf? In diesem Code wurde eine \u00c4nderung durchgef\u00fchrt, aber nicht an allen Stellen richtig und sauber. Wer genau hinschaut stellt fest, dass der Scale und das Spacing des Score-Objekts beim zweiten Spieler mit .55 festgelegt wird. Beim ersten Spieler aber mit .65. Ob das Absicht war? Sicherlich nicht, warum sollten die Punkte des einen Spielers kleiner angezeigt werden als die des anderen?<\/p>\n<p>Ich habe den Code mal refaktorisiert. So sollte er deutlich besser lesbar und vor allen einfacher &#8211; und fehlerfreier &#8211; zu \u00e4ndern sein:<\/p>\n<pre class=\"wp-block-preformatted\">public override void Initialize()\n{\n    \/\/ Initialize background sprite\n    background = SpriteManager.AddSprite(@\"Content\\MainScreen\");\n    background.Name = \"Background\";\n    background.Position = new Vector3(camera.X, camera.Y, 0);\n    this.mSprites.Add(background);\n\n\tVector3 playerPosition = new Vector3(14f, 13, 0);\n\tint playerIndex = 0;\n\tCreatePlayer(playerIndex, playerPosition);\n\n    if (PlayerManager.NumberOfPlayers &gt; 1)\n    {\n\t\tplayerPosition = new Vector3(14f, 6, 0);\n\t\tplayerIndex = 1;\n\t\tCreatePlayer(playerIndex, playerPosition);\n    }\n[...]\n}\n\nprivate void CreatePlayer(int playerIndex, Vector3 position)\n{\n\tPlayer player = PlayerManager.GetPlayerByIndex(playerIndex);\n\n\tText playerText = TextManager.AddText(player.Name);\n\tplayerText.Position = position;\n\tplayerText.Font = fontKristen;\n\tplayerText.Scale = .75f;\n\tplayerText.Spacing = .75f;\n\tplayerText.ColorOperation = ColorOperation.Modulate;\n\tplayerText.Red = player.Color.R;\n\tplayerText.Green = player.Color.G;\n\tplayerText.Blue = player.Color.B;\n\tthis.mTexts.Add(playerText);\n\n\tscores[playerIndex] = TextManager.AddText(player.Score.ToString());\n\tscores[playerIndex].Position = new Vector3(20.5f, position.Y - 1.5f, 0);\n\tscores[playerIndex].Font = fontArial;\n\tscores[playerIndex].Scale = .65f;\n\tscores[playerIndex].Spacing = .65f;\n\tscores[playerIndex].HorizontalAlignment = HorizontalAlignment.Right;\n\tscores[playerIndex].ColorOperation = ColorOperation.Modulate;\n\tscores[playerIndex].Red = 0;\n\tscores[playerIndex].Green = 1;\n\tscores[playerIndex].Blue = 0;\n\tthis.mTexts.Add(scores[playerIndex]);\n}<\/pre>\n<p>Der Code der Methode <em>CreatePlayer(&#8230;)<\/em> l\u00e4sst sich sicherlich noch optimieren (hier sind vor allem zu viele new-Aufrufe enthalten), aber dem Prinzip DRY sind wir mit diesem Code schon einen sehr gro\u00dfen Schritt n\u00e4her gekommen.<br \/>\nImplizit habe ich auch noch eine andere Wiederholung eliminiert. Im alten Code gab es mehrfach den Aufruf <em>PlayerManager.GetPlayerByIndex(0)<\/em>, etwa um die Farbe oder die Punktzahl des Spielers abzurufen. Diesen Aufruf habe ich nun einmal durchgef\u00fchrt und am Anfang der Methode in einer Variable <em>player<\/em> gespeichert.<em><br \/>\n<\/em><\/p>\n<p>Unterm Strich ist der Code in der Methode <em>Initialize()<\/em> nun deutlich leichter lesbar und der Code der Methode <em>CreatePlayer(&#8230;)<\/em>\u00a0 muss &#8211; im Falle des Falles &#8211; nur einmal ge\u00e4ndert werden.<\/p>\n<p><strong>Duplizierte Algorithmen vermeiden<\/strong><\/p>\n<p>Das gleiche, was generell f\u00fcr unseren Code gilt, gilt auf einer etwas h\u00f6heren Abstraktionsebene auch f\u00fcr Algorithmen. Wenn wir einmal eine State-Machine gebaut haben und brauchen dann &#8211; ggf. sogar in einem anderen Projekt &#8211; ebenfalls eine State-Machine mit den gleichen Anforderungen, w\u00e4re es ein guter Zeitpunkt dar\u00fcber nachzudenken, ob die Implementierung der State-Machine nicht in einer Bibliothek verallgemeinert werden kann um dann von beiden Projekten genutzt zu werden.<\/p>\n<p>Wenigstens, wenn man eine solche Situation in einem Projekt vorfindet, sollte man diese gemeinsamen Konzepte nur einmal implementieren. Es gelten die gleichen Gr\u00fcnde wie auch beim doppelten Code:<\/p>\n<ul>\n<li>\u00c4nderungen m\u00fcssen nur einmal durchgef\u00fchrt werden<\/li>\n<li>Fehler m\u00fcssen nur einmal gefunden und behoben werden<\/li>\n<li>der Algorithmus muss nur einmal getestet werden<\/li>\n<li>Brauche ich oder ein anderer Entwickler eine solche Funktion an anderer Stelle, muss sie nicht nochmal implementiert werden<\/li>\n<\/ul>\n<p><strong>Zusammenfassung<\/strong><br \/>\nWiederholt euch nicht, sagt was ihr zu sagen habt, aber sagt es nur genau einmal &#8211; dann aber richtig und gepr\u00fcft. Dadurch wird das Leben f\u00fcr euch und alle Programmierer die mit eurem Code arbeiten einfacher.<\/p>\n<p>Viel Spa\u00df beim Anwenden<br \/>\nEure Spa\u00df-Coder<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hallo Spa\u00df-Coder, im Artikel Basics? Kenn\u2019 ich doch schon! haben wir begr\u00fcndet, warum grundlegende Programmierpraktiken sinnvoll und hilfreich sind. Eine wichtiger Punkt in der objektorientierten Programmierung ist die Wiederverwendbarkeit unseres Codes. Dar\u00fcber m\u00f6chten wir im Folgenden ein wenig sprechen. Don&#8217;t Repeat Yourself Anders als beim Lernen, wo st\u00e4ndige Wiederholung das zu Lernende festigt, ist es [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":312,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[22,90],"tags":[23,18,40,39,38,41],"_links":{"self":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/261"}],"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=261"}],"version-history":[{"count":21,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/261\/revisions"}],"predecessor-version":[{"id":1295,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/261\/revisions\/1295"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media\/312"}],"wp:attachment":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media?parent=261"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/categories?post=261"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/tags?post=261"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}