Most of us write it this way the first time. There is a base Notification. Each pair of "what we say" and "how we send it" gets its own subclass.
The setup
Swiggy style notifications: three message types and three channels.
| SMS | |||
|---|---|---|---|
| OTP | OtpSmsNotification | OtpEmailNotification | OtpWhatsAppNotification |
| Promo | PromoSmsNotification | PromoEmailNotification | PromoWhatsAppNotification |
| Order update | OrderUpdateSmsNotification | OrderUpdateEmailNotification | OrderUpdateWhatsAppNotification |
Nine classes. Each one looks like this:
class OtpSmsNotification extends Notification {
@Override
void send(String to) {
String text = "Your Swiggy OTP is 482913. Valid for 5 minutes.";
System.out.println("[SMS via gateway] to " + to + ": " + text);
}
}One class looks harmless. The trouble shows when you put all nine side by side.
What goes wrong
1. The count multiplies
Classes = message types x channels. Today that is 3 x 3 = 9.
- Add Push: 3 x 4 = 12.
- Add a "Rate your order" message too: 4 x 4 = 16.
Every new item on either axis adds a full new row or column of classes to the table above.
2. The same text lives in three places
// OtpSmsNotification
String text = "Your Swiggy OTP is 482913. Valid for 5 minutes.";
// OtpEmailNotification
String text = "Your Swiggy OTP is 482913. Valid for 5 minutes.";
// OtpWhatsAppNotification
String text = "Your Swiggy OTP is 482913. Valid for 5 minutes.";Security asks you to change the OTP expiry to 10 minutes. You update two copies and miss the third. Now WhatsApp users see the wrong expiry.
3. The same sending code lives in three places
Every *Sms* class has its own copy of the SMS call. In a real system that copy holds the gateway URL, the API key, the DLT template id (an Indian SMS rule: each message text must be registered) and the retry logic. Switch SMS providers and you edit three files. With 5 message types it is five files.
4. The channel is baked into the type
new OtpSmsNotification().send("+91 98450 12345");Say the SMS provider goes down at 8 pm on a Friday (peak dinner time) and you want OTPs to fall back to WhatsApp. You cannot pass a different channel. You must build a different class, so every place that sends an OTP needs an if.
5. Two reasons to change, one class
OtpSmsNotification changes when the OTP copy changes and also when the SMS integration changes. Two teams (growth and platform) end up editing the same file.
What the run prints
[SMS via gateway] to +91 98450 12345: Your Swiggy OTP is 482913. Valid for 5 minutes.
[Email via SMTP] to priya@example.com: Flat 50% off up to Rs 100 on your next order. Code: TRYNEW
[WhatsApp via Business API] to +91 98450 12345: Order #SW1043 from Meghana Foods is out for delivery.
[WhatsApp via Business API] to +91 98450 12345: Your Swiggy OTP is 482913. Valid for 5 minutes.
The output is fine. The code behind it is what hurts.
The smell in one line
If your class names look like <Thing><Way> and make a grid, two dimensions are squeezed into one hierarchy. That is the signal for Bridge.
Run this version
From the repository root, create the output directory, compile this example, and run its fully qualified class name:
mkdir -p out
javac -d out src/DesignPatterns/StructuralPatterns/BridgeDesignPattern/Before/Main.java
java -cp out DesignPatterns.StructuralPatterns.BridgeDesignPattern.Before.MainAll classes sit in one Main.java so the example is easy to run. The problem is the same as in a multi-file application. Each combination is a separate class, and each class mixes one message choice with one channel choice.