Wenn dein Code „Blinde Kuh“ spielt: Eine Reise durch das Labyrinth des digitalen Spaghetti-Chaos!

Ich betrat zum ersten Mal ein mir unbekanntes Haus und war auf der verzweifelten Suche nach einem Kugelschreiber und einem Blatt Papier.

Das Arbeitszimmer!“, dachte ich, „Ein Schriftsteller wohnt hier, dort wird sicherlich ein Schreibutensil zu finden sein.“

Die Türschilder leiteten mich durch die Räume, und ich machte mich auf den Weg zum beschrifteten „Arbeitszimmer“. Dort erwartete ich den klassischen Anblick eines Schreibtisches, Buchregale und ein paar Notizbücher. Doch was mich hinter der Tür erwartete, war der duftende Innenraum eines gut bestückten Kühlschranks, umgeben von Kochutensilien. Verblüfft verließ ich das vermeintliche Arbeitszimmer und folgte meiner Intuition zur Tür mit dem Schild „Küche“. Doch anstelle eines Herds fand ich ein farbenfrohes Kinderzimmer vor.

Nach einigen weiteren, eher komischen Zimmern und einer kleinen, skurrilen Reise durch das Haus, stolperte ich schließlich im „Gästezimmer“ über den lange gesuchten Schreibtisch. Mit einem erleichterten „Endlich!“ auf den Lippen schreckte ich hoch. Es war nur ein Albtraum gewesen, aber wenigstens mit einem glücklichen Ausgang.

Und die Moral der Geschichte lautet: Die Bezeichnung einer Klasse in der Programmierung sollte präzise widerspiegeln, was sich darin befindet. Ein „Converter“ ist dafür da, zu konvertieren und nicht zu formatieren. Ein „Formatter“ sollte keine Serialisierungsmethoden anbieten, genauso wie ein „DatabaseManager“ sich nicht um die Serialisierung von Daten in XML oder JSON kümmern sollte.

Die Analogien in dieser Kurzgeschichte, in der Reihenfolge ihres Auftretens:

  • Unbekanntes Haus: fremder Code
  • Schriftsteller: Datei- oder Klassen-Name
  • Kugelschreiber und Blatt Papier: spezifische, aber festgelegte Methode(n) mit klar definierten Funktionen
  • Türschilder: Namensräume, Klassen-, Methoden- und Property-Namen
  • Albtraum: Albtraum für den Code-Autor, seine Kollegen, die Nachwelt, die Firma und die Kunden
  • Nachwelt: alle, die nach dem Ausscheiden des Autors aus der Firma in irgendeiner Weise mit dem Code in Berührung kommen.

Naming System & Code lesen

Der Kunde zahlt fürs Code-Schreiben und nicht fürs Code-Lesen!

