Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby on Rails
Code

Ruby / Rails Code Smell-Grundlagen 01

by
Length:LongLanguages:
This post is part of a series called Ruby / Rails Code Smell Basics.
Ruby/Rails Code Smell Basics 02

German (Deutsch) translation by Władysław Łucyszyn (you can also view the original English article)

file

Themen

  • Kopf hoch
  • Widerstand
  • Große Klasse / Gott-Klasse
  • Klasse extrahieren
  • Lange Methode
  • Lange Parameterliste

Kopf hoch

Die folgende kurze Artikelserie ist für erfahrene Ruby-Entwickler und -Starter gedacht.  Ich hatte den Eindruck, dass Codegerüche und deren Refactorings Neulinge sehr entmutigend und einschüchternd sein können - insbesondere wenn sie nicht in der glücklichen Position sind, Mentoren zu haben, die mystische Programmierkonzepte in leuchtende Glühlampen verwandeln können.

Nachdem ich diese Schuhe offensichtlich selbst betreten hatte, fiel mir ein, dass es unnötig neblig war, in Codegerüche und Refactorings zu geraten.

Einerseits erwarten Autoren ein gewisses Maß an Können und fühlen sich daher nicht unbedingt dazu gezwungen, den Leser mit dem gleichen Kontext auszustatten, den ein Neuling möglicherweise früher in diese Welt eintauchen könnte.

Infolgedessen erwecken Neulinge vielleicht den Eindruck, dass sie etwas länger warten sollten, bis sie fortgeschrittener sind, um etwas über Gerüche und Refactorings zu erfahren.  Ich stimme diesem Ansatz nicht zu und denke, dass die Annäherung an dieses Thema ihnen helfen wird, bessere Software früher in ihrer Karriere zu entwickeln.  Zumindest hoffe ich, dass es hilfreich ist, jungen Leuten einen soliden Vorsprung zu bieten.

Worüber sprechen wir genau dann, wenn Leute Codegerüche erwähnen?  Ist es immer ein Problem in Ihrem Code?  Nicht unbedingt!  Kannst du sie vollständig vermeiden?  Ich glaube nicht! Meinen Sie damit, dass Codegerüche zu gebrochenem Code führen?  Nun, manchmal und manchmal nicht.  Sollte es meine Priorität sein, sie sofort zu reparieren?  Gleiche Antwort, fürchte ich: Manchmal ja und manchmal sollten Sie zuerst größere Fische braten.  Bist du verrückt?  Faire Frage an dieser Stelle!

Bevor Sie weiter in dieses ganze stinkende Geschäft eintauchen, denken Sie daran, eine Sache von all dem wegzunehmen: Versuchen Sie nicht, jeden Geruch zu beseitigen, auf den Sie stoßen - dies ist mit Sicherheit eine Verschwendung Ihrer Zeit!

Es scheint mir, dass Code-Gerüche in einem schön beschrifteten Karton etwas schwer einzuwickeln sind.  Es gibt alle Arten von Gerüchen mit verschiedenen Optionen, um sie anzusprechen.  Unterschiedliche Programmiersprachen und Frameworks neigen auch zu unterschiedlichen Gerüchen - aber es gibt definitiv viele übliche „genetische“ Stämme.  Mein Versuch, Codegerüche zu beschreiben, besteht darin, sie mit medizinischen Symptomen zu vergleichen, die Sie auf ein Problem hinweisen.  Sie können auf alle möglichen latenten Probleme hinweisen und haben bei Diagnose eine Vielzahl von Lösungen.

Zum Glück sind sie insgesamt nicht so kompliziert wie der Umgang mit dem menschlichen Körper - und natürlich mit der Psyche.  Es ist jedoch ein fairer Vergleich, da einige dieser Symptome sofort behandelt werden müssen, und andere geben Ihnen ausreichend Zeit, um eine Lösung zu finden, die für das allgemeine Wohlbefinden der Patienten am besten ist.  Wenn Sie Arbeitscode haben und auf etwas stinkendes stoßen, müssen Sie die schwierige Entscheidung treffen, wenn es sich lohnt, einen Fix zu finden, und wenn das Refactoring die Stabilität Ihrer App verbessert.

