Strategy pattern allows to change the internal behavior of a class based on input. Sounds, familiar, right?
The State pattern helps us
with pretty much the same problem, it changes internal state of a class based on circumstances. There are differences though.
State has different
behaviors that are somewhat aware of each other, and the pattern allows for flexibility, such that in the
example shown on the State
pattern page, the internal status of a work item could have been switched back and forth many times. Strategy is about
something else. Strategy is more of a fire-and-forget, "guns or missiles" type of thing.
The example below illustrates Strategy pattern as it is applied to a selected type of notification. Someone
is notified via their preferred method. That method (strategy) is selected once and used once. None of the methods are aware
of each other and you cannot switch them on the fly. Well, you can, but the context class needs to figure out that the main
method failed and backup needs to be called, but then again, that would be another "fire-and-forget" strategy that is not
aware of alternatives.
Now, to code. This first interface and set of classes imitate notification services
interface Strategy {
notify(): string;
}
class SMS implements Strategy {
notify(): string {
return "Notified via Short Message Service (SMS) using Twilio.";
}
}
class PhoneCall implements Strategy {
notify(): string {
return "Notified via a phone call using some robot service.";
}
}
class Email implements Strategy {
notify(): string {
return "Notified via an e-mail via an e-mail relay.";
}
}
The next class, Context, acts as a container implementing a notification strategy:
class Context {
strategy: Strategy;
setStrategy(strategy: Strategy) {
this.strategy = strategy;
}
executeStrategy() {
return this.strategy.notify();
}
}
Finally, the service that sets the strategy at the time of creation depending on the choice and is able to perform a notification.
class NotificationService {
private context: Context;
constructor(preferredNotificationMethod: string) {
this.context = new Context();
switch (preferredNotificationMethod.toLowerCase()) {
case "sms":
this.context.setStrategy(new SMS());
break;
case "call":
this.context.setStrategy(new PhoneCall());
break;
default:
this.context.setStrategy(new Email());
break;
}
}
notify(): string {
return this.context.executeStrategy();
}
}
Here is some test code:
let text = new NotificationService("sms");
console.log("Notification via SMS: ", text.notify());
let call = new NotificationService("call");
console.log("Notification via a call: ", call.notify());
let email = new NotificationService("email");
console.log("Notification via email: ", email.notify());
Results:
Notification via SMS: Notified via Short Message Service (SMS) using Twilio.
Notification via a call: Notified via a phone call using some robot service.
Notification via email: Notified via an e-mail via an e-mail relay.