sealed interface Rechnung
permits InterneVerechnung, ExternVersandt {}
record ExternVersandt(Kunde kunde, double wert)
implements Rechnung {}
record InterneVerechnung(String abteilung, double wert)
implements Rechnung {}
Merlin Bögershausen / @mboegie
Heavy use of preview features!
Data is Data and nothing but the data
Strict separation between data and logic
like functional programming
sealed interface Rechnung
permits InterneVerechnung, ExternVersandt {}
record ExternVersandt(Kunde kunde, double wert)
implements Rechnung {}
record InterneVerechnung(String abteilung, double wert)
implements Rechnung {}
// omitted interface Kunde and record Businesskunde
record Privatkunde(String name, List<String> mail)
implements Kunde {
Privatkunde {
Objects.requireNonNull(name);
mail = List.copyOf(mail);
}
}
static double calculateMwSt(Kunde kunde, double wert) {
if (kunde instanceof Privatkunde)
return calculateMwSt((Privatkunde) kunde, wert);
else if (kunde instanceof Businesskunde)
return calculateMwSt((Businesskunde) kunde, wert);
else
throw new IllegalArgumentException(/* */);
}
static double calculateMwSt(Privatkunde p, double wert) {
return wert * 0.1d;
}
static double calculateMwSt(Businesskunde b, double wert) {
return b.isVorsteuerAbzugsberechtigt() ? 0d : wert * 0.1d;
}
static double calculateMwSt(Kunde kunde, double wert) {
if (kunde instanceof Privatkunde) {
return wert * 0.1d;
} else if (kunde instanceof Businesskunde b) {
if (b.isVorsteuerAbzugsberechtigt()) return 0.0d;
else return wert * 0.1d;
} else {
throw new IllegalArgumentException(/* */);
}
}
static double calculateMwSt(Kunde kunde, double wert) {
return switch (kunde) {
case Privatkunde p -> wert * 0.1d;
case Businesskunde b
when b.isVorsteuerAbzugsberechtigt() -> 0.0d;
case Businesskunde b -> wert * 0.1d;
};
}
static double calculateMwSt(Kunde kunde, double wert) {
return switch (kunde) {
case Privatkunde p -> wert * 0.1d;
case Businesskunde(String n, var m, var noMwSt)
when noMwSt -> 0.0d;
case Businesskunde b -> wert * 0.1d;
};
}
static double calculateMwSt(Kunde kunde, double wert) {
return switch (kunde) {
case Privatkunde _ -> wert * 0.1d;
case Businesskunde(_, _, var noMwSt) when noMwSt -> 0.0d;
case Businesskunde _ -> wert * 0.1d;
};
}
static String produceInvoiceText(
Kunde kunde, double wert, double mwst) {
return FMT. """
Hallo \{
switch (kunde) {
case Privatkunde(String name, _) -> name;
case Businesskunde(var name, _, _) -> name;
} },
Bitte senden Sie uns den Rechnungsbetrag in Höhe von \
%.2f\{ wert }€ plus %.2f\{ mwst }€ MwSt \
%2.f\{ wert + mwst }.
Mit freundlichen Grüßen
Merlin Bögershausen
""" ;
}
@Test
void invoiceValue100() {
var customer = new Privatkunde("test", "test@dummy.de");
var actualMwSt = MwStRechner.calculateMwSt(customer, 100d);
assertThat(actualMwSt).isEqualTo(10d);
}
6 Test to rule them all
@Path("/invoice")
public class InvoiceResource {
@Inject KundenRepository kundenRepo;
@Inject MailService mailService;
@GET
public void sendInvoice(long kundeID, double wert) {
var kunde = kundenRepo.findById(kundeID);
var mwst = MwStRechner.calculateMwSt(kunde, wert);
var text = InvoiceFormatter.
produceInvoiceText(kunde, wert, mwst);
mailService.sendMail(kunde, text);
}
}
For every te am there are different trade-offs:
Readability - learn and use
Duplication - not always bad
Testability - no brain needed
Performance - don’t guess, measure!
Micro steps and huge gain for small disruption.
svstudioart on Freepik
creativeart on Freepik
ImageFreepik
Image from Freepik