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
Invalid state is not representable
Nothing is Mutable
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) {
return b.isVorsteuerAbzugsberechtigt()
? 0.0d : 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;
};
}
@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;
@POST // parameter mapping omitted
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);
}
}
Readability 👓 - learn and use 📚
Duplication 🖨️ - not always bad 🧀
Testability ✅ - no brain needed 🧟
Performance 🚴- don’t guess, measure! ⏲️
svstudioart on Freepik
creativeart on Freepik
ImageFreepik
Image from Freepik
Tradeoffs Meme from Imgflip Meme Generator