Strategy Pattern With Spring Boot

Halil UĞUR
4 min readSep 6, 2022

--

Bla… Blaa… and Blaa. Let’s skip empty words and start the main subject.

Every developer wants useful code that works everywhere. Therefore we need some design patterns. In this article, we hand Strategy design.

Strategy Pattern

The strategy design pattern is useful for different tasks that we want to work on. For example: Think we have a notification message that we have a few platforms like website, SMS, email whatever. We need configuration for every platform. But when we want to add a new platform (as WhatsApp) configuration to our project, we will break the Open/Close principle. Because we will touch the controller, service, façade, and main abstract class, and then we will have broken every layer.

So, we don’t want to do that. if you say Why? Senior will punch you :). We did explain above.

Okay, firstly we need a spring boot project from the initializer website.

Project Structure

application.properties and StrategyPatternApplication.java have default main codes and properties. We didn’t add anything.

  • Let’s create an Enum class that name is NotificationType. This class just will have said to us which type of notification they have.
public enum NotificationType {
SITE,
SMS,
EMAIL
}
  • Create an interface that has sendMessage and notificationType methods. Because we have different platforms that we need to send.
public interface NotificationStrategy {
void sendMessage(String message);

NotificationType notificationType();
}
  • Now we can create a few classes in which we implement the NotificationStrategy class.

For Email Notification

@Slf4j
@Component
public class EmailNotificationStrategy implements NotificationStrategy {
@Override
public void sendMessage(String message) {
log.info("message send to email" + message);
}

@Override
public NotificationType notificationType() {
return NotificationType.EMAIL;
}
}

For website Notification

@Slf4j
@Component
public class SiteNotificationStrategy implements NotificationStrategy {
@Override
public void sendMessage(String message) {
log.info("message send to site" + message);
}

@Override
public NotificationType notificationType() {
return NotificationType.SITE;
}
}

For SMS Notification

@Slf4j
@Component
public class SmsNotificationStrategy implements NotificationStrategy {
@Override
public void sendMessage(String message) {
log.info("message send to phone" + message);
}

@Override
public NotificationType notificationType() {
return NotificationType.SMS;
}
}
  • We need a configuration to get all implemented classes with NotificationStrategy. If you sign every strategy that we implement with NotificationStrategy, you can get all strategies with the notificationStrategies list.
@Configuration
@AllArgsConstructor
public class StrategyConfig {
private final List<NotificationStrategy> notificationStrategies;

@Bean
public Map<NotificationType, NotificationStrategy> sendNotificationByType() {
Map<NotificationType, NotificationStrategy> messagesByType = new EnumMap<>(NotificationType.class);
notificationStrategies.forEach(notificationStrategy -> messagesByType.put(notificationStrategy.notificationType(), notificationStrategy));
return messagesByType;
}
}
  • Everything is ready and we can create a context class to apply the sendMessage method. This class provides independence from a controller class. Thus controllers never know information about the strategy. We need one more layer as a service but it is just for learning and we don’t care about that. :)
@AllArgsConstructor
@Component
@Slf4j
public class NotificationContext {
private final Map<NotificationType, NotificationStrategy> sendNotificationByType;

public void sendMessage(String message, NotificationType notificationType) throws NotFoundNotificationStrategy {
NotificationStrategy notificationStrategy = sendNotificationByType.getOrDefault(notificationType, null);
if (Objects.isNull(notificationStrategy)) {
throw new NotFoundNotificationStrategy("Notification Type not found. type: " + notificationType);
}
notificationStrategy.sendMessage(message);
}
}
  • We can test our project with Postman on the NotificationController.
@RestController
@RequestMapping("/")
@AllArgsConstructor
public class NotificationController {
private final NotificationContext notificationContext;

@GetMapping
public String sendNotification(@RequestParam String message,
@RequestParam NotificationType notificationType) throws NotFoundNotificationStrategy {
notificationContext.sendMessage(message, notificationType);
return message;
}
}
  • Test Results:
Notification Project Test Result
Notification Bad Request Test Result
  • You see, we don’t have WHATSAPP implementation but we can apply small changes. Let’s implement WhatsApp.

Add WhatsApp type on the Enum class.

Added WhatsApp Type on NotificationType Class

Create a new WhatsApp strategy and implement the NotificationStrategy interface. (I give name is WhatsAppNotificationStrategy)

WhatsApp Notification Strategy Class

You see we don’t touch anywhere. No class, service, controller, façade whatever. We just need to change log.info things. :) We need to apply real implementation there. (WhatsApp API)

If you want you can check all code on the GitHub repository. And don’t forget to follow me :)

--

--