16 · 32 · 6 min
Fixing the function from page one
Back to the 200 line function. Here is what the four tools do to it.
Before
class NotificationService {
void send(String type, String to, String body) {
if (type.equals("email")) { /* 20 lines of SMTP */ }
else if (type.equals("sms")) { /* 15 lines of gateway */ }
else if (type.equals("push")) { /* 25 lines of tokens */ }
}
}After
interface Channel {
void send(String to, String body);
}
class EmailChannel implements Channel { /* the SMTP code, alone */ }
class SmsChannel implements Channel { /* the gateway code, alone */ }
class NotificationService {
private final Map<String, Channel> channels;
NotificationService(Map<String, Channel> channels) {
this.channels = channels;
}
void send(String type, String to, String body) {
Channel c = channels.get(type);
if (c == null) throw new IllegalArgumentException("unknown channel: " + type);
c.send(to, body);
}
}What changed
Adding WhatsApp is now one new class and one entry in the map that builds the service. NotificationService is never edited again, which is why SMS stops breaking.
The SMTP code did not go away. It moved into a file where nothing else lives, and where changing it cannot affect SMS because they no longer share a function.
And it became testable
var sent = new ArrayList<String>();
Channel fake = (to, body) -> sent.add(to + ":" + body);
new NotificationService(Map.of("email", fake)).send("email", "[email protected]", "hi");
assertEquals(List.of("[email protected]:hi"), sent);The old version needed a live SMTP server to test at all.