SOLID
Motivation
- The more things change, the more they break
- Anforderungen ändern sich ⟹ Code ändert sich

Agile

Ziel
ETC - easier to change
- Clean Code
- Code leichter lesbar ⟹ ETC
- DRY - Don't repeat yourself
- eine Änderung ⟹ ETC
- Orthogonalität
- Keine Codeverflechtungen ⟹ ETC
- z.B. Datenbank wird von MySQL auf PostgreSQL portiert, GUI bleibt gleich
Namen
- Principle of least Surprise
- Variablen, Funktionen, Klassen sinnvoll benannt
- Name > Typ
- Keine Abkürzungen, Klarheit > Kürze
❌ int x = 5;
✅ var age = 5;
❌ List<Student> list = ...;
✅ var students = ...;
❌ bool f = true;
✅ var isActive = true;
Funktionen
- Funktion macht nur eine Sache
- Viele kurze Funktionen (< 20 lines)
- < 4 Parameter
- ähnliche Parameter in Objekte zusammenfassen
public void processOrder(Order order) {
// validate the order
code
// save to database
code
// send confirmation mail
code
} ❌
public void processOrder(Order order) {
validate(order);
save(order);
sendConfirmationMail(order.customer);
} ✅
Formatting
- Konsistent
- Leerzeilen zur Unterteilung
- Linter
public int sum(int a, int b) { return a + b; } ❌
public int sum(int a, int b) {
return a + b;
} ✅
Duplication
- Duplication vermeiden
- Redundanz
- Single source of truth
if(user.getAge() > 18) { ... }
...
if(customer.getAge() > 18) { ... }
... ❌
boolean isAdult(Person person) {
return person.getAge() > 18;
} ✅
class Person {
public boolean isAdult() {
return age > 18;
}
} ✅
Lesbarkeit
- Code sollte eine Geschichte erzählen
- Don't try to be clever
- Simple control flows
nums = [i for i in range(100) if not i%2 and i%3] ❌
nums = []
for i in range(100):
if i % 2 == 0 and i % 3 != 0:
nums.append(i) ✅
Kommentare
- Unfähigkeit, Gedanken in Code zu fassen
- Warum?
- Müssen gewartet werden
// session-id ❌
int sessionId = 0;
// expiry date ❌
Date expiry = new Date();
// if during work hours ❌
if (currentTime.hours >= 9 && currentTime.hours <= 17) { ... }
// Matches a Dutch zipcode, eg, '1974 XA' or '4844RA' ✅
Pattern DUTCH_ZIPCODE = Pattern.compile("[1-9][0-9]{3} ?[A-Z]{2}");
var duringWorkHours = currentTime.hours >= 9 && currentTime.hours <= 17; ✅
if (duringWorkHours) { ... }
Komplexe Zusammenhänge
var resultCode = ...;
var isSucess = resultCode == 200;
var isTemporaryError = resultCode == 503 || resultCode == 504;
// Sometime we receive a temporary error.
// However, in all cases which were researched, results were successfully stored.
// Because of this we will return true in that case.
return (isSucess || isTemporaryError); ✅
// Don't rewrite this to try-with-resources because that will
// auto-close the socket. The socket is needed every 15 minutes so the
// error might not show directly but this will break the application
try {
... open a socket
} catch (IOException e) {
... handle error
} ✅
Testbarkeit
- Automatisierte Tests
- Nicht nur happy path
- Testqualität >> Codequalität
SOLID
Guidelines, keine Regeln
"Uncle Bob" Robert C. Martin
- Single responsibility principle
- Open closed Principle
- Liskov substitution principle
- Interface segregation principle
- Dependency inversion principle
SRP - Single responsibility principle

SRP - Single responsibility principle
Edsger W. Dijkstra - Seperation of concerns
Gather together the things that change for the same reasons. Separate things that change for different reasons.
public class Employee {
public Money calculatePay()
public void save()
public String reportHours()
}
- Buchhaltung verlangt Änderung ⟹ Recompile
- DBA verlangt Änderung ⟹ Recompile
- HR verlangt Änderung ⟹ Recompile
class Book {
public String getTitle()
public String getAuthor()
public Page nextPage()
public void printCurrentPage()
public void save()
}
class Book {
public String getTitle()
public String getAuthor()
public Page nextPage()
public Page getCurrentPage()
}
interface Printer {
void printPage(Page page);
}
class PlainTextPrinter implements Printer {
public void printPage(Page page) {}
}
class HtmlPrinter implements Printer {
public void printPage(Page page)
}
Dependency Inversion Principle

