A class should by like a ninja

A ninja is a kind specialist, he only has a defined purpose.

A ninja is silent, effective and has always his target in head. 

A ninja disappears if the work is done, and you won‘t know that he was ever here after he has done his work.

You have to give ninjas clear instructions, and the right weapons.

If you give a ninja bad weapons, he will throw something to you when he is using those bad weapons.

He will report fast, if something is wrong.

Defensive Programmierung

Vertraue keinen, validiere alle Eingaben. In C# 7 sind null Prüfungen leider immer noch nötig.

Ich gruppiere Inhalte semantisch, modular. Daher werde ich auch variablen Initialisierung und die dazugehörige Typenprüfung immer zusammen lassen.

Hier ein Beispiel an einem Konstruktor, der einen. Nullceck und eine Validierung beinhaltet, aber wie es aus meiner Sicht nicht sein sollte:

public MyConstructor(string inputString, string validationPattern){
    if (inputString == null){
        throw new ArgumentNullException(name(inputString));
    } 
    if (validationPattern == null){
        throw new ArgumentNullException(name(validationPattern));
    } 
    if (validatinPattern.length == 0){
        throw new FormatException(nameof(validationPattern));
    }
    
    _pattern = validationPattern;
    _input = inputString;
}

Pro

  • Prüfungen / Validierungen sind an einer Stelle und dadurch wirkt der Code auf den ersten Blick simpler / aufgeräumter.
  • Der obere Teil der Methode wird dann immer nur aus den Validierungen bestehen unten sind dann immer nur die Zuweisungen.

Contra

  • Umso mehr Variablen es gibt (grundsätzlich sollten es so wenig möglich sein) desto weiter wird der Abstand zwischen Prüfung und Zuweisung sein.
  • Zuweisung und (fachliche-)Prüfung innerhalb des Beispiel Konstruktors. Sind, ob sie nun direkt zusammen stehen oder nicht, miteinander Verbunden.

Alternative I

Man könnte einfach die Zuweisung und Null-Check in einen Block zusammenziehen und die fachliche Prüfung auslagern:

public MyConstructor(string inputString, string validationPattern){
    if (inputString == null){
        throw new ArgumentNullException(name(inputString));
    } 
    _input = inputString;

    if (validationPattern == null){
        throw new ArgumentNullException(name(validationPattern));
    } 
    _pattern = validationPattern;
}

....

public string Pattern() {
   if(_value.Length == 0){
       throw new FormatException($"{_name} should not be empty");
   }
   return _pattern;
}

Pro

  • Zuweisung und Null-Check sind direkt zusammen, modular.
  • Anzahl der Zeilen Code die voneinander Abhängig sind, sind zusammen.

Contra

  • Der code sieht beim ersten Blick etwas unaufgeräumter aus.
  • Die Überprüfung von _pattern wird nun in der Methode Pattern durchgeführt. Theoretisch muss nun immer bei der Verwendung die Prüfung gemacht werden. Hier besteht nun aber Gefahr von Code Duplikation.

Alternative II

Um code duplication zu vermeiden kann man die Logik in eine Methode auslagern.

private T NullCheckedValue<T>(T value, string name)
{
    if (value == null){
        throw new ArgumentNullException(name);
    }
    return value;
}

private ValidatedPattern(string pattern, string name)
{
   if(_value.Length == 0){
       throw new FormatException($"{name} should not be empty");
   }
   return pattern
}

....

public MyConstructor(string inputString, string validationPattern){
    _input = NullCheckedValue(inputString);
    _pattern = NullCheckedValue(validationPattern);
}

public Pattern() => ValidatedPattern(_pattern);

Pro

  • Bessere Lesbarkeit, Zuweisung und Null-Check an einer Stelle
  • Fachliche Prüfung von Pattern in einer Methode ausgelagert und wieder verwendbar.

Contra

  • Methode ist innerhalb der Klasse wiederverwendbar, jedoch ist grundsätzliche Wiederverwendung nur durch Kopieren der Methode möglich.
  • Eine extra Methode innerhalb der Klasse, die nicht direkt mit dem Ziel der Klasse übereinstimmt.
  • Die Fachliche Prüfung kann nicht unabhängig geprüft werden.

Alternative III

Um die Wiederverwenbarkeit und Testbarkeit zu erhöhen, können hier eigene Klassen erstellt werden.

public class StringParameter
{
    public StringParameter(string value, string name)
    {
        if (value == null){
            throw new ArgumentNullException(name);
        }
        _value = value;
    }
    public string Value() => _value;
}

public class NonEmptyStringParameter
{
    public StringParameter(string value, string name)
    {
        _value = new StringParameter(value, name).Value();
        _name = new StringParameter(
            name,
            name(nameof(name))
        ).Value();
    }

    public string Value() {
        if(_value.Length == 0){
            throw new FormatException($"{name} should not be empty");
        }
    };
}

....

public MyConstructor(string inputString, string validationPattern){
    _input = new StringParameter(inputString, name(inputString)).Value();
    _pattern = new NonEmptyStringParameter(validationPattern).Value();
}

Pro

  • Die Klassen können wiederverwendet werden.
  • Der code ist lesbar und deklarativ.
  • Testbarkeit, Klassen können unabhängig voneinander getestet werden

Contra

  • Mehr Code
  • Im Konstruktur wird ausgeführt, ggf. werden komplexere nicht Prüfungen durchgeführt. Das instanziieren von Klassen sollte so schnell wir nur möglich passieren. Nur die NULL-Prüfung, und nicht mehr, sollte im Konstruktor durchgeführt werden.

