Violation in the Bad Code
NotificationService is the high-level class. It holds the decision "tell the user something". EmailService and SMSService are low-level details. They know how one delivery method works.
In the BadCode version, NotificationService creates both concrete services itself, with new. That gives the business class four jobs it should not have:
- It decides which channels exist.
- It knows how to build each channel.
- It offers one method per channel, such as
notifyByEmailandnotifyBySMS. - It calls the concrete sending code.
The dependency points from the business rule down to the delivery detail. So when a detail changes, the business rule may have to change too.
Adding WhatsApp shows the problem. You add a field, create a WhatsAppService, and add another method to NotificationService. That also breaks OCP (the Open-Closed Principle), because every new channel means editing the same class.
Problems with This Design
- Tight coupling:
NotificationServicecannot be used without the exact senders it picked. - Difficult tests: a test cannot swap email or SMS for a harmless fake, because the service builds them inside its constructor.
- Hard vendor changes: a new provider can force edits in the business class.
- Hidden setup: constructors, credentials, and network calls are buried inside the service.
- Growing API: each channel adds another
notifyBy...method. - Mixed language: the business code talks in vendor or transport terms instead of just saying "send this notification."
The new keyword is not the problem by itself. Creating simple value objects (like a date or an amount) inside business logic is fine. The smell is business code building volatile infrastructure, such as payment gateways, databases, mailers, or system clocks, which tests need to replace.
The GoodCode version adds a NotificationChannel interface, written in business terms, and passes it in through the constructor. The business class then depends on a promise ("something can send a message"). The concrete senders supply the details.
See GoodCode for the inverted direction and DIP-Notes.md for the complete guide.