Wenn Sie jedoch auf Code stoßen, den Sie sofort verbessern können, ist es ratsam, den Code ein wenig besser als zuvor zu belassen - selbst ein kleines bisschen besser summiert sich im Laufe der Zeit erheblich.

Widerstand

Die Qualität Ihres Codes wird fragwürdig, wenn das Einfügen von neuem Code schwieriger wird - wenn Sie beispielsweise entscheiden, wo neuer Code eingefügt werden soll, ist dies ein Problem oder hat viele Auswirkungen auf die gesamte Codebase.  Dies wird Widerstand genannt.

Als Richtlinie für die Codequalität können Sie immer daran messen, wie einfach Änderungen vorgenommen werden können.  Wenn das immer schwieriger wird, ist es definitiv an der Zeit, den letzten Teil von rot-grün-REFACTOR in Zukunft ernster zu gestalten.

Große Klasse / Gott-Klasse

Beginnen wir mit einem etwas ausgefallenen Klang - „God-Klassen“ - weil ich denke, dass sie für Anfänger besonders leicht zu verstehen sind.  Gottesklassen sind ein spezieller Fall eines Codegeruchs namens Große Klasse.  In diesem Abschnitt werde ich beide ansprechen.  Wenn Sie ein wenig Zeit in Rails Land verbracht haben, haben Sie sie wahrscheinlich so oft gesehen, dass sie für Sie normal aussehen.

Sie erinnern sich sicherlich an das Mantra „Fat Models, Skinny Controller“?  Tatsächlich ist Skinny gut für alle diese Klassen, aber als Richtlinie ist es ein guter Rat für Neulinge, denke ich.

Gottesklassen sind Objekte, die wie schwarzes Loch alle Arten von Wissen und Verhalten anziehen.  Zu den üblichen Verdächtigen zählen meistens das Benutzermodell und alle Probleme (hoffentlich!), Die Ihre App zu lösen versucht - in erster Linie zumindest.  Eine Todo-App kann sich auf das Todos-Modell, eine Shopping-App auf Produkte, eine Foto-App auf Fotos konzentrieren - Sie bekommen den Drift.

Die Leute nennen sie Gottheiten, weil sie zu viel wissen.  Sie haben zu viele Verbindungen zu anderen Klassen - meistens, weil sie von ihnen faul modelliert wurden.  Es ist jedoch harte Arbeit, den Gottesunterricht in Schach zu halten.  Sie machen es sehr einfach, mehr Verantwortlichkeiten auf sie zu werfen, und wie viele griechische Helden bezeugen würden, bedarf es einiger Geschicklichkeit, um „Götter“ zu teilen und zu erobern.

Das Problem bei ihnen ist, dass sie immer schwieriger zu verstehen sind, insbesondere für neue Teammitglieder, die schwieriger zu ändern sind, und dass ihre Wiederverwendung immer weniger eine Option ist, je mehr Schwerkraft sie anhäufen.  Oh ja, Sie haben Recht, Ihre Tests sind auch unnötig schwieriger zu schreiben.  Kurz gesagt, es gibt nicht wirklich einen großen Vorteil, wenn man große Klassen und insbesondere Gottesstunden hat.

Es gibt einige häufige Symptome / Anzeichen, dass Ihre Klasse Heroismus / Operation benötigt:

  • Du musst scrollen!
  • Tonnenweise private Methoden?
  • Hat Ihre Klasse sieben oder mehr Methoden?
  • Schwer zu sagen, was Ihre Klasse wirklich macht - prägnant!
  • Hat Ihre Klasse viele Gründe, sich zu ändern, wenn sich Ihr Code weiterentwickelt?

Auch, wenn Sie auf Ihre Klasse blinzeln und denken: „Eh? Ew! ”Vielleicht bist du auch bei etwas dran.  Wenn Ihnen das alles bekannt vorkommt, stehen die Chancen gut, dass Sie ein feines Exemplar gefunden haben.