Alternative IV

public class StringParameter: IValidatedParameter
{
    private readonly string _value;

    public StringParameter(string value, string name)
    {
        if (value == null){
            throw new ArgumentNullException(name);
        }
        _value = value;
    }
    public string Value() => _value;
}

public class NonEmptyStringParameter: IValidatedParameter<string>
{
    private readonly IValidatedPrameter<string> _value;
    private readonly IValidatedPrameter<string> _name;

    public StringParameter(string value, string name)
    {
        _value = new StringParameter(value, name);
        _name = new StringParameter(
            name,
            name(nameof(name))
        );
    }

    public string Value() {
        if(_value.Length == 0){
            throw new FormatException($"{_name} should not be empty");
        }
        return value;
    };
}

....

public MyConstructor(string inputString, string validationPattern){
    _input = new StringParameter(inputString, name(inputString));
    _pattern = new NonEmptyStringParameter(validationPattern);
}

SSH Verbindung ohne ein Password eingeben zu müssen? (MAC 2 MAC)

Ich muss relative häufig auf meinem Media-Server aufräumen, dazu benutze Midnight Commander und verbinde mich über SSH. Dabei muss man in der Regel immer ein Password eingeben. Es handelt sich aber um einen Rechner der sich in meinem Netzwerk befindet und es ist nicht nötig das Password jedesmal einzugeben. Daher möchte ich gerne darauf verzichten, ohne aber auf Sicherheit zu verzichten.

Dazu habe ich ein einfaches Skript erstellt welche im Terminal ausgeführt werden kann. Einfach „michaelnikolaus@mediapc“ mit euren Werten ersetzen.

ssh-keygen -t rsa && cd ~/.ssh && ssh michaelnikoaus@mediapc mkdir -p .ssh && cat .ssh/id_rsa.pub | ssh michaelnikoaus@mediapc 'cat >> .ssh/authorized_keys' 

Ab sofort muss ich kein Password mehr eingeben. Tip Top!

Indikator #2 – Variablen

So grundlegend Variablen in der Programmierung sind, so falsch kann man sie benutzen.

Es ist nicht überraschend, dass diese oft falsch in den meisten Fällen oder unnötigerweise benutzt werden. Meistens werden sie ähnlich wie Kommentare verwendet. Aber das ist grundlegend falsch.

Eine Variable dient dazu Inhalte für die mehrfache Verwendung einfach zugänglich zu machen. Dabei sollte der Name den nur den Inhalt wieder spiegeln. Fängt man an mehr Variablen zu verwenden, ist dies ein Indikator dafür, das zu viele Dinge gemacht werden die hier gar nicht gemacht werden sollten.

Dinge die man sich merken muss, kosten uns Ressourcen und machen es schwieriger den code zu verstehen.

Aus meiner Sicht können in den seltesten Fällen Variablen dazu verwendet werden den Code lesbarer zu gestalten, das scheint nur auf den ersten Blick so. In der Regel kann der Code so angepasst werden das man keine Variablen benötigt. Lesbarkeit kommt in erster Linie von guten Design.

Beispiele

Wichtig ist zu erkennen was nicht stimmt und das korrigieren zu können. Doch wie erkennt man diese?

Wenn eine Variable verwendet wird um Dinge zu Kürzen

Wenn der benötigte Inhalt innerhalb eines Objektes selektiert wird, dann fängt man oft an Variablen zu verwenden. Beispiel:

var author = Books.Where(e => e.Published()).OrderBy(d => d.PublishingDate()).Author().Name;

Console.WriteLine($"The author of the latest book is: {Author}");

Hier sind gleich mehrere Probleme: Erstens, der Name „author“, ich muss später wissen welche Art von Variable es ist, von Namen her könnte auch eine Author-Klasse sein.

Zweitens, ich benutze die Variable nur einmal. Hmm, aber jetzt das ganze Konstrukt in den ein den WriteLine einzutragen, macht es auch nicht wirklich lesbarer.

Könnte also so aussehen: (Teil I)

Console.WriteLine($"The author of the latest book is: {Books.Where(e => e.Published()).OrderBy(d => d.PublishingDate())
First().Author().Name}");

Okay, aber nein. Das reicht nicht, also warum nicht eine Methode, welche die Selektierung extrahiert?

Also so: (Teil II)

private string LatestPublishedAuthor(List<Book> books)
=> Books
.Where(e => e.Published())
.OrderBy(d => d.PublishingDate())
First()
.Author()
.Name;

Console.WriteLine($"The author of the latest book is: {LatestPublishedAuthor(Books))}");

Es ist wahrscheinlich das diese Logik wiederverwendet wird, aktuell könnte man diese nur in der Klasse benutzen, ggf. will man das aber auch woanders wiederverwenden. Daher, warum nicht, eine eigene Klasse: (Teil III)

public class LatestPublishedBook: IBook
{
public LatestPublishedBook(IEnumerable<IBook> books) {
_origin = book;
}
private IBook Value()
=> _origin
.Where(e => e.Published())
.OrderBy(d => d.PublishingDate())
.First();

public Author Author => Value().Author;
}

Console.WriteLine($"The author of the latest book is: {new LatestPublishedBook(Books).Author())}");

Ja, es ist mehr Code, da stimmt ich zu. Aber es:

  1. Deklarative, sie sagt aus was es ist.
  2. Sauberer, keine Variablen.
  3. Wiederverwendar,die Klasse kann einfach wiederverwendet werden.
  4. Testbarkeit,als Klasse kann dies nun einfach getestet und ausgetauscht werden.