The refactored code uses the Template Method Design Pattern. This behavioral pattern puts the outline of an algorithm in a superclass. Subclasses can replace specific steps, but they cannot change the order of the steps.
1. How This Approach Solves the Issues
The separate, duplicated workflows are replaced by one abstract base class (a class that cannot be created on its own) that holds the template. The order of steps is now enforced in one place, and the shared code is reused.
| Issue | Solution with Template Method Pattern |
|---|---|
| Code Duplication | Shared steps (rate limit checks, recipient validation, pre-send auditing) are in the base class, so the copies are gone. |
| Hardcoded Behavior | Channel-specific work (an HTML email body vs. a plain [SMS] text) is pushed down to the subclasses. |
| Lack of Extensibility | To add a new channel (e.g., PushNotification or SlackNotification), subclass the template and write its abstract methods. |
| Maintenance Overhead | The shared logic is in a single file (the abstract superclass). A new compliance rule or a change to rate limiting needs exactly one edit. |
2. Key Architectural Steps of the Template Method Pattern
The refactored design has four kinds of methods. Together they fix the workflow and still let subclasses vary:
Template Method (final Method in Base Class)
The send() method runs the whole job. It lists the steps in a fixed order. Because it is marked final, subclasses cannot change the checks, reorder the steps or skip one.
Primitive Operations (abstract Methods)
The methods composeMessage() and sendMessage() are abstract. They are the steps that differ. A subclass such as EmailNotification or SMSNotification must write both, or the code will not compile.
Concrete Operations (private / concrete Methods)
Methods like rateLimitCheck(), validateRecipient(), and preSendAuditLog() are fully written in the base class. They are shared steps that every channel must run, and subclasses cannot change them.
Hooks (Optional Methods with Default Behavior)
The postSendAnalytics() method is a Hook. The base class gives it a default body. A subclass can keep that default (like EmailNotification) or override it with its own version (like SMSNotification).
3. Structural Breakdown
| Component | Responsibility |
|---|---|
Abstract Class (NotificationSender) | Declares the final template method to lock the step order and writes the shared logic. |
Concrete Implementations (EmailNotification / SMSNotification) | Write the abstract methods that compose the message and send it for their own channel. |
4. Summary of the "Inverted Control" Architectural Model
The Template Method Pattern follows the Hollywood Principle: "Don't call us, we'll call you." The subclass does not drive the flow and call up into the parent. The parent drives. It runs the steps in order and calls down into the subclass only when it needs a channel-specific job, such as composing the message. This way a subclass can plug into the framework without breaking its flow.