Hässlicher Typ, was?  Kannst du sehen, wie viel Bosheit hier gebündelt ist?  Natürlich habe ich ein bisschen Kirsche drauf, aber Sie werden früher oder später auf Code wie diesen stoßen.  Überlegen Sie, welche Verantwortlichkeiten diese CastingInviter-Klasse zu erledigen hat.

  • E-Mail senden
  • Überprüfen auf gültige Nachrichten und E-Mail-Adressen
  • Leerraum loswerden
  • Aufteilen von E-Mail-Adressen in Kommas und Semikolons

Sollte das alles auf eine Klasse gekippt werden, die einfach einen Casting-Call per Delivery liefern möchte?  Sicherlich nicht!  Wenn sich Ihre Einladungsmethode ändert, können Sie davon ausgehen, dass Sie in eine Schrotflintenoperation geraten.  CastingInviter muss die meisten dieser Details nicht kennen.  Dies ist eher die Aufgabe einer Klasse, die sich auf den Umgang mit E-Mails spezialisiert hat.  In der Zukunft werden Sie hier auch viele Gründe finden, Ihren Code zu ändern.

Klasse extrahieren

Wie sollen wir damit umgehen?  Das Extrahieren einer Klasse ist oft ein praktisches Refactoring-Muster, das sich als eine vernünftige Lösung für Probleme wie große, verschachtelte Klassen darstellt - insbesondere, wenn die betreffende Klasse mehrere Verantwortlichkeiten behandelt.

Private Methoden sind oft gute Kandidaten und auch leichte Noten.  Manchmal müssen Sie sogar mehr als eine Klasse von einem solchen bösen Jungen extrahieren - tun Sie dies nicht alles in einem Schritt.  Wenn Sie genügend kohärentes Fleisch gefunden haben, das in ein eigenes Spezialobjekt zu gehören scheint, können Sie diese Funktionalität in eine neue Klasse extrahieren.

Sie erstellen eine neue Klasse und verschieben die Funktionalität nach und nach - eine nach der anderen. Verschieben Sie jede Methode einzeln und benennen Sie sie um, wenn Sie einen Grund dafür sehen.  Verweisen Sie dann auf die neue Klasse in der ursprünglichen Klasse und delegieren Sie die erforderliche Funktionalität.  Gut, dass Sie eine Testabdeckung haben (hoffentlich!), Mit der Sie überprüfen können, ob die Dinge bei jedem Schritt noch einwandfrei funktionieren.  Versuchen Sie, Ihre extrahierten Klassen auch wiederzuverwenden.  Es ist einfacher zu sehen, wie es in Aktion ausgeführt wird, also lesen wir etwas Code:

In dieser Lösung können Sie nicht nur sehen, wie sich diese Trennung von Problemen auf Ihre Codequalität auswirkt, sondern auch wesentlich besser lesbar und einfacher zu verstehen.

Hier delegieren wir Methoden an eine neue Klasse, die darauf spezialisiert ist, diese Einladungen per E-Mail zuzustellen.  Sie haben einen dedizierten Ort, der überprüft, ob die Nachrichten und eingeladenen Personen gültig sind und wie sie zugestellt werden müssen.  CastingInviter muss über diese Details nichts wissen, daher delegieren wir diese Verantwortlichkeiten an eine neue Klasse CastingEmailHandler.

Das Wissen, wie diese Casting-Einladungs-E-Mails zugestellt und auf Gültigkeit überprüft werden können, ist jetzt in unserer neuen extrahierten Klasse enthalten.  Haben wir jetzt mehr Code? Sie wetten Lohnte es sich, Bedenken zu trennen?  Ziemlich sicher!  Können wir darüber hinausgehen und CastingEmailHandler noch mehr umgestalten?  Absolut!  Sich selbst ausknocken!

Falls Sie sich über die Gültigkeit wundern?  Diese Methode für CastingEmailHandler und CastingInviter dient RSpec zum Erstellen eines benutzerdefinierten Matchers.  Dies lässt mich etwas schreiben wie:

