paint-brush
Open Closed Principle With Service Locator Patternby@esca
483 reads
483 reads

Open Closed Principle With Service Locator Pattern

by EscaMay 20th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Open closed principle is the most simple one in the list of software design principles I understand. "Open for extension, close for modification" - the idea seems quite straightforward. When a new message type comes in, you have to change this class again to support the new type. This approach violates the open closed principle. To deal with this, we use the ServiceLocatorFactoryFactoryBean provided by Spring framework to support new validation without changing the existing code. By this approach, your code will look much cleaner and become easier to maintain.
featured image - Open Closed Principle With Service Locator Pattern
Esca HackerNoon profile picture

Open closed principle is the most simple one in the list of software design principles I understand. "Open for extension, close for modification" - the idea seems quite straightforward. Let's create an example about validation.

We have a service which provides the feature to send message to recipients. The message's type could be SMS, Email, and Telegram, etc. Here is the structure of these message types.

Obviously, each message type has its own validation logic. SMSMessage must have valid recipient's phone number, EmailMessage must have valid receipient's email address, for example. We need a class to handle the validation and it might look like this.

@Autowired
    @Qualifier("Email")
    private MessageValidator emailValidator;

    @Autowired
    @Qualifier("SMS")
    private MessageValidator smsValidator;

    @Autowired
    @Qualifier("Telegram")
    private MessageValidator telegramValidator;

    @Override
    public void processMessage(Message msg) {
        boolean isValid = false;
        if (msg instanceof SMSMessage) {
            isValid = smsValidator.validate(msg);
        } else if (msg instanceof EmailMessage) {
            isValid = emailValidator.validate(msg);
        } else if (msg instanceof TelegramMessage) {
            isValid = telegramValidator.validate(msg);
        }

        //do other processing
    }

When a new message type comes in, you have to change this class again to support the new type. This approach violate the open closed principle.

To deal with this, we use the ServiceLocatorFactoryBean provided by Spring framework to support the new validation without changing the existing code, we only need to create more classes to handle the validation logic of the new message type.

As you can see the code fragment below has been incredibly shortened.

@Autowired
    private MessageValidatorFactory msgValidatorFactory;

    @Override
    public void processMessage(Message msg) {
        boolean isValid = msgValidatorFactory.getMsgValidator(msg.getType()).validate(msg);

        //do other processing
    }
public interface MessageValidatorFactory {
    MessageValidator getMsgValidator(String msgType);
}
@Configuration
public class BeanConfigs {
    @Bean("validatorFactory")
    public FactoryBean serviceLocatorFactoryBean() {
        ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
        factoryBean.setServiceLocatorInterface(MessageValidatorFactory.class);
        return factoryBean;
    }
}

The configuration merely help to lookup the bean by beanId such as

Email
,
SMS
,
Telegram

@Component("Email")
public class EmailMessageValidator implements MessageValidator {
    @Override
    public boolean validate(Message m) {
        //logic for validation
        return false;
    }
}
@Component("SMS")
public class SMSMessageValidator implements MessageValidator {
    @Override
    public boolean validate(Message m) {
        //logic for validation
        return false;
    }
}
@Component("Telegram")
public class TelegramMessageValidator implements MessageValidator {
    @Override
    public boolean validate(Message m) {
        //logic for validation
        return false;
    }
}

By this approach, your code will look much cleaner and become easier to maintain, mitigate the impact to the old features when implementing new feature.

Cheers