Um Code schneller lesen und durchsuchen zu können, habe ich im Laufe der Zeit, mein System für die Benennung einige Programmelemente verfeinert:

  • Interfaces beginnen mit „I„, wie: IAuthor, IBook, IPublisher,…

  • Abstrakte Klassen beginnen mit „A„, wie: AAuthor, ABook, APublisher,…

  • Enums beginnen (bei mir) mit „E„, wie: „EOperationMode“, „EError“, „EDeviceType“, „EPeriodKind“,…

  • Helper-Klassen enden mit „Helper„, wie: „FileHelper“, „CommandHelper“, „SecurityHelper“,…

  • Model-Klassen (MVC, MVVM) enden mit „Model„, wie: ServiceModel, MachineModel, ConfigModel, ProductModel, CategoryModel,…

  • DTO-Klassen enden mit „DTO„, wie: PersonDTO, ProductDTO,…

  • POCO-Klassen enden mit „POCO„, wie: PersonPOCO, ProductPOCO,…

  • Delegates enden mit „Handler„, wie: CommandInvokedHandler, ValueChangedHandler, SafeModeActivationChangedHandler, StreamOpeningHandler, StreamOpenedHandler, StreamClosingHandler, StreamClosedHandler, UserLoggedInHandler, UserLoggedOutHandler,…

  • Event-Handling-Methoden beginnen mit „On“ plus Event-Name, und können -wenn passend- mit „ed“ enden, also: „On<EventName>ed„, wie: OnCommandInvoked, OnValueChanged, OnSafeModeActivationChanged, OnStreamOpening, OnStreamOpened, OnStreamClosing, OnStreamClosed, OnUserLoggedIn, OnUserLoggedOut, OnMachineReplaced, OnErrorOccured,…

  • Funktionen/Methode, die etwas Berechnen und eine Zahl zurückgeben beginnen mit „Calc“ oder „Calculate“ (und NIEMALS mit „Get“!), wie: CalculateCircleSurface(…), CalculateStandardDeviation(…) oder kurz: CalcStdDeviation(…)

  • Methoden die asynchron laufen enden mit „Async“, wie: CalcStdDeviationAsync(IEnumerable pTooManyNumbers),…

  • Boolsche Properties beginnen mit „Is„, „Has„, „Use„, „Contains“ udg., wie: „IsEnabled“, „IsReady“, „IsEmpty“, „HasElement“, „ContainsError“, „ContainsNull“, „UseForceMode“, „DoBackup“,…

  • Properties die Exception werfen können, werden NICHT als Properties, sondern mit „Get…()“ und „Set…()“ implementiert (so wie es die C# Sprach-Designer Anders Hejlsberg, Bill Wagner & Co vorgesehen haben), denn ein Property sollte niemals Exceptions werfen, schon gar nicht der Getter. Ausgenommen sind die Indexer (wegen IndexOutOfRangeException).

  • „Get…“ Methoden welche Exceptions abfangen und behandeln enden mit:
    • OrNull“ wenn bei Exception NULL zurückgeliefert wird
    • OrDefault“ wenn bei Exception Default-Wert (z. B. default(int)) zurückgeliefert wird

  • Member-Variablen beginnen mit „m“ oder „m_„, wie: mFirstName oder m_FirstName

  • Parameter beginnen mit „p“ wie: CalcRectSurface(int pWidth, int pHeight), SetTopLeft(int pX, int pY),…

  • Konstanten werden GROSS_GESCHRIEBEN: DEFAULT_VALUE, MAX_VALUE,…

Der Beispiel-Code unten erfüllt die oben erwähnten Regeln.
Versuche herauszufinden: Welcher Name ist ein Property, welcher ein Parameter, welcher eine Member-Variable und welcher eine Konstante:

Konstanten, Parameter, Properties etc. sind eindeutig zu erkennen

Ugly vs Clean Code (zum Aufwärmen)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Ugly.Code
{
    public class GeoCalc
    {
        public double GetCS(double value)
        {

            double val1;
            double calc;


            val1 = value;
            calc = Math.Pow(val1, 2) * Math.PI;


            return calc;


        }

        public double GetSS(double value)
        {

            double val1;
            double calc;


            val1 = value;
            calc = Math.Pow(val1, 2);


            return calc;
        }

        // Some other methods...
    }
}

Warum ist der Code oben „Ugly“?

  1. Ungenutzte „using“ Zeilen
  2. Der Klassen-Name sagt nicht, was die Klasse enthält/anbietet/tut
  3. Die Methoden-Namen sagen nicht, was sie tun oder berechnen/zurückliefern
  4. Die Parameter-Namen sagen nicht, was sie enthalten
  5. Die lokalen Variable-Namen sagen nicht, was sie enthalten
  6. Lokale Variablen kaschieren die Parameter und dessen Werte, machen den Algorithmus schwer verständlich
  7. Unnötige Leerzeilen verlängern unnötig die Methode. Man muss mehr scrollen und mit den Augen rauf & runterschauen.

Die Klasse „GeoCalc“ von Oben, könnte man auch so schreiben:

using System;

namespace Clean.Code
{
    public class GeometryCalculator
    {
        public double CalculateCircleSurface(double radiusLength)
        {
            return radiusLength * radiusLength * Math.PI;
        }


        public double CalculateSquareSurface(double sideLength)
        {
            return sideLength * sideLength;
        }

        // Some other methods...
    }
}