Ziemlich praktisch, denke ich.

Es gibt mehr Techniken für den Umgang mit großen Klassen / Gegenständen, und im Verlauf dieser Serie werden Sie einige Möglichkeiten kennenlernen, wie Sie solche Objekte umgestalten können.

Es gibt kein festes Rezept für den Umgang mit diesen Fällen - es hängt immer davon ab, und es ist eine Entscheidung von Fall zu Fall, ob Sie die großen Geschütze mitbringen müssen oder ob kleinere, inkrementelle Umgestaltungstechniken das Beste tun.  Ich weiß, manchmal frustrierend.  Das Single-Responsibility-Prinzip (SRP) zu befolgen, wird jedoch einen langen Weg gehen und ist eine gute Nase.

Lange Methode

Methoden zu haben, die ein bisschen groß geworden sind, ist eines der häufigsten Dinge, denen Sie als Entwickler begegnen.  Im Allgemeinen möchten Sie auf einen Blick wissen, was eine Methode tun soll.  Es sollte auch nur eine Verschachtelungsebene oder eine Abstraktionsebene aufweisen.  Kurz gesagt, vermeiden Sie das Schreiben komplizierter Methoden.

Ich weiß, das hört sich hart an und ist es oft.  Eine häufig vorkommende Lösung besteht darin, Teile der Methode in eine oder mehrere neue Funktionen zu extrahieren.  Diese Refactoring-Technik wird als Extraktmethode bezeichnet - sie ist eine der einfachsten, aber dennoch sehr effektiv.  Als netter Nebeneffekt wird Ihr Code besser lesbar, wenn Sie Ihre Methoden entsprechend benennen.

Schauen wir uns die Features an, wo Sie diese Technik sehr brauchen.  Ich erinnere mich an die Einführung der Extraktionsmethode beim Schreiben solcher Feature-Spezifikationen und wie erstaunlich es sich anfühlte, als die Glühbirne weiterging.  Da diese technischen Daten leicht verständlich sind, eignen sie sich gut für Demonstrationszwecke.  Außerdem werden Sie immer wieder in ähnliche Szenarien geraten, wenn Sie Ihre Daten schreiben.

spec/features/some_feature_spec.rb

Wie Sie leicht sehen können, ist in diesem Szenario viel los.  Sie gehen zur Indexseite, melden sich an und erstellen eine Mission für das Setup. Anschließend müssen Sie die Mission als abgeschlossen markieren und schließlich das Verhalten überprüfen.  Keine Raketenwissenschaft, aber auch nicht sauber und definitiv nicht für die Wiederverwendbarkeit komponiert.  Wir können es besser machen:

spec/features/some_feature_spec.rb

Hier haben wir vier Methoden extrahiert, die jetzt in anderen Tests einfach wiederverwendet werden können.  Ich hoffe es ist klar, dass wir drei Fliegen mit einer Klappe schlagen.  Das Feature ist viel übersichtlicher, liest sich besser und besteht aus extrahierten Komponenten ohne Duplizierung.

Stellen Sie sich vor, Sie hätten alle möglichen ähnlichen Szenarien geschrieben, ohne diese Methoden zu extrahieren, und Sie wollten einige Implementierungen ändern.  Jetzt möchten Sie, dass Sie sich die Zeit genommen haben, Ihre Tests umzugestalten, und Sie hätten einen zentralen Platz, um Ihre Änderungen anzuwenden.

Sicher, es gibt einen noch besseren Weg, mit solchen Features wie beispielsweise Seitenobjekten umzugehen, aber das ist heute nicht unser Bereich.  Ich denke, das ist alles, was Sie über das Extrahieren von Methoden wissen müssen.  Sie können dieses Refactoring-Muster überall in Ihrem Code anwenden - natürlich nicht nur in Spezifikationen.  In Bezug auf die Häufigkeit der Nutzung schätze ich, dass es Ihre wichtigste Technik sein wird, um die Qualität Ihres Codes zu verbessern.  Habe Spaß!

Lange Parameterliste

