IT-Berufe-Podcast

IT-Berufe-Podcast

Download on the App Store

IT-Berufe-Podcast episodes

  • Dependency Inversion Principle (DIP) – Wissenshäppchen #7
    Im siebten Wissenshäppchen geht es um das Dependency Inversion Principle.
    TL;DR
    Mit dem Dependency Inversion Principle, dem D aus SOLID, richte ich Abhängigkeiten auf abstrakte Schnittstellen aus. Die Geschäftslogik und ihre technischen Komponenten hängen dadurch von Abstraktionen ab, wobei konkrete Implementierungen von außen übergeben werden. So kannst du beispielsweise eine Datenbank austauschen oder im Test durch ein Fake-Objekt ersetzen, während die Geschäftslogik unverändert bleibt. Zusätzliche Schnittstellen und das Zusammensetzen der Objekte erhöhen den Aufwand, weshalb ich passende Stellen für diese Abstraktionen gezielt auswähle.
    Kapitelmarken
    [00:00:00] Einstieg ins Dependency Inversion Principle
    [00:00:50] Definition und Richtung der Abhängigkeiten
    [00:01:46] Geschäftslogik und Datenbank in der Schichtenarchitektur
    [00:02:30] Feste Abhängigkeiten beim Testen und Austauschen
    [00:02:55] Schnittstellen und von außen übergebene Datenbanken
    [00:04:55] Änderungen an technischen Komponenten entkoppeln
    [00:05:46] Abhängigkeiten im Test durch Fake-Objekte ersetzen
    [00:07:06] Ruby-Codebeispiel mit Payroll und Datenbank
    [00:08:16] Abhängigkeiten durch Konstruktorparameter sichtbar machen
    [00:09:45] Vorteile und das Open-Closed-Principle
    [00:10:58] Over-Engineering und YAGNI
    [00:12:28] Sinnvolle Stellen für Abstraktionen auswählen
    [00:13:09] Objekte zusammensetzen und Dependency-Injection-Container
    [00:14:36] Praktische Hinweise zu Schnittstellen und Testbarkeit
    [00:16:03] Infrastruktur im Test austauschbar machen
    [00:17:08] Shownotes und Abschluss der SOLID-Reihe
    Inhalt
    Das DIP ist das letzte der fünf SOLID-Prinzipien.
    High level modules should not depend upon low level modules. Both should depend upon abstractions.
    Oder:
    Abstractions should not depend upon details. Details should depend upon abstractions.
    Welche Abhängigkeiten werden hier "umgedreht"? Komponenten, die andere Komponenten benötigen, erzeugen sich diese nicht selbst, sondern bekommen sie von außen hineingegeben. Braucht ein Service beispielsweise ein Repository um arbeiten zu können, erzeugt er es sich nicht selbst, z.B. mit this.repo = new Repository() in seinem Konstruktor, sondern bekommt es stattdessen schon fertig als Parameter übergeben, z.B. public Service(Repository repo). Das heißt, obwohl die Abhängigkeit vom Service zum Repository geht, wird sie "umgekehrt" aufgelöst, indem das Repository dem Service übergeben wird.
    Erklärung
    Wenn Komponenten sich ihre Abhängigkeiten selbst erzeugen, sind diese später nur umständlich oder gar nicht mehr austauschbar.
    Tests von Komponenten, die sich ihre Abhängigkeiten selbst erzeugen, sind schwierig/unmöglich/langsam/fehleranfällig, da die Abhängigkeiten nicht so einfach gegen Fake-Objekte ausgetauscht werden können.
    Die Abhängigkeiten sind nicht explizit in der API sichtbar. Der obige Service "braucht" ein Repository, aber das sieht man nur, wenn man sich den Quelltext von Service anschaut. Beim Erzeugen eines Services ist dies nicht offensichtlich, weil er sich das Repository "heimlich" selbst erzeugt.
    Beispiel
    Um alle Employees zu bezahlen, erzeugt sich die Payroll ihr eigenes MySqlEmployeeRepository. Dadurch wird auch im Test die echte Datenbank verwendet und ein Austauschen gegen eine Oracle-Datenbank erfordert eine Anpassung der Klasse Payroll (vgl. Open Closed Principle).
    class Payroll
    def initialize
    @repo = MySqlEmployeeRepository.new
    end
    def pay_employees
    @repo.find_all.each { |employee| employee.pay }
    end
    end
    class MySqlEmployeeRepository
    def find_all
    puts "Accessing the database and reading all employees"
    [Employee.new] * 10
    end
    end
    class Employee
    def pay
    puts "Thank you for paying me"
    end
    end
    Payroll.new.pay_employees
    # Accessing the database and reading all employees
    # Thank you for paying me
    # ...
    # Thank you for paying me
    Statt dieser "harten" und versteckten Abhängigkeit sollte Payroll sich lieber die Implementierung eines Repositorys liefern lassen. Das erfordert nur eine kleine Umstrukturierung des Codes und ermöglicht deutlich einfachere Tests.
    class Payroll
    def initialize(repo)
    @repo = repo
    end
    def pay_employees
    @repo.find_all.each { |employee| employee.pay }
    end
    end
    class MySqlEmployeeRepository
    def find_all
    puts "Accessing the database and reading all employees"
    [Employee.new] * 10
    end
    end
    class FakeEmployeeRepository
    def find_all
    [Employee.new] * 1
    end
    end
    class Employee
    def pay
    puts "Thank you for paying me"
    end
    end
    puts "Production:"
    Payroll.new(MySqlEmployeeRepository.new).pay_employees
    puts "Test:"
    Payroll.new(FakeEmployeeRepository.new).pay_employees
    # Production:
    # Accessing the database and reading all employees
    # Thank you for paying me
    # ...
    # Thank you for paying me
    # Test:
    # Thank you for paying me
    Außerdem kann Payroll nun auch ohne Anpassungen (!) mit einer Oracle-Datenbank arbeiten.
    class OracleEmployeeRepository
    def find_all
    puts "Reading employees from Oracle database"
    [Employee.new] * 100
    end
    end
    puts "Using Oracle:"
    Payroll.new(OracleEmployeeRepository.new).pay_employees
    # Reading employees from Oracle database
    # Thank you for paying me
    # ...
    # Thank you for paying me
    Vorteile
    Abhängigkeiten sind explizit sichtbar und können bei geänderten Anforderungen oder in Tests einfach ausgetauscht werden.
    Neue Funktionalitäten können dem Programm einfacher hinzugefügt werden, da durch die Abhängigkeit von Abstraktionen oftmals auch das OCP umgesetzt wird.
    Nachteile
    Ein Problem könnte wieder ein "Overengineering" sein. Es ist wahrscheinlich nicht sinnvoll, jede Kleinigkeit hinter einer Abstraktion zu verbergen. Aber wie entscheidet man, welche Abstraktion sinnvoll ist?
    Bei vielen Abhängigkeiten wird das Erstellen von Objekten (und damit auch deren Test) schwierig, weil viel Arbeit vorab erledigt werden muss. Hier könnte dann ein DI-Container zum Einsatz kommen.
    Literaturempfehlungen
    Wie sollte es anders sein: Auch für das letzte SOLID-Prinzip empfehle ich noch einmal Clean Code* von Uncle Bob.
    *
    Links
    Permalink zu dieser Podcast-Episode
    RSS-Feed des Podcasts
    Dependency Inversion Principle
    ArticleS.UncleBob.PrinciplesOfOod (Artikel von Uncle Bob, Beispiel in C++)
    Dependency-Inversion-Prinzip – Wikipedia
    Das Dependency Inversion Principle | Informatik Aktuell (Deutsches Beispiel in C#)
    Zusammenfassung
    Abhängigkeiten auf Schnittstellen ausrichten
    Beim Dependency Inversion Principle (DIP), dem letzten der fünf SOLID-Prinzipien, hängen höher angesiedelte Module wie meine Geschäftslogik und technische Module wie eine Datenbankanbindung von Abstraktionen ab. Die konkreten technischen Details richten sich ebenfalls nach diesen Abstraktionen. Damit wird die Richtung der Abhängigkeiten umgedreht.
    In einer klassischen Schichtenarchitektur hängt meine Geschäftslogik häufig direkt von einer konkreten Datenbankklasse ab. Steht dort ausdrücklich eine MySQL-Klasse, zieht ein Wechsel zu Oracle Änderungen am Business-Code nach sich. Solche Änderungen können Fehler verursachen. Auch technische Änderungen an der Datenbankklasse können sich bis zur Geschäftslogik auswirken und erneutes Kompilieren erforderlich machen.
    Eine Datenbank von außen übergeben
    Für meine Geschäftslogik definiere ich eine Schnittstelle wie IDatabase, die die benötigten Funktionen anbietet. Eine Basisklasse kann ebenfalls als Abstraktion dienen. Die Business-Klasse erhält von außen ein Objekt, das diese Schnittstelle implementiert.
    Damit kannst du verschiedene Datenbankklassen einsetzen:
    Eine MySQL-Klasse implementiert die vereinbarte Schnittstelle und stellt die Verbindung zu MySQL her.
    Für Oracle schreibst du eine weitere Klasse mit derselben Schnittstelle, die du anschließend an die Geschäftslogik übergibst.
    Die Business-Klasse kennt dabei die gemeinsame Schnittstelle. Solange diese stabil bleibt, können Änderungen an der konkreten Datenbankklasse getrennt bleiben; unter Umständen muss dann nur diese Klasse neu kompiliert werden. Bei einem Interface prüft der Compiler, ob das übergebene Objekt die Schnittstelle implementiert.
    Geschäftslogik gezielt testen
    Wenn meine Business-Klasse selbst eine echte Datenbankverbindung erzeugt, braucht ihr Test diese Datenbank ebenfalls. Dadurch können Tests langsam oder fehleranfällig werden und bei fehlender Datenbank scheitern.
    Mit einer übergebenen Abhängigkeit kannst du im Test ein Fake-Objekt einsetzen, das die passende Schnittstelle anbietet und festgelegte Werte zurückliefert. Auch Stubs oder Mocks können Abhängigkeiten ersetzen. So lässt sich die eigentliche Geschäftslogik gezielt prüfen, während die beteiligten technischen Komponenten durch Testobjekte abgedeckt werden.
    In meinem Ruby-Beispiel erhält eine Payroll-Klasse, die Mitarbeiter:innen bezahlt, ihre Datenbank von außen. Im Test übergebe ich eine Fake-Datenbank, die genau eine:n Mitarbeiter:in liefert. Ruby hat als dynamisch typisierte Sprache keine Interfaces; das Prinzip funktioniert dort über Objekte, die dieselbe Schnittstelle anbieten.
    Abhängigkeiten sichtbar machen
    Über einen Konstruktorparameter mache ich sichtbar, welche Objekte meine Klasse braucht. Du erkennst bereits beim Erzeugen einer Payroll, dass du eine Datenbank bereitstellen musst und in welcher Reihenfolge die Objekte entstehen sollen.
    Werden benötigte Objekte innerhalb von Methoden erzeugt, musst du diese Methoden lesen, um die Abhängigkeiten zu entdecken. Ein Konstruktorparameter zeigt dir den Bedarf direkt und hilft beim Schreiben von Tests sowie beim Arbeiten am produktiven Code.
    Bestehenden Code erweitern
    Durch eine neue Klasse, die das vorhandene Datenbankinterface implementiert, kann meine Payroll auch mit einer weiteren Datenbank arbeiten. Die bestehende Klasse bleibt dabei unverändert. Das unterstützt das Open-Closed-Principle, nach dem sich Verhalten erweitern lässt,...
    19 min
  • Interface Segregation Principle (ISP) – Wissenshäppchen #6
    Das sechste Wissenshäppchen hat das Interface Segregation Principle als Thema.
    TL;DR
    Mit dem Interface Segregation Principle (ISP) halte ich die Schnittstellen, von denen mein Code abhängt, so klein, wie es die jeweilige Aufgabe erlaubt. Am Beispiel von List und Iterable in Java zeige ich dir, wie eine passende Schnittstelle den Zugriff auf Methoden begrenzt und deinen Code für weitere Datenstrukturen öffnet. Kleine Interfaces erleichtern Tests und verringern den Aufwand, wenn sich andere Teile des Codes ändern. Dabei entstehen mehr einzelne Schnittstellen, die sich mit modernen IDEs gezielt finden lassen.
    Kapitelmarken
    [00:00:20] Das vierte SOLID-Prinzip: Interface Segregation
    [00:00:44] Kleine Interfaces und passende Abhängigkeiten
    [00:01:47] Große Interfaces und das Liskov Substitution Principle
    [00:02:35] Beispiel: Eine Liste durchlaufen
    [00:04:12] Iterable als passende Schnittstelle
    [00:05:25] Fachliche Aufgaben trennen und das Single Responsibility Principle
    [00:06:42] Mehrere kleine Interfaces in einer Klasse implementieren
    [00:07:32] Änderungen an ungebrauchten Methoden und erneutes Kompilieren
    [00:08:55] Einfachere Tests mit kleinen Interfaces
    [00:10:06] Duck Typing und explizite Abhängigkeiten
    [00:11:45] Vorteile kleiner Schnittstellen
    [00:12:46] Mehr Komponenten und der Umgang mit vielen Dateien
    [00:13:59] Schnittstellen klein halten
    [00:14:55] Ausblick, Codebeispiel und weiterführende Links
    Inhalt
    Das ISP ist das vierte SOLID-Prinzip.
    The dependency of one class to another one should depend on the smallest possible interface.
    Im Prinzip kann man das ISP so zusammenfassen: Verwende immer nur die kleinstmögliche Schnittstelle zu deinen Abhängigkeiten. Je mehr Funktionen eine Komponente an ihren Abhängigkeiten aufrufen kann, desto abhängiger wird sie von ihr. Wenn sich diese Funktionen nämlich ändern (z.B. die Signatur einer Methode), muss die nutzende Komponente neu kompiliert werden. Außerdem können Funktionen aufgerufen werden, die die nutzende Komponente weder benötigt, noch anwenden soll, z.B. die clear()-Methode einer Liste, die sie eigentlich nur durchlaufen soll. Zuletzt müssen auch implementierende Klassen von zu großen Interfaces für sie unnötige Methoden implementieren, nur um der Schnittstelle zu entsprechen.
    Erklärung
    Zu große Interfaces oder Basisklassen bieten evtl. Zugriff auf verschiedene Funktionalitäten, die nicht zusammengehören (z.B. Repository.getUser() und Repository.getArticle()), oder über das benötigte/erlaubte Verhalten hinausgehen (z.B. Repository.deleteUser()).
    Komponenten, die von diesen Schnittstellen abhängen, können mit ihnen "zu viel" machen und müssen angepasst (mind. rekompiliert) werden, wenn sich diese ändern, auch wenn sie die geänderte Funktionalität gar nicht nutzen.
    Komponenten, die die vorgegebenen Schnittstellen anbieten möchten, müssen mehr implementieren, als sie eigentlich wollen (oder dies nicht tun und das LSP mit einer NotImplementedException verletzen).
    Tests von Komponenten, die Abhängigkeiten auf große Schnittstellen haben, werden schwieriger, da viele eigentlich unnötige Funktionen gestubbt oder gemockt werden müssen.
    Auch eine "große" Klasse kann mehrere "kleine" Interfaces anbieten und damit wohldefinierte "Fenster" auf ihre Funktionalität anbieten.
    Beispiel
    Die Methode printEmployees() im folgenden Beispiel erwartet eine List, obwohl sie diese lediglich durchlaufen können muss. Durch das "zu große" Interface kann sie darüber hinaus jetzt auch die Liste leeren, was für den Aufrufer unschöne Konsequenzen hat.
    public class ISP
    {
    public static void main(String[] args)
    {
    List employees = new ArrayList<>();
    employees.add(new Employee("Stefan", "Macke"));
    employees.add(new Employee("Karl", "Meier"));
    employees.add(new Employee("Hans", "Georg"));
    printEmployees(employees);
    System.out.println(employees.size());
    }
    private static void printEmployees(List employees)
    {
    for (var employee : employees)
    {
    System.out.println(employee);
    }
    employees.clear();
    }
    }
    // Ausgabe:
    // Stefan Macke
    // Karl Meier
    // Hans Georg
    // 0
    Die Methode sollte stattdessen das kleinstmögliche Interface benutzen, das sie braucht, um ihre Aufgabe erfüllen zu können. In Java wäre das z.B. ´Iterable´:
    private static void printEmployees(Iterable employees)
    {
    for (var employee : employees)
    {
    System.out.println(employee);
    }
    }
    Der restliche Code funktioniert wie vorher, aber es sind nun keine modifizierenden Aufrufe mehr möglich, da das kleinere Interface die Methoden nicht anbietet.
    Dynamisch typisierte Sprachen haben hier einen Vorteil, da sie dank Duck-Typing ohnehin kaum Basisklassen oder gar Interfaces benötigen. Sie rufen einfach nur die Methoden auf, die sie brauchen. Das kann allerdings auch zu impliziten Abhängigkeiten führen, die nicht vom Compiler geprüft werden, da der Zugriff auf die Klasse nicht durch ein Interface "geschützt" ist.
    Vorteile
    Kleinere Komponenten machen uns das Entwicklerleben einfach: klare Zuständigkeiten, einfachere Tests, weniger unnötige Abhängigkeiten.
    Durch klar definierte Schnittstellen wird die fehlerhafte oder unerlaubte Verwendung von Funktionen verhindert.
    Kleinere Schnittstellen entsprechen dem Single Responsibility Principle.
    Nachteile
    Insgesamt steigt die Anzahl der Typen im System durch kleine Schnittstellen an (siehe auch SRP).
    Literaturempfehlungen
    Es wird schon langweilig: Auch beim vierten SOLID-Prinzip empfehle ich wieder Uncle Bobs Clean Code*.
    *
    Links
    Permalink zu dieser Podcast-Episode
    RSS-Feed des Podcasts
    Interface Segregation Principle
    ArticleS.UncleBob.PrinciplesOfOod (Artikel von Uncle Bob, Beispiel in C++)
    Interface-Segregation-Prinzip – Wikipedia
    Das Interface Segregation Principle (Deutsches Beispiel in C#)
    Zusammenfassung
    Was das Interface Segregation Principle bedeutet
    Wenn ich eine Schnittstelle für meinen Code auswähle, soll sie genau die Methoden anbieten, die er für seine Aufgabe braucht. Das Interface Segregation Principle (ISP) ist das vierte der fünf SOLID-Prinzipien. Es fordert, die Schnittstelle zwischen einer Klasse und ihrer Abhängigkeit möglichst klein zu halten.
    Für dich bedeutet das: Wähle ein Interface, dessen Methoden zur jeweiligen Aufgabe passen. Jede zusätzliche Methode erweitert die Möglichkeiten des aufrufenden Codes und schafft eine weitere Abhängigkeit.
    Beispiel: Eine Liste durchlaufen
    Ich möchte eine Liste mit Mitarbeiter:innen durchlaufen und ihre Daten ausgeben. Erwartet meine Methode dafür das Java-Interface List, erhält sie auch Zugriff auf Methoden wie clear(). Damit könnte sie die übergebene Liste leeren, obwohl ihre Aufgabe lediglich darin besteht, die Elemente zu durchlaufen. Außerdem erschweren die vielen angebotenen Methoden den Überblick.
    Für diese Aufgabe reicht Iterable aus. Über dessen Methode iterator() erhält mein Code den Iterator, den er zum Durchlaufen braucht. Die Schnittstelle bietet ihm damit genau den passenden Zugriff.
    Dadurch kann ich auch andere Objekte übergeben, die Iterable implementieren. Eine selbst geschriebene Collection-Klasse muss dafür lediglich die entsprechende Methode anbieten. Mein Code kann so mit weiteren Datenstrukturen arbeiten.
    Fachlich passende Schnittstellen bilden
    Bei einem Repository, das Benutzer:innen und Artikel sowie Bestellungen aus der Datenbank lädt, treffen mehrere fachliche Aufgaben zusammen. Code, der eine Bestellung bearbeitet, erhält über eine gemeinsame Schnittstelle möglicherweise auch Zugriff auf Methoden für Benutzer:innen.
    Solche großen Interfaces können darauf hinweisen, dass das Single Responsibility Principle verletzt wird. Die Methoden gehören dann zu unterschiedlichen Aufgaben und lassen sich auf kleinere Schnittstellen verteilen. Das ISP betrifft auch Basisklassen, die zu viele Funktionen anbieten.
    Ich kann für die einzelnen Aufgaben eigene Interfaces definieren und sie weiterhin in einer gemeinsamen Repository-Klasse implementieren. Der aufrufende Code erwartet jeweils die fachlich passende Schnittstelle. Sie wirkt wie ein kleines Fenster auf die Klasse, durch das nur die benötigten Methoden sichtbar sind.
    Was große Interfaces nach sich ziehen
    Wenn sich eine Methode einer großen Schnittstelle ändert, kann davon auch Code betroffen sein, der andere Methoden dieser Schnittstelle aufruft. Im Repository-Beispiel muss der Code für Artikel unter Umständen erneut kompiliert werden, sobald sich die gemeinsame Schnittstelle für Benutzer:innen ändert. Getrennte Interfaces begrenzen solche Abhängigkeiten und den damit verbundenen Aufwand.
    Auch die Klasse, die ein Interface implementiert, muss alle vorgegebenen Methoden anbieten. Braucht sie fachlich nur einen Teil davon, entstehen zusätzliche Methoden ohne sinnvolle Aufgabe. Wer dort lediglich Exceptions wirft, riskiert einen Verstoß gegen das Liskov Substitution Principle: Der Compiler erkennt die Methode, beim Aufruf zur Laufzeit scheitert sie.
    Tests mit kleinen Interfaces
    Wenn ich eine Abhängigkeit im Test durch einen selbst geschriebenen Stub oder Mock ersetze, muss dieser alle Methoden des Interfaces implementieren. Bei einer großen Schnittstelle entsteht dadurch viel Boilerplate-Code für Methoden, die mein Test gar nicht aufruft.
    Mit einer kleinen Schnittstelle beschränkt sich diese Arbeit auf die benötigten Methoden. Moderne Mocking-Frameworks können die Methoden automatisch bereitstellen und nehmen dir damit einen Teil dieses Aufwands ab.
    Duck Typing und sichtbare Abhängigkeiten
    Beim Duck Typing kann ich einer Methode ein Objekt übergeben, das die aufgerufenen Methoden bereitstellt. Ein ausdrücklich implementiertes Interface ist dafür entbehrlich. Das erleichtert es, passende Objekte zu übergeben.
    Welche Methoden der aufrufende Code erwartet, lässt sich dabei erst durch einen Blick in den Code erkennen. Fehlt eine davon, zeigt sich der Fehler zur Laufzeit....
    16 min
  • Liskov Substitution Principle (LSP) – Wissenshäppchen #5
    Im fünften Wissenshäppchen geht es um das Liskov Substitution Principle.
    TL;DR
    Am Liskov Substitution Principle (LSP) zeige ich dir, worauf du beim Austauschen von Klassen achten musst: Subklassen und Klassen, die ein Interface implementieren, müssen das erwartete Verhalten auch zur Laufzeit einhalten. Unerwartete Exceptions oder Seiteneffekte können bestehenden Code beschädigen, obwohl der Compiler alle geforderten Methoden akzeptiert. An Quadrat und Rechteck wird das greifbar; Unit-Tests helfen dir, solche Fehler zu entdecken. Kleine, passende Interfaces erleichtern es dir, den vereinbarten Vertrag vollständig zu erfüllen.
    Kapitelmarken
    [00:00:20] Das dritte SOLID-Prinzip und Barbara Liskov
    [00:01:11] Austauschbare Klassen und Polymorphie
    [00:02:01] Was der Compiler bei Methoden prüft
    [00:03:38] Erwartetes Verhalten zur Laufzeit
    [00:04:58] Definition des Liskov Substitution Principle
    [00:06:51] LSP-Verletzung durch eine NotImplementedException
    [00:09:09] Fehler mit Unit-Tests entdecken
    [00:10:14] Quadrat und Rechteck als Vererbungsbeispiel
    [00:14:18] Getrennte Klassen für Quadrat und Rechteck
    [00:15:07] Ruby-Beispiel: Employee, Developer und Überstunden
    [00:17:42] Prinzipien bewusst anwenden
    [00:18:13] Verständlicher Code durch erwartbares Verhalten
    [00:19:12] Prinzip der geringsten Überraschung
    [00:20:12] Einschränkungen bei der Vererbung
    [00:21:19] Unveränderbare Listen und passende Abstraktionen
    [00:23:02] Zusammenhang mit dem Interface Segregation Principle
    [00:24:14] Rückblick auf das LSP
    [00:25:46] Shownotes, Code-Beispiele und Buchempfehlung
    Inhalt
    Das LSP ist das dritte der fünf SOLID-Prinzipien. Es wurde 1987 von Barbara Liskov definiert, die ihm auch seinen Namen gab:
    Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
    Man könnte sagen, dass dies quasi die Grundanforderung ist, um Polymorphie zu ermöglichen. Denn überall dort, wo eine Basisklasse oder ein Interface erwartet wird, müssen auch alle abgeleiteten bzw. implementierenden Klassen erlaubt sein. Dies ist in den heutigen Programmiersprachen auch der Fall und der Compiler prüft dies (statisch). Aber das LSP geht noch einen Schritt weiter und fordert auch zur Laufzeit ein korrektes Verhalten.
    Erklärung
    Subklassen müssen sich so verhalten, wie es von ihren Basisklassen erwartet wird. Dies gilt auch für Interfaces und ihre Implementierungen.
    Statisch ist das kein Problem: Der Compiler prüft, ob alle geforderten Methoden von den Subklassen bzw. implementierenden Klassen angeboten werden.
    Die korrekte Verhaltensweise der Implementierung in den Subklassen bzw. implementierenden Klassen kann jedoch nicht statisch geprüft werden. Hierfür ist der Entwickler zuständig, um das LSP nicht zu verletzen.
    Wenn beispielsweise eine Subklasse in einer überschriebenen Methode eine NotImplementedException wirft, anstatt sie zu implementieren, wird das LSP verletzt.
    Aber auch unerwartete Nebeneffekte der Implementierungen in überschriebenen Methoden verletzten das LSP (Beispiel: Square erbt von Rectangle und setzt beim Aufruf von setA() auch die Seite b auf den gleichen Wert).
    Beispiel
    Die Basisklasse Employee gibt die Methode work_overtime() vor, die damit in allen Subklassen (Manager und Developer) zur Verfügung steht. Somit kann das Project gerade noch erfolgreich fertiggestellt werden.
    class Employee
    def work_overtime
    puts "Working in the evening and on weekends..."
    end
    end
    class Manager < Employee
    end
    class Developer < Employee
    end
    class Project
    def finish(employees)
    employees.each { |e| e.work_overtime }
    "Project done"
    end
    end
    puts Project.new.finish([Manager.new, Developer.new])
    # Working in the evening and on weekends...
    # Working in the evening and on weekends...
    # Project done
    Aber was passiert, wenn sich der Developer zwar formell an den "Vertrag" seiner Basisklasse hält, die Methode work_overtime() aber wie folgt überschreibt?
    class Developer < Employee
    def work_overtime
    raise "Sorry, I use XP and adhere to the 40 hour workweek"
    end
    end
    puts Project.new.finish([Manager.new, Developer.new])
    # Working in the evening and on weekends...
    # Traceback (most recent call last):
    # 4: from ./LSP.rb:23:in `'
    # 3: from ./LSP.rb:18:in `finish'
    # 2: from ./LSP.rb:18:in `each'
    # 1: from ./LSP.rb:18:in `block in finish'
    # ./LSP.rb:12:in `work_overtime': Sorry, I use XP and adhere to the 40 hour workweek (RuntimeError)
    Autsch! Das Project ist wohl zum Scheitern verurteilt! ;-) Es hatte ja keine Ahnung, dass man Developer nicht zu Überstunden verdonnern kann, denn das geht ja schließlich bei allen Employees...
    Vorteile
    Der Code wird deutlich besser nachvollziehbar, da keine unerwarteten Fehler zur Laufzeit auftreten, obwohl laut Compiler alles ok ist.
    Klassen verhalten sich so, wie man es von ihnen erwarten würde ("Principle of Least Astonishment"), und überraschen den Entwickler nicht.
    Nachteile
    Die Möglichkeiten der Vererbung werden ein wenig eingeschränkt.
    Literaturempfehlungen
    Auch beim dritten SOLID-Prinzip empfehle ich wieder Uncle Bobs Clean Code*.
    *
    Links
    Permalink zu dieser Podcast-Episode
    RSS-Feed des Podcasts
    Liskov Substitution Principle
    ArticleS.UncleBob.PrinciplesOfOod (Artikel von Uncle Bob, Beispiel in C++)
    Liskovsches Substitutionsprinzip – Wikipedia
    Zusammenfassung
    Was das Liskov Substitution Principle verlangt
    Mit dem Liskov Substitution Principle (LSP) behandle ich das dritte SOLID-Prinzip. Es wurde 1987 von der Entwicklerin Barbara Liskov definiert und trägt ihren Namen. "Substitution" bedeutet "Ersatz".
    Wo dein Code eine Basisklasse erwartet, muss er auch mit einem Objekt einer abgeleiteten Klasse korrekt arbeiten können. Dasselbe gilt für Interfaces und die Klassen, die sie implementieren. Der aufrufende Code soll dabei unabhängig davon bleiben, welche konkrete Klasse er erhält.
    Für dich als IT-Azubi gehört dieser Zusammenhang zur Polymorphie, die auch in der Abschlussprüfung vorkommt. Das LSP verlangt, dass die austauschbaren Objekte das erwartete Verhalten zur Laufzeit einhalten.
    Was der Compiler prüft
    In den betrachteten Sprachen Java und C# prüft der Compiler, ob eine konkrete Klasse die geforderten abstrakten Methoden mit passenden Signaturen implementiert. Fehlen solche Methoden, muss die Klasse selbst abstrakt bleiben oder der Code lässt sich nicht kompilieren.
    Ob eine Methode fachlich das Erwartete tut, liegt bei dir als Entwickler:in. Eine vorhandene Methode kann zur Laufzeit unerwartete Seiteneffekte auslösen oder eine Exception werfen, die der aufrufende Code gar nicht berücksichtigt.
    Beispiel: Eine Methode wirft eine Exception
    In meinem ersten Beispiel überschreibe ich eine abstrakte Methode und hinterlege darin lediglich eine NotImplementedException. Damit ist die Methode für den Compiler vorhanden.
    Beim Aufrufen erwartet der bestehende Code, dass die vereinbarte Aufgabe ausgeführt wird. Durch die unerwartete Exception kann das Programm abbrechen, wenn eine passende Fehlerbehandlung fehlt. Die Subklasse verletzt damit das LSP.
    Mit Unit-Tests kannst du solche Fehler entdecken. Ohne Tests zeigt sich das Problem möglicherweise erst beim Ausführen des Programms, wodurch es aufwendiger und teurer wird, den Fehler zu beheben. Michael Feathers bezeichnet Code ohne Unit-Tests als Legacy-Code.
    Beispiel: Quadrat und Rechteck
    Für das geometrische Beispiel lasse ich Square von Rectangle erben. Die Klasse Rectangle bietet mit SetA und SetB zwei Setter an, über die sich die beiden Seitenlängen unabhängig voneinander ändern lassen.
    Ein Quadrat muss gleich lange Seiten behalten. Seine Setter ändern deshalb jeweils beide Werte. Wenn du SetA aufrufst, wird auch B angepasst.
    Code, der mit Rectangle arbeitet, erwartet beim Setzen von A, dass B unverändert bleibt. Ein entsprechender Unit-Test schlägt mit einem Square fehl. Auch produktiver Code kann durch diesen Seiteneffekt falsche Ergebnisse liefern.
    Für dieses Modell empfehle ich getrennte Klassen für Quadrat und Rechteck, gegebenenfalls mit einer gemeinsamen Basisklasse für geometrische Formen. Eine direkte Vererbungsbeziehung in umgekehrter Richtung passt ebenfalls nicht zu dem beschriebenen Verhalten.
    Beispiel: Überstunden im Klassenmodell
    Im Ruby-Beispiel aus meinen Shownotes erben Manager und Developer von Employee. Über WorkOvertime kann der aufrufende Code Überstunden für alle Mitarbeiter:innen anfordern.
    Die Klasse Developer folgt im Beispiel der 40-Stunden-Woche aus Extreme Programming und verweigert die Methode mit einer Exception. Dadurch verletzt sie das Verhalten, das der Code von Employee erwartet.
    Verständlicher Code und weniger Überraschungen
    Wenn ich mich auf den Vertrag einer Basisklasse oder eines Interfaces verlassen kann, lässt sich mein Code leichter nachvollziehen. Besondere Regeln einzelner Subklassen muss ich dann beim Aufrufen nicht berücksichtigen.
    Dazu passt das Principle of Least Astonishment, das Prinzip der geringsten Überraschung. Verständlicher Code und erwartbares Verhalten verringern die Wahrscheinlichkeit von Fehlern.
    Das LSP begrenzt, welches Verhalten du in einer Subklasse verändern darfst. SOLID-Prinzipien verlangen grundsätzlich eine bewusste Entscheidung über ihren Einsatz und den damit verbundenen Aufwand. Beim LSP gewichte ich die Vorteile höher als diese Einschränkung.
    Passende Interfaces wählen
    An einer unveränderbaren Liste beschreibe ich ein weiteres Problem: Im Beispiel erwartet der aufrufende Code, über das gewählte Listen-Interface Elemente löschen zu können....
    27 min
  • Open Closed Principle (OCP) – Wissenshäppchen #4
    Mein viertes Wissenshäppchen hat das Open Closed Principle zum Thema.
    TL;DR
    Mit dem Open-Closed-Principle (OCP) erweitere ich Software um neue Funktionen, während der bestehende Code unverändert bleibt. Durch Polymorphie und Interfaces kannst du zusätzliche Klassen einbinden; typabhängige Abfragen mit instanceof oder switch weisen auf Stellen hin, die bei jeder neuen Klasse angepasst werden müssen. Das Prinzip senkt das Risiko, Fehler in funktionierenden Code einzubauen. Wie viel zusätzliche Architektur dafür sinnvoll ist, wäge ich anhand der erwarteten Anforderungen und des YAGNI-Prinzips ab.
    Kapitelmarken
    [00:00:00] Einstieg in das Open-Closed-Principle
    [00:00:53] Bedeutung des OCP und Motivation
    [00:02:42] Ursprung bei Bertrand Meyer
    [00:03:33] Vererbung und Polymorphie
    [00:04:27] Grenzen der Vererbung
    [00:05:41] Das OCP mit Interfaces umsetzen
    [00:07:14] Typabhängige Abfragen als Code Smell
    [00:10:34] Replace Conditional with Polymorphism
    [00:11:16] Beispiel: Tiere durch eigene Methoden beruhigen
    [00:13:24] Ruby und Duck Typing
    [00:14:21] Beispiel: Boni für Mitarbeiter:innen berechnen
    [00:15:43] Vorteile des Open-Closed-Principle
    [00:17:05] Overengineering vermeiden und YAGNI beachten
    [00:18:55] Rückblick auf das OCP
    [00:20:50] Codebeispiele und weiterführende Quellen
    Inhalt
    Das OCP ist das zweite der SOLID-Prinzipien. Es wurde vor Robert "Uncle Bob" Martin bereits 1988 von Bertrand Meyer definiert:
    Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
    Ursprünglich ging es darum, durch Vererbung die Anpassung ("modification") bereits bestehender Klassen zu verhindern, indem Subklassen abgeleitet werden, die die vorhandene Funktionalität erweitern ("extension"). Heutzutage ist Vererbung aber eher nicht mehr so gerne gesehen, da es eine starke Kopplung an die Basisklasse zur Folge hat, was das Design inflexibel macht. Stattdessen sollte man besser von vornherein Interfaces mit potentiell verschiedenen Implementierungen einsetzen.
    Erklärung
    Bei Änderungen an einem Programm sollen bestehende Komponenten bestenfalls gar nicht angepasst werden müssen, da diese Anpassungen Fehler nach sich ziehen können, die auch das bereits bestehende Verhalten betreffen.
    Neue Funktionalität wird dem Programm immer durch neue Komponenten hinzugefügt, also z.B. indem neue Klassen oder Methoden ergänzt werden.
    Fallunterscheidungen auf Basis von Typen (z.B. mit instanceof in Java) oder Enumerations (insb. wenn diese in einem switch verwendet werden) sind oft ein Hinweis auf Verletzung des OCP und sollten durch Polymorphie ersetzt werden.
    Beispiel
    class Manager
    end
    class Developer
    end
    class Payroll
    def self.calculate_bonus(employee)
    if employee.is_a?(Manager)
    return 1000
    end
    if employee.is_a?(Developer)
    return 500
    end
    end
    end
    puts Payroll.calculate_bonus(Manager.new) # 1000
    puts Payroll.calculate_bonus(Developer.new) #500
    Wenn diese Bonusberechnung um einen neuen Mitarbeitertyp Administrator erweitert werden soll, muss die bestehende Implementierung von calculate_bonus() angepasst werden:
    def self.calculate_bonus(employee)
    if employee.is_a?(Manager)
    return 1000
    end
    if employee.is_a?(Developer)
    return 500
    end
    if employee.is_a?(Administrator)
    return 250
    end
    end
    Stattdessen sollte von Anfang an die Berechnung des Bonuses an die Mitarbeiterklassen delegiert werden.
    class Manager
    def bonus
    1000
    end
    end
    class Developer
    def bonus
    500
    end
    end
    class Payroll
    def self.calculate_bonus(employee)
    employee.bonus
    end
    end
    puts Payroll.calculate_bonus(Manager.new) # 1000
    puts Payroll.calculate_bonus(Developer.new) # 500
    Dadurch ist eine spätere Erweiterung um neue Mitarbeitertypen ohne Anpassung des bisherigen Codes möglich:
    class Administrator
    def bonus
    250
    end
    end
    puts Payroll.calculate_bonus(Administrator.new) # 250
    In dynamisch typisierten Sprachen (wie im Beispiel Ruby) ist das OCP übrigens meist noch einfacher umzusetzen als in statisch typisierten, da durch das Duck-Typing umfangreiche Klassenhierarchien oder Interfaces unnötig werden. Dies sieht man auch im Beispiel oben: Manager und Developer haben keinerlei Beziehung zueinander. Trotzdem funktioniert der Code, da beide Klassen die Methode bonus() anbieten.
    Vorteile
    Bei Erweiterungen der Funktionalität sinkt die Wahrscheinlichkeit, Fehler in den bereits funktionierenden Code einzubauen.
    Klassen behalten ihre Aufgabe "ein Leben lang". Abhängige Klassen können sich darauf verlassen, dass sich die Funktionalität nicht ändert.
    Erweiterungen sind ggfs. einfacher zu implementieren, da nicht an mehreren Stellen Code angepasst, sondern "nur" neuer Code geschrieben werden muss.
    Nachteile
    Meist ist gar nicht im Voraus bekannt, welche Stellen im Code später einmal erweitert werden müssen. Einfach nur noch mit Abstraktionen zu arbeiten, macht den Code aber schwer verständlich. Außerdem verletzten wir damit das YAGNI-Prinzip.
    Literaturempfehlungen
    Da Uncle Bob die SOLID-Prinzipien so schön aufgeschrieben hat, kann ich wieder einmal seinen Klassiker Clean Code* empfehlen.
    *
    Links
    Permalink zu dieser Podcast-Episode
    RSS-Feed des Podcasts
    Open Closed Principle
    ArticleS.UncleBob.PrinciplesOfOod (Artikel von Uncle Bob, Beispiel in C++)
    Open-Closed-Prinzip – Wikipedia
    SOLID – Das Open Closed Principle | Informatik Aktuell (ausführliche Diskussion der Nachteile, Beispiel in C#)
    SOLID Design Principles Explained – The Open/Closed Principle with Code Examples (Beispiel in Java)
    Zusammenfassung
    Was das Open-Closed-Principle bedeutet
    Wenn du ein bestehendes System um neue Funktionen ergänzt, sollen seine vorhandenen Komponenten unverändert bleiben können. Das Open-Closed-Principle (OCP) beschreibt dieses Ziel mit "offen für Erweiterungen, geschlossen für Änderungen". Es ist das zweite der fünf SOLID-Prinzipien und steht für das O in SOLID.
    Jeder Eingriff in funktionierenden Code birgt das Risiko, Fehler einzubauen. Indem ich neue Funktionen in zusätzlichen Klassen unterbringe, bleibt das bisherige Verhalten erhalten.
    Ursprung und Vererbung
    Bereits 1988 formulierte Bertrand Meyer das Prinzip. Robert C. Martin griff es später im Zusammenhang mit SOLID auf. Die ursprüngliche Idee setzte auf Vererbung: Eine fertige Basisklasse wird durch Unterklassen ergänzt, die zusätzliche Funktionen mitbringen. Durch Polymorphie kann eine Unterklasse überall dort eingesetzt werden, wo die Basisklasse erwartet wird.
    In meiner Ausbildung empfehle ich Vererbung dann, wenn vorhandener Code wiederverwendet werden soll. Eine Hierarchie aus der Realität allein ist für mich kein ausreichender Anlass. In Java und C# kann eine Klasse nur von einer einzigen Basisklasse erben, wodurch sie sich eng an diese bindet.
    Das OCP mit Interfaces umsetzen
    Für Java und C# bevorzuge ich Interfaces als Grundlage. Eine Klasse kann mehrere Interfaces implementieren. Wenn dein bestehender Code mit einem Interface arbeitet, kannst du passende Klassen austauschen oder ergänzen.
    Für eine neue Funktion schreibst du eine weitere Klasse, die das erwartete Interface implementiert. Der aufrufende Code arbeitet weiterhin mit derselben Schnittstelle, während die neue Klasse das zusätzliche Verhalten mitbringt.
    Typabhängige Abfragen als Warnsignal
    Wenn du konkrete Klassen mit instanceof prüfst und davon abhängig unterschiedliche Aktionen ausführst, deutet das auf einen Verstoß gegen das OCP hin. Solche Fallunterscheidungen können in if-Abfragen oder switch-Anweisungen stecken.
    In meinem Beispiel bekommt eine Methode ein Objekt vom Typ Tier. Prüft sie ausdrücklich auf Hund und Katze, braucht sie beim späteren Hinzufügen eines Pferd einen weiteren Zweig. Damit musst du bestehenden Code ändern. Je mehr solcher Stellen vorhanden sind, desto mehr Stellen musst du bei einer neuen Tierart bearbeiten.
    Fallunterscheidungen durch Polymorphie ersetzen
    Martin Fowler beschreibt in seinem Buch Refactoring das passende Vorgehen als Replace Conditional with Polymorphism. Dabei wandert das typabhängige Verhalten in die jeweiligen Klassen.
    Für das Beispiel aus einer Tierarztpraxis erhält Tier eine Methode beruhige. Die aufrufende Klasse ruft diese Methode am übergebenen Tier auf. Jede konkrete Tierklasse legt selbst fest, wie das Tier beruhigt wird. Ergänzt du später ein Meerschweinchen mit einer eigenen Methode beruhige, kann der bestehende Aufruf damit arbeiten.
    Ruby und Duck Typing
    In meinem Ruby-Beispiel genügt es, dass ein übergebenes Objekt die aufgerufene Methode anbietet. Ruby ist dynamisch typisiert; ein ausdrücklich implementiertes Interface ist dafür entbehrlich. Dieses Konzept heißt Duck Typing. Entscheidend ist, ob das Objekt das benötigte Verhalten bereitstellt.
    Das Beispiel behandelt Boni für Mitarbeiter:innen. Die jeweiligen Klassen für Manager:innen und Entwickler:innen bestimmen ihren Bonus selbst. Auch Administrator:innen erhalten eine eigene Klasse. Die aufrufende Methode fragt den Bonus über get Bonus ab. Eine zusätzliche Klasse für Reinigungskräfte kann denselben Aufruf mit einer eigenen Bonusberechnung unterstützen.
    Was dir das Prinzip bringt
    Weil bestehender Code unverändert bleibt, sinkt das Risiko, dort neue Fehler einzubauen.
    Klassen behalten ihre Aufgaben und ihr Verhalten, sodass sich aufrufender Code weiterhin darauf verlassen kann.
    Neue Funktionen lassen sich in zusätzlichen Klassen unterbringen. Dadurch entfällt die Suche nach verstreuten Fallunterscheidungen, die du sonst erweitern müsstest.
    Aufwand und YAGNI abwägen
    Bei meinen Beispielen lässt sich leicht vorstellen, welche Klassen später hinzukommen. Im Alltag ist oft unklar, welche Teile eines Systems künftig erweitert werden müssen. Zusätzliche Interfaces und Abstraktionsschichten kosten Aufwand und machen den Code schwerer verständlich. Überflüssige Komplexität kann ihrerseits Fehler begünstigen; außerdem kann der
    23 min
  • Ausbildung der Ausbilder nach AEVO mit Julian Hirche – Anwendungsentwickler-Podcast #133
    Ein Interview mit Julian Hirche zum Thema Ausbildung der Ausbilder (AdA) nach Ausbilder-Eignungsverordnung (AEVO) gibt es in der einhundertdreiunddreißigsten Episode des Anwendungsentwickler-Podcasts.
    TL;DR
    Mit Julian Hirche bespreche ich, wie du dich auf das Ausbilden vorbereitest und was dich in der AEVO-Prüfung erwartet. Ein AdA-Kurs vermittelt dir pädagogische Methoden und rechtliche Grundlagen, mit denen du IT-Azubis durch ihre Ausbildung begleitest. An seinem Lehrgespräch zu Bubble Sort zeigt Julian, wie offene Fragen zum selbstständigen Denken anregen. Zum Ausbilden gehören außerdem fachliche und persönliche Eignung sowie die Bereitschaft, auf die Bedürfnisse eines Menschen einzugehen.
    Kapitelmarken
    [00:00:00] Thema: Ausbildung der Ausbilder:innen
    [00:01:47] Julian Hirche und sein Team für Bankensoftware
    [00:03:14] Manuelle Tests und geplante Testautomatisierung
    [00:04:26] Julians beruflicher Weg in die IT
    [00:06:41] C#, WinForms und geplante Weboberflächen
    [00:09:06] Was AdA und AEVO bedeuten
    [00:10:55] Die vier Handlungsfelder der Ausbildung
    [00:11:43] Lehrmethoden und offene Fragen im Lehrgespräch
    [00:13:00] Abendkurs und praktische Übungen
    [00:14:16] Vier-Stufen-Methode und Lehrgespräch
    [00:15:37] Bubble Sort als Thema der praktischen Prüfung
    [00:17:02] Prüfer:innen in der Rolle eines IT-Azubis
    [00:17:41] Lehrmethoden passend zur Lerngruppe wählen
    [00:18:46] Ausbildungsstart und Ausbildungsende gestalten
    [00:20:13] Rechtliche Grundlagen und Jugendarbeitsschutz
    [00:21:06] Feedback geben und als Vorbild begleiten
    [00:22:49] Warum Julian den Ausbilderschein gemacht hat
    [00:24:19] Persönliche und fachliche Eignung zum Ausbilden
    [00:26:30] Ablauf der praktischen AEVO-Prüfung
    [00:28:10] Die Ausbildungssituation auf 15 Minuten abstimmen
    [00:29:08] Noten und schriftliche Multiple-Choice-Prüfung
    [00:30:10] Fallbeispiele und Fragen aus der Ausbildungspraxis
    [00:31:23] Wie vorbereitet sich Julian für die Praxis fühlt
    [00:31:56] Lehrkräfte und Teilnehmer:innen im AdA-Kurs
    [00:34:01] Lehrmethoden mit anderen Berufsgruppen erproben
    [00:35:19] Erfahrungen mit dem Kurs und dem dualen Ausbildungssystem
    [00:37:41] Was gute Ausbilder:innen ausmacht
    [00:39:07] Buchtipps: Robert C. Martin und Informatik Macchiato
    [00:40:37] Clean Code im beruflichen Alltag
    [00:42:06] Kontakt zu Julian Hirche
    [00:43:13] Shownotes und Feedback
    Inhalt
    Allgemeines zur Person
    Wie ist dein Name und wo arbeitest du (falls gewünscht)?
    Julian Hirche, 34 Jahre alt
    Ich arbeite in Hamburg in einem Unternehmen, das Software für Banken entwickelt.
    Ich bin dort Teamleiter von 3 Entwicklern und 2 Testern.
    An welchen Projekten arbeitest du zur Zeit in deinem Tagesjob?
    Wir erstellen Reports für Banken und haben z.B. eine Drag/Drop-Oberfläche zum "Klicken" von Reports programmiert. Außerdem machen wir Reporting mit SQL und PL/SQL oder Datenexport nach Excel.
    Wir haben leider noch keine automatische Testabdeckung. Daher gibt es viele manuelle Tests. Wir planen aber, das zu automatisieren.
    Wir haben "richtige" Tester, die z.B. Akzeptanztests durchführen.
    Wie bist du zur Informatik bzw. Softwareentwicklung gekommen?
    Ich programmiere seit ich 8 Jahre alt war. Bin mit dem C64 angefangen. Programmieren fand ich spannender als Spielen.
    Im Jahr 2000 habe ich die Realschule abgeschlossen und wollte Fachinformatiker werden, habe aber keinen Ausbildungsplatz gefunden.
    Das Arbeitsamt hat mir eine Ausbildung zum Mechatroniker empfohlen. Danach habe ich dann mehrere Jahre aks Mechatroniker gearbeitet, aber letztlich gekündigt, weil ich in die IT wollte.
    Welche Ausbildung bzw. welches Studium hast du im Bereich der Informatik absolviert?
    Ich habe zunächst eine Informatikassistentenausbildung in Wedel absolviert an einer privaten Fachhochschule. Dort habe ich auch viel zusammen mit Bachelor-Studenten gemacht.
    Mit welcher/n Programmiersprache/n arbeitest du im Alltag?
    Hauptsächlich C#. Früher auch Java oder PHP.
    Früher meinte ich mal "Microsoft ist doof", aber inzwischen gefällt mir C# sehr gut. Auch .NET Core unter Linux ist eine tolle Sache.
    Wir entwickeln aktuell mit React im Frontend.
    Was ist deine Lieblingsprogrammiersprache und warum?
    C#, weil man mit ihr viele coole Sachen machen kann.
    Ausbildereignungsprüfung, AdA und AEVO
    Was heißen die ganzen Abkürzungen: AdA, AEVO?
    AEVO ist die Ausbilder-Eignungsverordnung, die insb. die berufs-/arbeitspädagogische Eignung der Ausbilder definiert.
    AdA steht für Ausbildung der Ausbilder, also den Weg, wie man zum Ausbilder wird (z.B. durch entsprechende Kurse).
    Wer darf einen Ausbilderschein machen? Welche Voraussetzungen gibt es?
    Die Person muss persönlich (z.B. keine Vorstrafen) und fachlich (z.B. selbst die Ausbildung gemacht oder 5 Jahre im Beruf gearbeitet) geeignet sein.
    Warum hast du den Ausbilderschein gemacht?
    Ich gebe mein Wissen gerne weiter.
    Ich war schon lange im Internet aktiv und nutze viele Foren.
    In unserem Betrieb finden wir keine Mitarbeiter, warum also nicht einfach selbst ausbilden?
    Letztes Jahr haben wir es aber aufgrund von Projekten leider nicht geschafft, jemanden einzustellen.
    Wie und wo hast du den Ausbilderschein gemacht (z.B. Kurs, Selbststudium)?
    Ich habe einen Kurs besucht. Über 2 Monate hinweg habe ich in der Abendschule die Inhalte gelernt. Ich hätte alternativ auch 2 Wochen in Vollzeit lernen können.
    Die Dozenten waren Lehrer an der Schule, die auch in dem Bereich gearbeitet haben.
    Wir waren 20 Teilnehmer. Ich war der einzige ITler. Die anderen waren aus den Büroberufen oder dem Einzelhandel.
    Viele haben den Kurs im Rahmen ihrer Meister-Ausbildung gemacht.
    Braucht man den Schein überhaupt (noch)? Welche gesetzlichen Grundlagen gibt es?
    Nach AEVO muss ein Unternehmen einen Ausbilder haben, wenn es ausbilden will.
    Welche Inhalte wurden in deinem Kurs vermittelt? Was hast du gelernt? Was konntest du konkret für deinen Berufsalltag mitnehmen?
    Die Inhalte haben mit den konkreten Berufen nichts zu tun. Sie sind berufsunabhängig.
    Wir sind z.B. das komplette BIBB durchgegangen, aber auch das JAschG war wichtig. Auch der Sinn und die Vorteile der Ausbildung wurden besprochen.
    Es gibt grundsätzlich 4 Handlungsfelder, die man lernt: Ausbildung prüfen, vorbereiten, durchführen und abschließen.
    Zur Vorbereitung zählen z.B. das Planen der Probezeit, Literatur raussuchen, den ersten Arbeitstag gestalten.
    Das Abschließen umfasst insb., den Azubi durch die Prüfung zu bringen, aber auch das Erstellen eines Arbeitszeugnisses und die Gestaltung des Arbeitsvertrags bei Übernahme.
    Beim Durchführen der Ausbildung haben wir uns u.a. die verschiedenen Lehrmethoden angeschaut: 4-Stufen-Modell, Lehrgespräch, Projektarbeit.
    Aber auch das Feedback geben mit einem Evaluierungsbogen haben wir gelernt, z.B. ob der Azubi wissbegierig und pünktlich ist, wie er mit Kollegen umgeht und ob er sein Arbeitspensum schafft.
    Das Motivieren von Azubis war auch Bestandteil des Kurses und manchmal ist auch ein wenig Erziehung nötig, wenn die Aubis noch jünger sind.
    Was hat es mit den Lehrmethoden auf sich?
    Es geht darum, wie man den Azubis Wissen optimal vermittelt.
    Für Gruppen aus mehreren Azubis und das Vermitteln von Grundlagen eignet sich evlt. Frontalunterricht.
    Bei tiefergehenden Themen und mehreren Azubis ist vielleicht eher eine Projektarbeit sinnvoll.
    Die 4-Stufen-Methode aus Erklären, Vormachen, Nachmachen und Wiederholen ist eine Art Schritt-für-Schritt-Anleitung und heute nicht mehr so gerne gesehen.
    Es sollen eher in einem Lehrgespräch offene Fragen gestellt werden, über die der Azubi das Problem selbst lösen kann.
    Diese Gespräche haben wir im Kurs auch mit den anderen Teilnehmern gemeinsam geübt. Ich selbst durfte z.B. von einem Paketierer lernen, wie man Pakete platzsparend packt und verklebt.
    Aus welchen Teilen besteht die Prüfung?
    Es gibt einen schriftlichen Teil über 180 Minuten und einen mündlichen über 30 Minuten.
    Wie läuft die Prüfung konkret ab?
    Die schriftliche Prüfung ist eine Multiple-Choice-Prüfung mit 70 Fragen mit angegebener Anzahl korrekter Antworten.
    Ähnlich zur IHK-Prüfung gibt es viele fachliche Fragen z.B. zur Länge der Probezeit.
    Es gibt auch Fallstudien mit Beispielen: Azubi schafft die Prüfung nicht, wie geht es weiter? Welche Lehrmethoden sind sinnvoll bei einzelnen Berufen? Viele rechtliche Fragen.
    Die mündliche Prüfung besteht aus einem Rollenspiel über 15 Minuten und einem Fachgespräch über 15 Minuten.
    Eine Präsentation ist optional auch möglich. Dort müsste man dann zeigen, wie man einen Azubi anlernen würde.
    Im Kurs haben wir die mündliche Prüfung mit den anderen Kursteilnehmern durchgespielt. Man muss genau auf 15 Minuten kommen, also muss die komplette Lerneinheit dann fertig sein.
    Als Ergebnis bekommt man ein Zeugnis mit Note.
    Wie lief deine eigene Prüfung ab?
    Ich habe in einem Lehrgespräch den Bubble-Sort erklärt.
    Der Azubi musste den Algorithmus selbst finden.
    Wer war dein Azubi?
    Ein Prüfer hat den Azubi gespielt.
    Würdest du den Ausbilderschein weiterempfehlen?
    Auf jeden Fall! Der Kurs war für mich sehr lehrreich.
    Unser duales System der Berufsausbildung in Deutschland ist insg. super und sollte weiterverfolgt werden. Bei uns können nicht nur starke Schüler ausgebildet werden. Haupt- oder Realschule reichen auch für eine tolle Ausbildung.
    Was macht einen guten Ausbilder aus?
    Ein guter Ausbilder sollte den Azubi nicht als Ressource oder Maschine ansehen, sondern als Menschen akzeptieren. Es sollte z.B. auch bei privaten Problemen helfen und Empathie zeigen.
    Aus- und Weiterbildung
    Welche Quellen kannst du für die Aus- und Weiterbildung im Bereich Programmierung empfehlen und warum?
    Informatik macchiato*. Hier werden für Schüler oder Studenten gut verständlich Informatikprobleme z.B. mit Bildern erklärt.
    Was ist dein absolutes Lieblingsbuch mit Bezug zur IT/Programmierung und warum?
    Robert C. Martin: Clean Code*. Das hat mir persönlich sehr viel gebracht. Ich habe gelernt,...
    45 min
  • Kryptographie – Hashverfahren und elektronische Signatur – Anwendungsentwickler-Podcast #132
    Die Fortsetzung zum Oberthema Kryptographie mit Hashverfahren und elektronischer Signatur gibt es in der einhunderzweiunddreißigsten Episode des Anwendungsentwickler-Podcasts.
    TL;DR
    Mit Hash-Verfahren prüfe ich, ob sich Daten verändert haben: Aus beliebig langen Daten entsteht ein Hash-Wert mit fester Länge, den du mit dem Hash der Originaldaten vergleichst. Eine elektronische Signatur verbindet diesen Hash mit dem privaten Schlüssel der absendenden Person und macht damit auch die Herkunft prüfbar. Wenn ich die signierte E-Mail zusätzlich mit dem öffentlichen Schlüssel der empfangenden Person verschlüssele, bleibt ihr Inhalt vertraulich. Für deine Abschlussprüfung solltest du die Eigenschaften von Hash-Verfahren und den Ablauf beim Signieren kennen.
    Kapitelmarken
    [00:00:00] Einstieg in Kryptografie Teil 2
    [00:01:33] Asymmetrische Verfahren und Schlüsselpaare
    [00:02:21] Integrität und Authentizität
    [00:04:37] Hash-Verfahren und feste Hash-Längen
    [00:05:46] Kollisionen und Einwegverfahren
    [00:07:09] Gehashte Passwörter und Begriffe in der Prüfung
    [00:08:47] Gleiche Eingaben und kollisionsarme Verfahren
    [00:12:45] Hash-Längen und die Schwächen von MD5
    [00:14:44] Downloads mit dem Original-Hash vergleichen
    [00:16:18] Kleine Änderungen und deutlich andere Hash-Werte
    [00:17:54] Eigenschaften von Hash-Verfahren für die Prüfung
    [00:19:45] Hash-Werte beim E-Mail-Versand
    [00:21:03] Angriffe auf ungeschützte Hash-Anhänge
    [00:21:44] Herkunft mit dem privaten Schlüssel nachweisen
    [00:26:20] Mehrere Schutzschichten für eine E-Mail
    [00:28:38] Den Hash mit dem privaten Schlüssel absichern
    [00:29:53] Elektronische Signatur und die Unterschrift im Mietvertrag
    [00:33:57] Ablauf beim signierten und verschlüsselten E-Mail-Versand
    [00:36:02] Schlüssel austauschen und Aufwand in der Praxis
    [00:36:41] Prüfungsbezug und Abschluss
    Inhalt
    Hashes
    Hashverfahren erzeugen aus einer Eingangszeichenfolge beliebiger Länge eine Ausgangszeichenfolge fixer Länge, "Hash" oder "Prüfsumme" genannt.
    Sie sind mathematische Einwegverfahren, die keinen Rückschluss auf die Eingangsdaten zulassen, wenn die Ausgangszeichenfolge bekannt ist.
    Die erzeugten Hashes sollten sich selbst bei kleinsten Änderungen an den Eingangsdaten deutlich verändern, damit sofort ersichtlich ist, dass die Eingangsdaten verändert wurden.
    Hashverfahren sollten möglichst "kollisionsarm" sein. Das heißt, dass es sehr unwahrscheinlich sein muss, dass zwei unterschiedliche Eingangszeichenfolgen zum gleichen Hashwert führen.
    Beispiele für Hashverfahren: MD5, SHA1, SHA256. Sie unterscheiden sich im verwendeten Algorithmus und/oder der Länge des erzeugten Hashwertes.
    Ein Hashverfahren gilt als unsicher, wenn mit vertretbarem Aufwand eine Kollision herbeigeführt werden kann, z.B. indem einer veränderten Zeichenfolge weitere bewusste Änderungen hinzugefügt werden, um dann wieder zum ursprünglichen Hashwert zu gelangen. Das Verfahren MD5 gilt heute als unsicher.
    Elektronische Signatur
    Die elektronische Signatur soll die Integrität und Authentizität von Daten sicherstellen.
    Beispiel: Bob möchte sicherstellen, dass die erhaltene Mail von Alice auch wirklich von ihr stammt (und nicht von einem Angreifer, der vorgibt, Alice zu sein) und auf dem Weg durch das Internet nicht von einem Angreifer manipuliert wurde (also z.B. Alices IBAN nicht durch die des Angreifers ersetzt wurde).
    Aufbau: Der Hash der Eingangsdaten wird mit dem privaten Schlüssel des Absenders verschlüsselt. Der Empfänger entschlüsselt den Hash mit dem öffentlichen Schlüssel des Absenders und erzeugt mit dem gleichen Hashverfahren den Hash über die empfangenen Daten. Wenn der entschlüsselte Hash mit dem selbst erstellten übereinstimmt, weiß der Empfänger, dass Integrität und Authentizität eingehalten wurden.
    Links
    Permalink zu dieser Podcast-Episode
    RSS-Feed des Podcasts
    Kryptographische Hashfunktion – Wikipedia
    Message-Digest Algorithm 5 – Wikipedia
    Zusammenfassung
    Schlüssel und Schutzziele
    Wenn ich dir eine vertrauliche Nachricht schicke, verschlüssele ich sie mit deinem öffentlichen Schlüssel. Du kannst sie mit deinem privaten Schlüssel wieder entschlüsseln. Für dieses asymmetrische Verfahren brauchst du ein Schlüsselpaar, dessen privater Teil geheim bleiben muss. Den öffentlichen Teil kannst du weitergeben.
    Bei einer E-Mail möchtest du außerdem prüfen können, ob ihr Inhalt unterwegs verändert wurde und von wem sie stammt. Dafür stehen die Schutzziele Integrität und Authentizität. Als Grundlage für die folgenden Abläufe brauchst du die asymmetrischen Verfahren, die ich in Episode 131 behandelt habe.
    Wie ein Hash entsteht
    Wenn ich eine Datei durch eine Hash-Funktion laufen lasse, erhalte ich einen Hash-Wert, auch Prüfsumme genannt. Die Eingabedaten dürfen beliebig lang sein. Wie lang das Ergebnis ist, legt das jeweilige Verfahren fest: MD5 erzeugt beispielsweise 128 Bit lange Hash-Werte; als weiteres Beispiel kommt SHA mit 256 Bit vor.
    Für Hash-Verfahren gelten folgende Eigenschaften:
    Gleiche Daten ergeben denselben Hash. Dadurch kannst du das Ergebnis erneut berechnen und vergleichen.
    Die Länge des Ergebnisses bleibt fest. Sie hängt vom Verfahren ab, auch wenn du eine große Datei oder nur einen Buchstaben eingibst.
    Das Verfahren arbeitet in eine Richtung. Aus dem Hash lässt sich die ursprüngliche Datei als Ganzes nicht zurückrechnen.
    Kleine Änderungen sollen den Hash deutlich verändern. Bereits ein geändertes Bit soll zu einem erheblich anderen Ergebnis führen.
    Ein gutes Verfahren ist kollisionsarm. Unterschiedliche Eingaben sollen möglichst selten denselben Hash ergeben.
    In meinen Prüfungen kommt es vor, dass Prüflinge gehashte Passwörter als "verschlüsselt" bezeichnen. Der passende Begriff lautet hier gehasht. Hash-Verfahren sind mathematische Einwegverfahren. Beim Verschlüsseln bleiben die Informationen erhalten, sodass du die Originaldaten mit dem passenden Schlüssel wiederherstellen kannst.
    Kollisionen und die Sicherheit eines Verfahrens
    Weil ein Hash eine feste Länge hat, gibt es nur eine begrenzte Anzahl möglicher Hash-Werte. Für die Eingabedaten stehen beliebig viele Möglichkeiten zur Verfügung. Deshalb können verschiedene Eingaben denselben Hash erzeugen. Das heißt Kollision.
    Ein geeignetes Verfahren muss es rechnerisch schwer machen, solche passenden Eingaben zu finden. Die Sicherheit hängt davon ab, wie viel Aufwand ein Angriff erfordert. Schnellere Rechner können diesen Aufwand verringern; auch die Länge des Hash-Werts spielt dabei eine Rolle.
    MD5 wird als Beispiel für ein bereits geknacktes Verfahren behandelt. Für schützenswerte Daten solltest du es deshalb nicht mehr einsetzen. Sobald sich mit vertretbarem Aufwand passende manipulierte Daten erzeugen lassen, ist das betreffende Verfahren für diesen Zweck unsicher.
    Eine heruntergeladene Datei prüfen
    Wenn ich eine Datei herunterlade, brauche ich zum Prüfen den Hash der Originaldatei. Diesen kann der/die Hersteller:in beispielsweise neben dem Download auf der Website veröffentlichen.
    Du gehst so vor:
    Lade die Datei herunter und berechne ihren Hash mit dem angegebenen Verfahren.
    Vergleiche dein Ergebnis mit dem veröffentlichten Original-Hash.
    Bei abweichenden Werten unterscheiden sich die Daten. Die Ursache musst du gesondert herausfinden: Beim Übertragen kann ein Fehler passiert sein, ebenso können die Daten gezielt verändert worden sein. Der Hash-Vergleich zeigt dir die Abweichung, ohne ihren Grund zu bestimmen.
    Den Hash einer E-Mail absichern
    Wenn ich den Hash einer E-Mail als Anhang mitschicke, kannst du selbst einen Hash über den empfangenen Inhalt berechnen und beide Werte vergleichen. Ein:e Angreifer:in könnte allerdings den Text verändern und dazu einen neuen passenden Hash anhängen.
    Deshalb muss auch die Herkunft des mitgeschickten Hash-Werts prüfbar sein. Dafür kommt der private Schlüssel der absendenden Person ins Spiel. Im vereinfachten Ablauf verschlüssele ich den Hash mit meinem privaten Schlüssel. Du prüfst ihn mit meinem öffentlichen Schlüssel, dessen Zugehörigkeit zu mir sicher feststehen muss.
    Elektronische Signatur
    Aus dem Hash des Inhalts und dem privaten Schlüssel entsteht die elektronische Signatur. Wenn ich eine E-Mail signiere, läuft das im beschriebenen Modell so ab:
    Ich berechne einen Hash über den E-Mail-Inhalt.
    Diesen Hash verschlüssele ich mit meinem privaten Schlüssel und hänge ihn an die Nachricht.
    Du entschlüsselst den angehängten Hash mit meinem öffentlichen Schlüssel.
    Über den empfangenen E-Mail-Inhalt berechnest du selbst einen Hash und vergleichst beide Werte.
    Der öffentliche Schlüssel macht die Herkunft prüfbar. Der Hash-Vergleich prüft, ob der Inhalt seit dem Signieren unverändert geblieben ist. Voraussetzung ist, dass mein privater Schlüssel geheim geblieben ist und du meinen öffentlichen Schlüssel sicher zuordnen kannst.
    Als Bild dafür verwende ich meinen Mietvertrag, den ich auf jeder Seite unterschreiben musste. Die Unterschrift ordnet den Vertrag einer Person zu; durch die unterschriebenen Seiten sollen ausgetauschte Seiten auffallen. Eine elektronische Signatur verfolgt ebenfalls die Schutzziele Integrität und Authentizität. Eine signierte Nachricht bleibt lesbar, solange ihr Inhalt offen übertragen wird.
    Eine signierte E-Mail vertraulich verschicken
    Für eine vertrauliche E-Mail ergänze ich den Ablauf um einen weiteren Schritt. Beide beteiligten Personen brauchen dafür ein eigenes Schlüsselpaar und den öffentlichen Schlüssel der jeweils anderen Person.
    Beim Senden
    Ich berechne den Hash des E-Mail-Inhalts und signiere ihn mit meinem privaten Schlüssel.
    Die Signatur hänge ich an die E-Mail.
    Anschließend verschlüssele ich die gesamte Nachricht einschließlich Anhang mit deinem öffentlichen Schlüssel.
    Beim Empfangen
    Du entschlüsselst die Nachricht mit deinem privaten Schlüssel und erhältst den Inhalt samt Signatur.
    Mit meinem öffentlichen Schlüssel prüfst du die Signatur beziehungsweise erhältst im beschriebenen Modell den angehängten Hash.
    Du berechnest selbst den Hash des E-Mail-Inhalts u
    38 min
  • Kryptographie – Schutzziele und Verschlüsselung – Anwendungsentwickler-Podcast #131
    Einen kurzen Einstieg in einige grundlegende Begriffe der Kryptographie - die drei Schutzziele und die symmetrische und asymmetrische Verschlüsselung - gebe ich in der einhunderteinunddreißigsten Episode des Anwendungsentwickler-Podcasts.
    TL;DR
    Mit Alice und Bob als Beispiel zeige ich dir, wie Kryptografie vertrauliche Nachrichten ermöglicht und welche Rolle sie in deiner IT-Ausbildung spielt. Zu den behandelten Schutzzielen gehören Vertraulichkeit, Integrität und Authentizität. Beim symmetrischen Verschlüsseln verwenden beide Seiten denselben geheimen Schlüssel, der sicher übermittelt werden muss. Das asymmetrische Verfahren arbeitet mit einem öffentlichen und einem privaten Schlüssel; dabei erfordert das Rechnen mehr Aufwand und der private Schlüssel muss geschützt bleiben.
    Kapitelmarken
    [00:00:00] Einstieg in die Kryptografie
    [00:00:33] IT-Sicherheit in Ausbildung und Abschlussprüfung
    [00:02:32] Was Kryptografie bedeutet
    [00:03:40] Schutzziele der Informationssicherheit
    [00:04:06] Vertraulichkeit am Beispiel von Alice und Bob
    [00:05:47] Integrität von Nachrichten
    [00:06:47] Authentizität der absendenden Person
    [00:08:31] Klartext, Chiffrat und Schlüssel
    [00:09:53] Symmetrisch verschlüsseln
    [00:12:04] Den geheimen Schlüssel sicher übermitteln
    [00:15:28] Schlüssellängen und Brute Force
    [00:17:10] Rechenleistung und Empfehlungen des BSI
    [00:19:24] Asymmetrische Verfahren und Schlüsselpaare
    [00:22:22] Schlüsselpaare mit PGP erzeugen
    [00:23:00] Private und öffentliche Schlüssel
    [00:25:09] Mit Bobs öffentlichem Schlüssel verschlüsseln
    [00:27:33] Rechenaufwand asymmetrischer Verfahren
    [00:29:52] Vertraulich in beide Richtungen kommunizieren
    [00:31:40] Schlüssel im privaten Alltag handhaben
    [00:32:44] Client-Server-Kommunikation und HTTPS
    [00:35:34] Rückblick auf die behandelten Grundlagen
    [00:38:18] Ausblick auf Integrität und Authentizität
    Inhalt
    Meist wird bei der Erklärung von kryptografischen Verfahren der Nachrichtenaustausch zwischen zwei Personen als Anwendungsfall verwendet. Person A (meist "Alice" genannt) möchte mit Person B (meist "Bob" genannt) kommunizieren und dabei gewisse Ziele erreichen. Nehmen wir einfach an, Alice möchte Bob eine E-Mail schicken.
    Kryptographie ist die Wissenschaft der Verschlüsselung von Informationen.
    Ein Schlüssel wird zum ver- bzw. entschlüsseln von Informationen verwendet und kann z.B. ein Passwort sein oder einfach eine lange Zahl, z.B. eine Kette aus 128 Bit.
    Um Schlüssel zu knacken, kann man einfach alle Kombinationsmöglichkeiten ausprobieren. Das wären bei 64 Bit z.B. ca. 18 Trilliarden. Dieses Verfahren heißt Brute Force und dauert tendentiell sehr lange. ;-)
    Da die Computer aber immer schneller werden (und damit auch schneller Schlüssel durchprobieren können), sollte die verwendete Schlüssellänge regelmäßig erhöht werden. Das Bundesamt für Sicherheit in der Informationstechnik gibt z.B. regelmäßig Empfehlungen zur Schlüssellänge heraus.
    Schutzziele der Informationssicherheit
    Vertraulichkeit: Der Inhalt der Nachricht kann nur vom Empfänger gelesen werden und nicht von einem Angreifer. Üblicherweise muss der Inhalt dafür verschlüsselt werden.
    Integrität: Der Inhalt der Nachricht wurde auf dem Weg zum Empfänger nicht durch einen Angreifer verändert. Hierzu werden meist Hashverfahren eingesetzt.
    Authentizität: Die Nachricht stammt sicher vom angegebenen Absender und nicht von einem Angreifer. Hierfür sind meist mehrere Verfahren (Verschlüsselung und Hashverfahren) nötig.
    Symmetrische vs. asymmetrische Verschlüsselung
    Wird zum Ver- und Entschlüsseln der Nachricht der gleiche Schlüssel verwendet, spricht man von symmetrischer Verschlüsselung.
    Beispiel: Alice verschlüsselt die Mail an Bob mit einem Kennwort, das auch für das Entschlüsseln benötigt wird.
    Vorteil: Wenig rechenintensiv, da Schlüssellänge aufgrund des geheimen Schlüssels kürzer sein darf (z.B. 128 Bit).
    Nachteil: Schlüssel muss auf sicherem Wege zwischen Absender und Empfänger ausgetauscht werden.
    Wenn zum Verschlüsseln ein anderer Schlüssel verwendet wird, als für das Entschlüsseln, spricht man von asymmetrischer Verschlüsselung.
    Es werden zwei Schlüssel ("Schlüsselpaar") benötigt, die mathematisch miteinander in Beziehung stehen, sich also berechnen lassen.
    Dieses Schlüsselpaar besteht aus dem privaten Schlüssel, der unbedingt geheimgehalten werden muss, und dem öffentlichen Schlüssel, der für Jederman einsehbar sein darf (und auch sollte).
    Damit alle Schutzziele erreicht werden können, braucht jeder Teilnehmer (Alice und Bob) sein eigenes Schlüsselpaar.
    Beispiel: Alice verschlüsselt die Mail an Bob mit dessen öffentlichem Schlüssel. Bob kann die Mail dann nur mit seinem privaten Schlüssel entschlüsseln.
    Vorteil: Schlüsselaustausch kann einfach erfolgen, sogar auf "unsicherem" Weg (z.B. Mail, Internet).
    Nachteil: Rechenintensives Verfahren mit Schlüsseln, die z.B. 40x so lang sind wie symmetrische Schlüssel (4096 Bit).
    Beispiel für asymmetrische Verschlüsselung
    Um mit asymmetrischer Verschlüsselung die Vertraulichkeit zu wahren, verschlüsselt der Absender mit dem öffentlichen Schlüssel des Empfängers die Inhalte.
    Nun kann ausschließlich der Empfänger, der als einziger das Gegenstück zu seinem öffentlichen Schlüssel - seinen privaten Schlüssel - besitzt, die Inhalte entschlüsseln.
    Auch der Absender kann die von ihm versendeten Inhalte nun nicht mehr lesen.
    Links
    Permalink zu dieser Podcast-Episode
    RSS-Feed des Podcasts
    Kryptographie – Wikipedia
    Informationssicherheit – Wikipedia
    IT-Schutzziele: Vertraulichkeit, Integrität, Verfügbarkeit
    Kryptographische Verfahren: Empfehlungen und Schlüssellängen
    Zusammenfassung
    Kryptografie in deiner IT-Ausbildung
    Seit der Überarbeitung der IT-Berufe im Jahr 2018 sind IT-Sicherheit und Datenschutz ausdrücklich im Ausbildungsrahmenplan verankert. Als IT-Azubi brauchst du diese Grundlagen auch in der Anwendungsentwicklung. Fragen zu kryptografischen Verfahren kamen bereits zuvor in Abschlussprüfungen vor.
    Mit Alice und Bob als Beispiel zeige ich dir, wie du Nachrichten vertraulich austauschen kannst. Kryptografie bezeichnet ursprünglich die Wissenschaft davon, Informationen zu verschlüsseln. Im hier verwendeten erweiterten Sinn gehören auch Hash-Verfahren und elektronische Signaturen dazu.
    Schutzziele beim Austausch von Nachrichten
    Wenn Alice eine E-Mail an Bob schickt, spielen folgende Schutzziele eine Rolle:
    Vertraulichkeit: Nur die vorgesehene Person darf den Inhalt lesen können. Alice kann ihre Nachricht dafür so verschlüsseln, dass Bob sie mit dem passenden Schlüssel wieder lesen kann.
    Integrität: Der Inhalt bleibt auf dem Weg zur empfangenden Person unverändert. Ändert ein:e Angreifer:in beispielsweise einen Rechnungsbetrag, ist dieses Schutzziel verletzt. Hash-Verfahren werden als Verfahren genannt, um solche Änderungen zu erkennen.
    Authentizität: Eine Nachricht stammt von der angegebenen absendenden Person. Dafür kommen unter anderem kryptografische Verfahren und Hash-Verfahren zum Einsatz.
    Die technischen Details zu Hash-Verfahren und elektronischen Signaturen bleiben für eine weitere Folge vorgesehen.
    Vom Klartext zum Chiffrat
    Ein Algorithmus verarbeitet den lesbaren Inhalt zusammen mit einem Schlüssel zu einem Chiffrat. Mit dem passenden Schlüssel lässt sich daraus der ursprüngliche Text wiederherstellen. Für deine Prüfung solltest du verstehen, wie symmetrische und asymmetrische Verfahren funktionieren und welche Schlüssel dabei erforderlich sind.
    Symmetrisch verschlüsseln
    Beim symmetrischen Verfahren verwendest du zum Verschlüsseln und Entschlüsseln denselben geheimen Schlüssel. Ein Beispiel ist ein kennwortgeschütztes ZIP-Archiv: Das beim Erstellen festgelegte Kennwort brauchst du später auch zum Entpacken.
    Alice könnte ihre Nachricht in eine Textdatei schreiben, diese in einem verschlüsselten ZIP-Archiv ablegen und das Archiv an Bob schicken. Sobald Bob das Kennwort kennt, kann er den Inhalt lesen. Beide können den gemeinsamen Schlüssel anschließend für Nachrichten in beide Richtungen verwenden.
    Das Verfahren benötigt wenig Rechenaufwand. Als Beispiel für einen technischen Schlüssel wird eine Bitfolge mit 128 Bit genannt.
    Den geheimen Schlüssel sicher übermitteln
    Damit Bob die Nachricht entschlüsseln kann, muss Alice ihm den Schlüssel über einen sicheren Weg zukommen lassen. Dafür braucht sie einen gesonderten Kanal.
    Aus meiner Praxis kenne ich verschlüsselte ZIP-Anhänge, deren Kennwort kurz darauf in einer zweiten E-Mail ankommt. Wer Zugriff auf das betroffene E-Mail-Konto oder den Mailserver hat, kann beide Nachrichten lesen und damit den Anhang entschlüsseln.
    Ein Telefonat ist ein möglicher zweiter Weg. Beim Diktieren komplexer Kennwörter können Fehler entstehen; außerdem muss die andere Person erreichbar sein. Auch Mithören oder Abhören bleibt ein Risiko. Für alltägliche private Dateien wird ein verschlüsseltes ZIP-Archiv mit einem Kennwort genannt, das beispielsweise telefonisch oder per SMS übermittelt wird.
    Schlüssellängen und Brute Force
    Technisch bestehen Schlüssel aus Bitfolgen. Bei einem Brute-Force-Angriff werden mögliche Schlüssel systematisch ausprobiert, bis einer passt. Mit 128 Bit gibt es bereits sehr viele Kombinationen, wodurch vollständiges Durchprobieren viel Rechenzeit erfordert.
    Die verfügbare Rechenleistung beeinflusst, welche Schlüssellängen als sicher gelten. Auch mögliche Auswirkungen von Quantencomputern werden angesprochen. Das Bundesamt für Sicherheit in der Informationstechnik, kurz BSI, veröffentlicht regelmäßig Empfehlungen zu geeigneten Schlüssellängen. Die genannten Zahlen dienen als Größenbeispiele im behandelten Kontext.
    Asymmetrisch verschlüsseln
    Ein kryptografisches Programm erzeugt ein Schlüsselpaar, dessen beide Teile mathematisch zusammenhängen. Als Beispiel wird PGP genannt, ausgeschrieben "Pretty Good Privacy". Für deine Ausbildung brauchst du das Prinzip; die Schlüssel von Hand mathematisch herzuleiten gehört laut den Ausführungen zum Ausbildungswissen,...
    40 min
  • Buchclub: Java by Comparison – Anwendungsentwickler-Podcast #130
    Ich habe ein neues Lieblingsbuch für die Ausbildung! "Java by Comparison" wird im Buchclub in der einhundertdreißigsten Episode des Anwendungsentwickler-Podcasts besprochen.
    TL;DR
    Ich nehme Java by Comparison in die Leseliste für meine IT-Azubis auf: Das Buch zeigt anhand von 70 Codebeispielen, wie du deinen Code verständlicher und weniger fehleranfällig schreibst. Die kurzen Beispiele mit ihren überarbeiteten Fassungen eignen sich bereits im ersten Ausbildungsjahr, sobald du grundlegende Programmierkenntnisse hast; viele Regeln lassen sich auch auf C# oder PHP übertragen. Dein:e Ausbilder:in kann bei Code-Reviews auf passende Kapitel verweisen, während du dir die Hintergründe selbst erarbeitest.
    Kapitelmarken
    [00:00:00] Buchclub zu Java by Comparison
    [00:02:09] Buchidee und Aufbau der Codebeispiele
    [00:05:31] Regeln auf andere Programmiersprachen übertragen
    [00:06:43] Zielgruppe und FizzBuzz als Einstiegstest
    [00:08:41] Geplanter Einsatz in Ausbildung und Code-Reviews
    [00:11:18] Grundlagen, Kommentare und lesbarer Code
    [00:12:47] Namen, Domänensprache und Fehlerbehandlung
    [00:13:49] Unit-Tests und Klassen entwerfen
    [00:14:42] Streams, Lambdas und Methodenreferenzen
    [00:15:08] Builds, Continuous Integration und weitere Praxisthemen
    [00:17:13] Fachliche Namen am Beispiel der Krankenversicherung
    [00:19:45] Optionals funktional mit ifPresent verarbeiten
    [00:23:33] Autoren und Hintergründe zum Buch
    [00:25:23] Links, Codebeispiele und ergänzende Beiträge
    Inhalt
    Ich habe das Buch an einem Wochenende komplett verschlungen und es direkt im Anschluss auf die ToRead-Liste meiner Azubis gesetzt. Ab sofort werde ich es mit jedem Azubi lesen. Direkt im 1. Ausbildungsjahr, kurz nach der Bearbeitung der ersten Java-Aufgaben, legen wir los.
    Idee des Buchs
    Die Idee des Buchs ist schnell erklärt: Es werden 70 Code-Beispiele in Java gezeigt, an denen jeweils einzelne Optimierungen durchgeführt werden. Der "schlechte" Code steht auf der linken Seite und der "gute" Code steht auf der rechten Seite. Somit kann man direkt die beiden Beispiele vergleichen und sich auf den jeweiligen Unterschied konzentrieren. Im Prinzip gibt es also 70 kurze Lektionen zum Thema "Clean Code".
    Die Beispiele sind interessant und teilweise auch ein bisschen witzig gewählt, da das Grundthema eine Reise zum Mars ist.
    Zielgruppe
    Die Zielgruppe des Buches sind Anfänger bis Fortgeschrittene in der Programmierung. Die Beispiele sind zwar komplett in Java, aber ich würde sagen, dass man sie genauso gut in anderen Sprachen anwenden kann. Einige Beispiele, wie z.B. die Verwendung von Optional, sind zwar sehr spezifisch für Java, aber die grundlegenden Ideen (in diesem Fall null zu vermeiden) lassen sich ohne Probleme auf andere Sprachen übertragen.
    Damit man die Beispiele und Optimierungen auch versteht, sollte man schon ein wenig Programmierererfahrung mitbringen. In meinem Fall lasse ich die Azubis das Buch direkt nach ihrer Einführung in Java lesen. Die grundlegenden Themen wie Objektorientierung, Vererbung, Interfaces und Teile der funktionalen Programmierung sind dann schon bekannt und die weiteren Optimierungen des Buches können verstanden und auch angewendet werden. Das Buch schlägt vor, das "FizzBuzz"-Problem zu lösen. Wenn das klappt, gehört man zur Zielgruppe! Hier habe ich das Problem übrigens selbst schonmal auseinandergenommen: Don't Repeat Yourself (DRY).
    Für mich als Ausbilder ist das Buch ebenfalls sehr interessant, da ich Erklärungen zu Anmerkungen in meinen Code Reviews nun nicht mehr jedesmal ausformulieren und erklären muss, sondern im Prinzip einfach auf das kurze Buchkapitel dazu verweisen kann. Bei meinen Erklärungen schweife ich nämlich gerne auch mal sehr weit ab und am Ende haben wir dann eine halbe Stunde über Themen diskutiert, die mit dem eigentlichen Problem gar nichts mehr zu tun haben. :-) Hier hilft mir das Buch weiter, da jedes Kapitel sich auf ein einziges Problem fokussiert und es in zwei Seiten gut nachvollziehbar erklärt.
    Inhalt des Buches
    Die einzelnen Kapitel des Buches bauen ein wenig aufeinander auf. Es startet mit einfachen Beispielen und wird immer komplexer. Wobei auch die späteren Beispiele immer noch gut verständlich sind. Es geht los mit einfachen Möglichkeiten, den eigenen Code aufzuräumen (z.B. die unten genannten unnötigen Vergleiche). Danach folgen etwas weitergehende Möglichkeiten wie z.B. Enhanced-For-Schleifen statt For-Schleifen einzusetzen. Es gibt ein eigenes Kapitel zum Thema Kommentare und auch zur Benennung von Variablen oder Methoden. Einiges davon wird auch in Clean Code* schon erklärt, aber hier werden halt aussagekräftige, kurze Beispiele mitgeliefert, mit denen auch Einsteiger etwas anfangen können.
    Es gibt des Weiteren ein eigenes Kapitel zur Fehlerbehandlung und insbesondere auch zum Thema Unit-Tests, was ich sehr begrüße. Denn meiner Erfahrung nach haben gerade beim Thema Tests meine Azubis und Studierenden die meisten Probleme. Am Ende des Buches gibt es dann noch ein wenig über das Design von Klassenstrukturen zu lesen und eine kurze Einführung in die funktionale Programmierung mit Java, also beispielsweise Streams und Lambda-Ausdrücke. Auch Methodenreferenzen werden erläutert. Zum Abschluss gibt es dann einen Ausblick auf das "echte Leben". Hier geht es dann z.B. um Continuous Integration oder den Einsatz eines Logging-Frameworks, anstatt alles auf die Konsole zu schreiben.
    Konkrete Beispiele
    Um ein Gefühl für den Inhalt zu vermittelt, beschreibe ich einfach mal drei Beispiele aus dem Buch.
    Unnötige Vergleiche
    Gleich das allererste ist eines, dass mir in der Praxis bei meinen Azubis und Studierenden immer wieder unterkommt: der Vergleich mit booleschen Werten. Es ist völlig unnötig, bei einer Abfrage einen booleschen Wert mit true oder false zu vergleichen. Stattdessen kann dieser direkt in der Abfrage verwendet werden.
    Nicht so gut:
    if (istErwachsen == true) { rabatt = 0; }
    Besser:
    if (istErwachsen) { rabatt = 0; }
    Domänensprache nutzen
    Ein weiteres schönes Beispiel ist die Verwendung von Domänen-Terminologie. Wir schreiben Code für andere Menschen und nicht für den Computer! Daher ist es wichtig, dass fachlich versierte Leser unseres Codes ihn gut verstehen und nicht durch missverständliche Begriffe abgelenkt werden.
    Nicht so gut:
    b = (summeR / lookupTabelleKS) * ax;
    Besser:
    monatsbeitrag = (rueckstellungen / kopfschaden) * rentenbarwart;
    Optionals als Stream verwenden
    Das letzte Beispiel ist sehr spezifisch für Java. Hier geht es um das schon erwähnte Optional. Anstatt die Methode isPresent() zu verwenden, sollte man den funktionalen Ansatz wählen und das Objekt so nutzen, wie es gedacht ist.
    Nicht so gut:
    if (optional.isPresent() { doSomething(optional.get()); }
    Besser:
    optional.ifPresent(this::doSomething);
    Literaturempfehlungen
    Logisch, oder? :-)
    *
    Links
    Permalink zu dieser Podcast-Episode
    RSS-Feed des Podcasts
    Website zum Buch: Become a Better Java Developer in 70 Examples
    Podcast-Interview Simon Harrer: Java by Comparison. Vorher/Nachher-Vergleiche zu Clean Code in Java
    Reviews des Buchs: Java by Comparison bei heise Developer, Java by Comparison bei InfoQ
    Zusammenfassung
    Wie du mit dem Buch lernst
    Nachdem ich Java by Comparison komplett gelesen habe, möchte ich es mit meinen IT-Azubis bereits im ersten Ausbildungsjahr lesen. Das englischsprachige Buch von Simon Harrer, Jörg Lenhardt und Linus Dietz ist bei den Pragmatic Programmers erschienen. Die Autoren haben Java an deutschen Hochschulen gelehrt und ihre Erfahrungen aus der Lehre eingebracht.
    Auf jeweils zwei gegenüberliegenden Seiten findest du ein Codebeispiel und seine überarbeitete Fassung. Dazu erfährst du, warum die Änderungen helfen und wie du sie umsetzt. Insgesamt enthält das Buch 70 Beispiele auf ungefähr 165 Seiten. Als fachlicher Rahmen dient eine Reise zum Mars, bei der unter anderem Raketen und Mars-Rover vorkommen.
    Mit einfachen Regeln für Bedingungen beginnt das Buch, bevor es zu anspruchsvolleren Themen wie funktionaler Programmierung übergeht. Die Beispiele sind kurz und brauchen wenig zusätzlichen Kontext.
    Welche Vorkenntnisse du brauchst
    Wenn du bereits mit Schleifen und Bedingungen programmieren kannst, hast du die Grundlagen für den Einstieg. Damit eignet sich das Buch für IT-Azubis mit ersten Erfahrungen ebenso wie für fortgeschrittene Entwickler:innen und Ausbilder:innen. Ich halte es nach ungefähr einem halben Jahr eigener Programmierpraxis für zugänglich.
    Als Einstiegstest schlägt das Buch FizzBuzz vor. Dabei gibst du die Zahlen von 1 bis 100 aus und ersetzt bestimmte Zahlen:
    Bei durch 3 teilbaren Zahlen erscheint Fizz.
    Bei durch 5 teilbaren Zahlen erscheint Buzz.
    Ist eine Zahl durch beide teilbar, erscheint FizzBuzz.
    Alle übrigen Zahlen gibst du unverändert aus.
    Kannst du diese Aufgabe selbstständig in ungefähr einer Viertelstunde lösen, bringst du die empfohlenen Vorkenntnisse mit.
    Obwohl alle Beispiele in Java geschrieben sind, lassen sich viele Regeln auf ähnliche Sprachen wie C# oder PHP übertragen. Spätere Kapitel behandeln Java-Konzepte wie Streams und Optionals. Vergleichbare Ansätze gibt es auch in anderen Sprachen, etwa LINQ in C#.
    Welche Themen das Buch behandelt
    Beim Lesen arbeitest du dich durch folgende Bereiche:
    Bedingungen und grundlegende Sprachmittel: Boolesche Werte direkt abfragen, positiv formulierte Begriffe wählen und bei if-Konstrukten konsequent Klammern setzen.
    Kommentare und lesbarer Code: Durch sprechende Methodennamen bleibt Code verständlich. Benannte Konstanten machen die Bedeutung von Zahlen im Code sichtbar.
    Namen und Domänensprache: Begriffe aus dem Fachbereich helfen dir, Variablen und Methoden nachvollziehbar zu benennen.
    Fehler behandeln: Ressourcen zuverlässig schließen, etwa mit Javas try-with-resources, das Dateien automatisch schließt.
    Unit-Tests: Tests sinnvoll aufbauen, die Argumente bei assertEquals richtig anordnen und Rundungsfehler bei Gleitkommazahlen berücksichtigen.
    ...
    28 min
  • Umschulung zum Fachinformatiker mit Jan Bauer – Anwendungsentwickler-Podcast #129
    Ein interessantes Interview mit Jan Bauer, einem Experten für Umschulungen im IT-Bereich, führe ich in der einhundertneunundzwanzigsten Episode des Anwendungsentwickler-Podcasts.
    TL;DR
    Mit Jan Bauer bespreche ich, wie du über eine Umschulung zum/zur Fachinformatiker:in einen IT-Beruf erlernen kannst. In Vollzeit dauert sie meist zwei Jahre und führt zum gleichen IHK-Abschluss wie die duale Ausbildung, wobei ein Kostenträger häufig die Lehrgangskosten und deinen Lebensunterhalt übernimmt. Bei einem Bildungsträger lernst du die Theorie und erste praktische Grundlagen, bevor du im Betrieb meist sechs Monate Praxis sammelst. Für den Berufseinstieg zählen deine eigenen Programmiererfahrungen und deine Bereitschaft, weiterzulernen; das Praktikum bietet zugleich die Chance, anschließend im Betrieb zu bleiben.
    Kapitelmarken
    [00:00:00] Einstieg: Umschulungen in der IT
    [00:01:19] Jan Bauer und seine Arbeit als Ausbilder
    [00:03:48] Jans Weg vom Quereinstieg zur Umschulung
    [00:06:38] Programmiersprachen passend zum Projekt wählen
    [00:08:49] Gleicher IHK-Abschluss und damalige Prüfungsregeln
    [00:10:10] Betriebliche und außerbetriebliche Umschulung
    [00:12:24] Dauer in Vollzeit und Teilzeit
    [00:13:34] Betrieb und Berufsschule bei einer Umschulung
    [00:14:42] Ablauf beim Bildungsträger bis zur Prüfung
    [00:18:03] Lehrgangskosten und Kostenträger
    [00:19:47] Einen Praktikumsplatz finden
    [00:21:21] Kosten und Aufgaben des Praktikumsbetriebs
    [00:22:48] Geld für den Lebensunterhalt
    [00:24:44] Gründe für eine Umschulung und Interesse an IT
    [00:27:42] Kursstarts und Gruppengrößen
    [00:30:31] Unterricht, virtuelle Klassenzimmer und Betreuung
    [00:34:49] Zeit für Theorie und eigene Programmierpraxis
    [00:39:07] Coding-Bootcamps und Erfahrung beim Programmieren
    [00:42:18] Mit IHK-Aufgaben auf die Prüfung vorbereiten
    [00:45:07] Vorbehalte bei Betrieben und Chancen durch das Praktikum
    [00:48:33] Anforderungen an Praktikumsplätze und Berufseinstieg
    [00:51:39] Hinweise für Betriebe, die Umschüler:innen ausbilden
    [00:54:55] Programmiersprachen und Austausch mit dem Bildungsträger
    [00:59:51] Bücher, Stack Overflow und Englischkenntnisse
    [01:04:01] Informatiklehrbücher und ergänzende Erklärungen
    [01:07:29] IT-Azubis individuell begleiten
    [01:09:45] Jan per E-Mail erreichen
    [01:10:49] Kommentare und weitere Fragen
    Inhalt
    Allgemeines zur Person
    Wie ist dein Name und wo arbeitest du?
    Jan Bauer, 48 Jahre alt, Fachkraft für Aus- und Weiterbildung beim Volkswagen-Bildungsinstitut.
    Wir bilden Berufe aus, die VW braucht, insb. auch Fachinformatiker.
    Vorher war ich 9 Jahre als Dozent und Lernbegleiter tätig in der Umschulung für mehrere IT-Berufe.
    An welchen Projekten arbeitest du zur Zeit in deinem Tagesjob?
    Ich bin Ausbilder für Fachfinformatiker Anwendungsentwicklung. Ich programmiere täglich, aber eher kleine Projekte, da die Ausbildung im Vordergrund steht.
    Wie bist du zur Informatik bzw. Softwareentwicklung gekommen?
    Ich habe klassisch mit dem C64 begonnen, dann zum Amiga gewechselt. Informatik war eher ein Hobby. Mein Studium habe ich im Bereich Ingenieurswesen absolviert, aber viel nebenbei in der IT gearbeitet.
    Im Internetboom hatte ich keine Probleme, als Quereinsteiger einen Job zu finden, und habe bei Lycos gearbeitet. Dort habe ich viel gelernt und gute Jahre gehabt. Aber dann ging die New Economy unter.
    2006/2007 war es schwierig, ohne formale Ausbildung einen Job zu bekommen und eine "normale" Ausbildung war aufgrund meines Alters schwierig.
    Welche Ausbildung bzw. welches Studium hast du im Bereich der Informatik absolviert?
    Aus obigem Grund habe ich selbst eine Umschulung zum Fachinformatiker Anwendungsentwicklung absolviert.
    Aktuell studiere ich nebenbei Wirtschaftsinformatik.
    Mit welcher/n Programmiersprache/n arbeitest du im Alltag?
    C#
    Was ist deine Lieblingsprogrammiersprache und warum?
    C# ist sehr modern, aber ich mag auch PHP im privaten Bereich.
    Ich bin da sehr undogmatisch und finde es langweilig, darüber zu diskutieren, welche Sprache die beste ist. Alle Sprachen haben Stärken/Schwächen.
    Umschulung zum Fachinformatiker
    Warum hast du eine Umschulung absolviert?
    Mir hat ein formaler Abschluss gefehlt.
    Warum hast du dich für eine Umschulung entschieden und machst nicht eine normale Ausbildung?
    Mit Mitte 30 kam für mich eine normale Ausbildung nicht in Frage.
    Welche Arten der Umschulung gibt es?
    Normale Ausbildung: Betrieb + Berufsschule.
    Betriebliche Umschulung: Betrieb + Berufsschule, aber Einstieg ins 2. Lehrjahr, da vorher Berufsausbildung absolviert und Grundlagen bekannt sind.
    Außerbetriebliche Umschulung: Bildungsträger (quasi wie Berufsschule, der die Theorie vermittelt) + Praktikum im Betrieb (6 Monate waren vorgegeben).
    Welche Unterschiede gibt es zwischen Umschulung und Ausbildung?
    Umschüler haben oft keine Zwischenprüfung. Die Berufsschule wird durch den Bildungsträger ersetzt. Die Praxis im Unternehmen dauert oft nur 6 Monate. Es gibt Bildungsträger, die Zwischenprüfungen machen (rein freiwillig). Die Dauer des Praktikums ist abhängig davon, was die örtliche Kammer vorschreibt. Es gibt durchaus auch längere Praktika.
    Wie lange dauert eine Umschulung?
    Nicht pauschal zu sagen. Vollzeit dauert normalerweise 2 Jahre, aber es gibt auch Bildungsträger, die 2,5 Jahre anbieten inkl. einiger Zertifikate.
    Die vorhandene Förderung bestimmt die Dauer. Bildungsgutscheine sind meist nur für 2 Jahre gültig.
    Teilzeitumschulungen gibt es auch, z.B. 4 Stunden/Tag vor Ort, dafür aber 3 Jahre lang.
    Eine Umschulung dauert nicht exakt 24 Monate, sondern einige Monate weniger, da nach der Prüfung Schluss ist.
    Der genaue Ablauf kann sich je nach Bildungsträger unterscheiden. Bei meinem Träger: 1 Jahr waren alle Teilnehmer vor Ort für Schulungen und die Vermittlung von Praxis. Ein guter Einstieg ins Praktikum sollte möglich sein. Danach gab es ein halbes Jahr Praxis im Unternehmen und danach kamen die Umschüler zurück zur Prüfungsvorbereitung (6-8 Wochen vor der Prüfung) und zur Dokumentation der Projektarbeit. Die Zeit bis zur mündlichen Prüfung haben wir für das Üben der Präsentation usw. gebraucht.
    Was hat der Bildungsträger davon?
    Der Bildungsträger bekommt Geld dafür. Eine Umschulung zum FIAE kostet ca. 20.000-30.000 EUR.
    Wer bezahlt die Umschulung?
    Der Kostenträger (z.B. Agentur für Arbeit, Jobcenter, Rentenversicherung, Bundeswehr, Selbstzahler) bezahlt die Bildungsträger und die Umschüler.
    Wie findet man einen Praktikumsbetrieb?
    Die Umschüler sollten sich selbst bewerben. Eigeninitiative ist gefordert (und gewünscht).
    Kontakte der Bildungsträger zu Unternehmen sind aber auch vorhanden und helfen zur Not.
    Was haben die Unternehmen davon, Umschüler zu beschäftigen?
    Ein Umschüler ist für den Praktikumsbetrieb zunächst kostenfrei. Der Bildungsträger übernimmt die Sozialversicherung usw.
    Unternehmen haben aber trotzdem Kosten durch Umschüler (z.B. für den Ausbilder usw.).
    Der Betrieb bekommt kein Geld für die Umschüler.
    Ausblick auf potentielle Arbeitskraft ist hilfreich.
    Wovon bestreitet ein Umschüler seinen Lebensunterhalt?
    Der Kostenträger zahlt, z.B. Arbeitslosengeld 1 oder 2.
    Wer macht allgemein eine Umschulung?
    Menschen, die in ihrem alten Job nicht mehr arbeiten können oder wollen.
    Das muss aber gut begründet sein, da der Kostenträger überzeugt werden muss.
    Einige Umschüler werden auch durch ihre Berater in die Umschulung bzw. gewisse Berufe gedrängt. Beispiel: Intelligenter junger Mann, der aber keinerlei IT-Affinität hatte, wurde zum FIAE angemeldet, weil das ein guter Job sei.
    Wie viele Umschüler sitzen in einem Kurs?
    Das schwankt extrem, da die Politik unterschiedlich stark fördert. Üblicherweise haben wir 15-45 Leute im Jahrgang, von denen die ITler nur einen Teil bilden, ca. 3-10 Leute.
    Unterstützen die Dozenten die Umschüler gut? Wie sieht dein Alltag als Umschüler aus?
    Die meiste Zeit geht für die Vermittlung theoretischer Inhalte drauf. Die sind für alle IT-Berufe gleich, da die Rahmenlehrpläne zu großen Teilen gleich sind.
    Die Praxis ist schwierig zu vermitteln, gerade für FISI und FIAE. Da sind eigene Projekte nötig, um gut auszubilden. Fallstudien helfen hier weiter.
    Es besteht eine Art Lehrer-/Schüler-Verhältnis zwischen Dozenten und Umschülern.
    Es wird nicht komplett Frontalunterricht gemacht, sondern eher Fallstudien bearbeitet. Auch Bücher zu lesen kann ein Auftrag sein.
    Virtuelle Klassenzimmer werden teils komplett ohne Präsenzunterricht eingesetzt.
    Die Bedürfnisse der Teilnehmer unterscheiden sich stark, was ein großes Problem für viele Teilnehmer ist. Ein großer Kritikpunkt an Umschulungen ist oft die schlechte Betreuung.
    Braucht man viel eigene Motivation für die Umschulung?
    Das ist sehr typabhängig. Grundsätzlich ist aber gerade für FIAE viel intrinsische Motivation nötig, um vernünftig programmieren zu lernen.
    Was sind die Inhalte der Umschulung? Macht man in 2 Jahren den Inhalt von 3 Ausbildungsjahren?
    Ich habe mal ausgerechnet, dass wir ca. 2,5x so viel Zeit für die Theorievermittlung in der Umschulung haben wie es Stunden in der Berufsschule für die Ausbildung gibt. Und vieles fällt weg: Deutsch, Ethik, Sport.
    Die Theorienoten unserer Umschüler waren immer besser als der IHK-Schnitt.
    Aber die Praxis fehlte, die natürlich aber wichtig ist für den späteren Job.
    Meine Erwartung an angehende FIAEler ist daher, dass man auch zuhause programmieren muss. Nicht weil das Pflicht ist, sondern weil man es sonst nicht vernünftig lernt.
    Wie bereitet man sich auf die Abschlussprüfung vor?
    Übungsaufgaben habe ich auch zum Einstieg wie die IHK-Aufgaben gestellt. Die Prüfungsvorbereitung war in die Umschulung komplett integriert. Wir haben auch Aufgaben aus der Zwischenprüfung verwendet.
    Das Abschlussprojekt wird im Praktikum im Unternehmen umgesetzt.
    Nach der langen Praxisphase ist häufig viel Theoriewissen wieder weg und muss neu aufgebaut werden kurz vor der Prüfung.
    Wie ist deine Einschätzung zur Anerkennung der Umschulung bei Arbeitgebern?
    ...
    1 hr 12 min
  • Testen von Java-EE-Anwendungen mit Matthias Bünger – Anwendungsentwickler-Podcast #128
    Wie steigt man in das automatisierte Testen einer Java-EE-Anwendung ein, wenn man bereits eine bestehende Anwendung hat und bislang nicht getestet hat? Diese und weitere Fragen kläre ich im Interview mit Matthias Bünger in der einhundertachtundzwanzigsten Episode des Anwendungsentwickler-Podcasts.
    TL;DR
    Bei bestehenden Java-EE-Anwendungen starte ich mit schnellen Unit-Tests für die Fachlogik und ergänze schrittweise Tests für die Infrastruktur. Über Konstruktor-Injection kannst du Abhängigkeiten direkt übergeben und Klassen im Java-SE-Kontext testen. Echte Implementierungen kommen infrage, wenn sie schnell und zuverlässig laufen; Zugriffe auf externe Ressourcen ersetze ich zunächst durch Test doubles. Arquillian verwende ich für wenige umfassende Tests im Application-Server, die auch das Zusammenspiel mit der echten Datenbank prüfen.
    Kapitelmarken
    [00:00:01] Einstieg ins Testen von Java-EE-Anwendungen
    [00:02:34] Matthias Bünger und sein Arbeitsumfeld
    [00:05:09] Aufbau der Backend-Anwendung und XML-Verarbeitung
    [00:07:57] Vorhandene Tests und Fragen zu Arquillian und Mocking
    [00:10:50] Von der Fachlogik nach außen testen
    [00:13:39] Automatische Regressionstests und reproduzierbare Testdaten
    [00:15:59] Aufwand und Einsatz von Arquillian
    [00:18:05] Domäne isolieren und Komponenten verbinden
    [00:19:39] Persistenz mit lokalen und echten Datenbanken testen
    [00:21:11] Testpyramide und vollständige Benutzerabläufe
    [00:22:28] CDI und Logging im bestehenden Projekt
    [00:24:12] Abhängigkeiten durch Konstruktor-Injection sichtbar machen
    [00:27:09] Logger übergeben und bestehende Klassen testbar machen
    [00:29:48] Objekte und Abhängigkeiten im Test vorbereiten
    [00:32:10] Lesbare Tests mit sprechenden Hilfsmethoden
    [00:33:59] Welche Abhängigkeiten werden gemockt?
    [00:38:43] Schnelle Tests und Infrastrukturtests getrennt halten
    [00:40:16] Den Einstieg auf Fachlogik und kurze Laufzeiten ausrichten
    [00:42:48] Oracle oder lokale Datenbank als Testziel
    [00:45:01] Datenbankrechte und getrennte Testkonfigurationen
    [00:46:12] JPA im Java-SE-Kontext mit eigenem EntityManager
    [00:49:10] Datenbanktests mit Arquillian abwägen
    [00:51:34] Umfangreiche Mock-Setups als Anlass zum Refactoring
    [00:54:17] Austauschbare Abhängigkeiten früh einplanen
    [00:56:16] Quellen zum Testen und Unterschiede zwischen Stubs und Mocks
    [00:58:54] Mockito als Framework für den Einstieg
    [01:00:36] Clean Code und Continuous Integration
    [01:02:21] Maven und plattformunabhängige Builds
    [01:04:07] Kontakt und Fragen zur Episode
    [01:05:14] Praktische Ausbildung und Hürden beim Einstieg
    [01:07:16] Weitere Fragen und Shownotes
    Inhalt
    Allgemeines zur Person
    Wie ist dein Name und wo arbeitest du?
    Matthias Bünger, aus Bonn, beschäftigt beim ITZBund (Informationstechnikzentrum Bund, IT-Dienstleister).
    An welchen Projekten arbeitest du zur Zeit in deinem Tagesjob?
    Qualitätssicherung, Buildmanagement, automatisches Testen.
    Automatisierung ist mir sehr wichtig. Ich möchte die Grundlagen für automatische Tests schaffen.
    Ansonsten bin ich hauptsächlich in der Backend-Anwendung tätig.
    Welche Ausbildung bzw. welches Studium hast du im Bereich der Informatik absolviert?
    Nach der Schule habe ich eine Ausbildung zum Fachinformatiker Anwendungsentwicklung absolviert.
    Danach habe ich noch Informatik studiert.
    Mit welcher/n Programmiersprache/n arbeitest du im Alltag?
    Java (EE)
    Was ist deine Lieblingsprogrammiersprache und warum?
    Java
    Automatisiertes Testen von Java-EE-Anwendungen
    Mein aktueller Wissensstand zum Testen ist etwas durcheinander. Ich bin verwirrt durch zahlreiche Blog-Beiträge, die unterschiedliche Meinungen propagieren. Zum Beispiel: "Mocke nur, was du unter Kontrolle hast!" oder "Mocks sind gemacht worden, um sie wieder abzuschaffen!". Ja was denn nun?
    Gibt es aktuell noch gar keine Tests in eurer Anwendung?
    Es gibt ein paar Unit-Tests ohne Mocking oder Dependency Injection.
    In vielen Komponenten werden Logger eingesetzt, die die Tests erschweren.
    Ich habe eine Schulung zu Arquillian besucht und kann theoretisch damit loslegen.
    Was mich abschreckt ist das Deployment bei Arquillian, das recht lange dauert.
    Was macht die Anwendung überhaupt? Welche Frameworks/Technologien werden eingesetzt?
    Backend zur Verarbeitung von XML-Dateien, die ins Dateisystem geliefert werden.
    Die Daten aus den XML-Dateien werden dann mit JPA in eine Datenbank geschrieben, von wo aus sie weiter verarbeitet werden.
    Fachlich geht es um Freistellungsaufträge von Banken.
    Unsere Herausforderungen sind die parallele Verarbeitung und eine hohe Last. Im Hintergrund läuft WebSphere.
    Die fachliche Logik ist nicht allzu komplex.
    Was sind deine Herausforderungen beim Testen?
    Wir verwenden häufig einen Logging Interceptor. Wie mockt man den?
    Wir setzen stark auf CDI mit @Inject an den Attributen. Wie testet man das?
    Ansonsten gibt es viele Data Access Objects.
    DB-Zustände müssen evtl. zum Testen hergestellt und danach zurückgesetzt werden.
    Wie findet man einen Einstieg ins Testen?
    Ich würde mit Unit-Tests für die Fachlichkeit beginnen. Dieser Teil der Anwendung "verdient das Geld".
    Die EE-Technik geht eher nicht kaputt und muss nicht zu Beginn getestet werden. Das bringt kaum Mehrwert.
    Ich würde nicht mit Arquillian beginnen, sondern mit "einfachen" Unit-Tests mit JUnit, ggfs. unterstützt durch Mocks mit Mockito.
    Was mockst du?
    Ich habe verschiedene Meinung gelesen: Nur mocken, was man vollständig versteht. Niemals Fremdimplementierungen mocken. Keine Legacy-Anwendungen mocken.
    Das sehe ich anders: Ich mocke alles, was mir im Weg steht und insb. die Infrastruktur berührt (Netzwerk, Datenbank, Dateisystem).
    Wenn die Mocks zu aufwändig werden, ist das ein Zeichen dafür, dass die zu testende Klasse verkleinert werden muss.
    Würdest du auch die Datenbank mocken?
    Erstmal ja, um die Tests zu beschleunigen.
    Aber danach würde ich gegen eine lokale oder In-Memory-DB testen und am Schluss auch gegen eine "echte" Datenbank, z.B. eine Entwicklungsdatenbank.
    Die jeweilige Zieldatenbank (z.B. Oracle) ist halt nicht H2. Die Systeme verhalten sich unterschiedlich. Und wichtig ist, dass die Software später auf dem echten Zielsystem lauffähig ist.
    Ich würde die echten DB-Tests aber nicht mit EE im Application Server laufen lassen, sondern komplett ohne Arquillian mit einem JPA-Provider wie EclipseLink oder Hibernate im SE-Kontext. Das ist schneller als ein komplettes Deployment.
    Welche DB-Benutzer verwendest du zum Testen?
    Technische User mit erweiterten Rechten zum Anlegen von Schemas, z.B. bei DB-Migrations nötig.
    Ich setze auf zwei verschiedene Persistence-Units für main und test, damit der Produktivcode keine Ahnung von der zweiten DB haben muss.
    Welche Ressourcen kannst du zum Thema empfehlen?
    "Test automation without a headache: Five key patterns" von Gojko Adzic auf der "Norwegian Developers Conference 2016" in London
    Martin Fowler - Mocks Aren't Stubs: Hier werden die Begriffe Stub, Mock, Spike usw. erklärt und abgegrenzt, da sie häufig falsch benutzt werden.
    Hast du eine allgemeine Buchempfehlung?
    Clean Code* finde ich sehr wichtig, auch wenn ich nicht alle Inhalte teile. Ich habe daraufhin CheckStyle usw. eingeführt und achte mehr auf Code-Qualität und Wartbarkeit.
    Alles rund um Continuous Integration! Automatisierung ist viel wert! Einmal aufsetzen, für immer die Vorteile genießen. Für mich wichtig ist die erreichte Plattformunabhängigkeit, keine Dependencies, die ich im Classpath manuell setzen muss etc. Bei uns kann mit Git und Maven jeder direkt in neue Projekte einsteigen. Außerdem findet der CI-Prozess auch Fehler in den Projekten.
    Abschluss
    Wo können die Hörer mehr über dich erfahren bzw. dich kontaktieren?
    Ich bin nicht online vertreten. Facebook halte ich privat. Höchstens bei StackOverflow oder bei einer Bewerbung auf unsere Stellenausschreibungen! ;-)
    Literaturempfehlungen
    *
    Links
    Permalink zu dieser Podcast-Episode
    RSS-Feed des Podcasts
    Test automation without a headache: Five key patterns von Gojko Adzic auf der "Norwegian Developers Conference 2016" in London
    Mocks Aren't Stubs von Martin Fowler
    100% Code Coverage - TDD mit Java EE von Stefan Macke auf der JavaLand 2018
    Zusammenfassung
    Ausgangspunkt: Eine bestehende Java-EE-Anwendung testen
    Im Gespräch mit Matthias Bünger gebe ich dir Einblicke aus meinen Projekten. Matthias arbeitet beim ITZ Bund an einer Backend-Anwendung, die XML-Dateien mit Meldungen zu Freistellungsaufträgen einliest und über JPA in einer relationalen Datenbank verarbeitet. Durch hohe Last und parallele Abläufe entstehen besondere Herausforderungen. Die vorhandenen Unit-Tests prüfen vor allem Hilfsfunktionen für Strings; die fachlichen Regeln sollen zusätzlich automatisch getestet werden.
    Mit automatischen Tests kannst du nach Änderungen auch bereits vorhandene Funktionen erneut prüfen. Das hilft etwa bei einem Hotfix. Für reproduzierbare Ergebnisse brauchst du definierte Testdaten und einen Ausgangszustand, den du vor jedem Test wiederherstellst.
    Mit der Fachlogik beginnen
    Für den Einstieg arbeite ich mich von der fachlichen Domäne nach außen vor. Eine Berechnung lässt sich mit einem selbst erzeugten Java-Objekt testen, dessen Eigenschaften du passend zum Testfall setzt. Die Domäne sollte möglichst frei von technischen Abhängigkeiten sein, damit solche Tests direkt im Java-SE-Kontext laufen.
    Anschließend verbinde ich Komponenten miteinander und prüfe ihre Zusammenarbeit. Sobald eine Komponente auf Infrastruktur zugreift, ersetze ich diese Abhängigkeit zunächst durch ein Test double. Ein Repository kann beispielsweise vorgegebene Datensätze liefern.
    Bei einer neuen Anforderung habe ich auch schon mit einem umfassenden Integrationstest begonnen, wie es im Buch Growing Object-Oriented Software, Guided by Tests beschrieben wird. Dieser Test bleibt zunächst rot, während kleinere Unit-Tests die einzelnen Schritte begleiten. Sobald der große Test erfolgreich durchläuft,...
    1 hr 10 min

About IT-Berufe-Podcast

From the publisher's feed

Der Podcast rund um die Ausbildung in den IT-Berufen (insb. Fachinformatiker für Anwendungsentwicklung) von Stefan Macke.

More shows like IT-Berufe-Podcast

Streitkräfte und Strategien by NDR

Streitkräfte und Strategien

43 Listeners

Sternengeschichten by Florian Freistetter

Sternengeschichten

45 Listeners

Geschichten aus der Geschichte by Richard Hemmer und Daniel Meßner

Geschichten aus der Geschichte

176 Listeners

c’t uplink - der IT-Podcast aus Nerdistan by c’t Magazin

c’t uplink - der IT-Podcast aus Nerdistan

10 Listeners

MORD AUF EX by Leonie Bartsch & Linn Schütze

MORD AUF EX

112 Listeners

{ungeskriptet} - Der Meinungsfreiheit verpflichtet. by Ben Berndt

{ungeskriptet} - Der Meinungsfreiheit verpflichtet.

48 Listeners