Dependency Inversion Principle
- High-level modules should not depend on low-level modules. Both should depend on the abstraction.
- Abstractions should not depend on details. Details should depend on abstractions.
Mit DIP
- Klassen haben nur Referenzen auf Interfaces
- Detail-Klassen implementieren diese Interfaces
- Wie kommt das Objekt in die Klasse?
Dependency Injection
public class TextEditor {
private SpellChecker checker;
public TextEditor() {
this.checker = new EnglishSpellChecker();
}
}
"new is glue"
public class TextEditor {
private SpellChecker checker;
public TextEditor(SpellChecker checker) {
this.checker = checker;
}
}
Open closed principle

Open closed principle
A Module should be open for extension but closed for modification.
Code ist so zu schreiben, dass neue Features eingefügt werden können, ohne den bestehenden Code zu verändern.
Extension? ⟹ extends!

import Eagle;
class App {
public static void main(String[] args) {
Eagle eagle = new Eagle();
eagle.makeSound();
eagle.fly();
}
}
makeSound returnt String ⟹ 🤔
Vererbung
- +
- DRY - Code existiert nur einmal
- Polymorphie
class Derived extends Super {
public int foo() {
return getX() + super.foo();
}
}
- Erzeugt Abhängigkeit ⟹ nicht ETC
- ++
- DRY - Auslagern in eigene Klasse (SRP)
- Polymorphie - Interfaces
public interface CalculatorOperation { }
public class Addition implements CalculatorOperation{
private double left;
private double right;
private double result;
...
}
public void calculate(CalculatorOperation operation) {
if (operation instanceof Addition addition) {
var result = addition.getLeft() + addition.getRight();
addition.setResult(result);
} else if (operation instanceof Subtraction subtraction) {
var result = subtraction.getLeft() - subtraction.getRight();
subtraction.setResult(result);
}
}
Customer will Multiplication
⟹ Änderung notwendig ⟹ nicht OCP
public interface CalculatorOperation {
double perform();
}
public class Addition implements CalculatorOperation{
private double left;
private double right;
@Override
public double perform() {
return left + right;
}
}
public void calculate(CalculatorOperation operation) {
var result = operation.perform();
}
OCP ✔️
sealed
sealed class Animal permits Dog, Cat, Alien { }
class Fish extends Animal { } 🚫
final class Dog extends Animal { }
sealed class Cat extends Animal permits Tiger { }
final class Tiger extends Cat { }
non-sealed class Alien extends Animal { }
var sound = switch(animal) {
case Dog dog -> dog.bark();
case Tiger tiger -> tiger.roar();
case Alien alien -> alien.sound();
// no default needed
}

Liskov substitution principle
Sei q(x) eine beweisbare Eigenschaft von Objekten x des Typs T. Dann soll q(y) für Objekte y des Typs S wahr sein, wobei S ein Untertyp von T ist
A program that uses an interface must not be confused by an implementation of that interface.
- normalerweise ✔️
- Verletzung: teilweises Implementieren eines interfaces
public class UserAdder {
public void addUser(Collection<User> users, User user) {
users.add(user);
}
}
SRP ✔️
var adder = new UserAdder();
var users = List.of(new User("Albert"), new User("Bernd"));
adder.addUser(users, new User("Christina"));
Exception in thread "main" java.lang.UnsupportedOperationException
Interface segregation principle

Interface segregation principle
Keep interfaces small so that users don’t end up depending on things they don’t need.
public interface FileStorer {
byte[] load(Path path);
void delete(Path path);
void save(Path path, Stream<String> lines);
boolean exists(Path path);
void createDirectory(Path path);
Stream<Path> getSubDirectories(Path path);
Stream<Path> getFiles(Path path);
}
Aus bestehendem interface wird für jede Client-Rolle ein neues interface abgesondert
RoleInterfaces


SRP vs ISP
- SRP trifft Aussagen über die Implementierung
- ISP trifft Aussagen über Interfaces
- SRP Verletzung
- neue Klassen
- neue Rollen
- ISP
Zusammenfassung
- SRP - nur eine Sache machen
- OCP
- Klassen extenden keine Klassen
- Composition > Inheritance
- alte Funktionalität nicht beeinflussen
- LSP - Interfaces vollständig implementieren
- ISP - SRP auch für Interface
- DIP
- alle Typen Interfaces (außer domain)
- kein new (außer domain)
- Testbarkeit ✔️
- Ersetzbarkeit ✔️
- Wiederverwendbarkeit ✔️