Schließen wir diesen Artikel mit einem Beispiel, wie Sie Ihre Parameter reduzieren können.  Es wird ziemlich schnell langweilig, wenn Sie Ihren Methoden mehr als ein oder zwei Argumente hinzufügen müssen.  Wäre es nicht schön, stattdessen ein Objekt vorzulegen?  Das ist genau das, was Sie tun können, wenn Sie ein Parameterobjekt einführen.

All diese Parameter sind nicht nur schwierig zu schreiben und in Ordnung zu halten, sondern können auch zu Code-Duplizierung führen - und wir möchten dies auf jeden Fall vermeiden, wo immer dies möglich ist.  Was ich besonders an dieser Refactoring-Technik mag, ist, wie sich dies auch auf andere Methoden auswirkt.  Sie sind oft in der Lage, eine Menge Parametermüll in der Nahrungskette zu beseitigen.

Gehen wir dieses einfache Beispiel durch.  M kann eine neue Mission zuweisen und benötigt einen Missionsnamen, einen Agenten und ein Ziel.  M ist auch in der Lage, den Doppelstatus der Agenten zu wechseln, dh die Lizenz zum Töten.

Wenn Sie das betrachten und fragen, was passiert, wenn die Parameter der Mission an Komplexität zunehmen, haben Sie schon etwas vor.  Das ist ein Problem, das Sie nur lösen können, wenn Sie ein einzelnes Objekt übergeben, das alle erforderlichen Informationen enthält.  In den meisten Fällen hilft dies auch dabei, die Methode nicht zu ändern, wenn sich das Parameterobjekt aus irgendeinem Grund ändert.

Also haben wir ein neues Objekt, Mission, erstellt, das sich ausschließlich darauf konzentriert, M die Informationen bereitzustellen, die erforderlich sind, um eine neue Mission zuzuweisen und #assign_new_mission mit einem einzigen Parameterobjekt zu versehen.  Sie müssen diese lästigen Parameter nicht selbst übergeben.  Stattdessen weisen Sie das Objekt an, die Informationen, die Sie in der Methode selbst benötigen, preiszugeben.  Außerdem haben wir einige Verhaltensweisen - die Informationen zum Drucken - in das neue Missionsobjekt extrahiert.

Warum sollte M wissen müssen, wie Missionsaufgaben gedruckt werden?  Das neue #assign profitierte auch von der Extraktion, indem es an Gewicht verlor, da wir das Parameterobjekt nicht übergeben mussten. Daher müssen Sie keine Elemente wie mission.mission_name, mission.agent_name usw. schreiben.  Jetzt verwenden wir einfach unsere attr_reader (s), die viel sauberer sind als ohne Extraktion.  Du gräbst?

Praktisch dabei ist auch, dass Mission alle möglichen zusätzlichen Methoden oder Zustände sammelt, die an einem Ort zusammengefasst sind und für den Zugriff bereit sind.

Mit dieser Technik erhalten Sie Methoden, die übersichtlicher sind, besser lesen und es vermeiden, die gleiche Gruppe von Parametern überall zu wiederholen.  Ziemlich gutes Geschäft!  Die Beseitigung identischer Parametergruppen ist auch eine wichtige Strategie für DRY-Code.

Achten Sie darauf, mehr als nur Ihre Daten zu extrahieren.  Wenn Sie auch Verhalten in der neuen Klasse platzieren können, werden Sie Objekte haben, die nützlicher sind. 

Andernfalls riechen sie auch schnell.Sicher, die meiste Zeit werden Sie mit komplizierteren Versionen davon konfrontiert - und Ihre Tests müssen bei solchen Umgestaltungen sicherlich gleichzeitig angepasst werden -, aber wenn Sie dieses einfache Beispiel in der Hand haben, sind Sie bereit zu handeln.

Ich werde jetzt den neuen Bond sehen.  Ich habe gehört, dass es nicht so gut ist ...

Update: Saw Spectre.  Mein Fazit: Im Vergleich zu Skyfall - das war MEH imho - war Specter Wawawiwa!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.