简体   繁体   中英

Multiple channel Notification system - LLD -

You have to design the classes for building a notification system that supports multiple channels such as email, SMS, Whatsapp. It should be easily extensible.

My design :

class Message {
NotificationType type ; //email, sms
msgId;
String content ;
}
MessagingServiceImpl {
static {
//map from notification type to the respective handler
map.put("SMS",new SMSHandler());
map.put("Email",new EmailHandler();
}
void processMessage(Message message) {
Handler handler = map.get(message.getNotificationType();
handler.handleMessage();
}
}
public abstract class Handler {
public abstract void handle(Mesage message) ;
}
public EmailHandler extends Handler {
public void handle(Message message) {
System.out.println("Sending email"): // similar class for phone.
}

Note: This design was rejected in the interview. Questions:

  1. Should we make Message abstract - EmailMessage,SMSMessage etc ?
  2. Note that the content of the message varies depending on the channel. For eg. In email,the content will be large. For SMS,it is much smaller. So,should Message be astract?
  3. How can you support a new channel eg..telegram messages with minimal changes?

Of course I'm not in the mind of your reviewer, but he could argue about you breaking the "O" of the "SOLID" principles, which is the Open-Closed principle and states that: "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".

The problem with your solution is that if you need to support a new channel you need to add another instance to the NotificationType Enum.

Let's start from the interfaces:

public interface Message {
     //no need to define any methods here

}

public interface MessageHandler {
     void handleMessage(Message message);
     Class<?> getSupportedMessageType();
}

public interface MessageService {
     void processMessage(Message message);
}

It is already possible to define a definitive version of the MessageServiceImpl class which will not need to change each time we need to support a new channel.

public class MessageServiceImpl implements MessageService
{

    private final Map<Class<?>, MessageHandler> handlers = new HashMap<>();

    public MessageServiceImpl(ICollection<MessageHandler> handlers) {
       foreach(MessageHandler handler : handlers)  {
           this.handlers.put(handler.getSupportedMessageType(), handler);
       }
    }

    public void processMessage(Message message)  {
        if (!this.handlers.containsKey(message.getType())) throw new InvalidArgumentException();

        this.handlers.get(message.getType()).handleMessage(message);

    }

With the help of a dependency injection framework it might be also possible to let DI handle new message handlers implementations.

Then we can define an abstract class to abstract the message handler:

public abstract class AbsMessageHandler<T implements Message> implements MessageHandler {
    private final Class<T> supportedMessageType;
    protected AbsMessageHandler(Class<T> supportedMessageType) {
        this.supportedMessageType = supportedMessageType;
    }
    protected abstract void handleMessageInternal(T message);
    public Class<?> getSupportedMessageType() { return supportedMessageType; }

    public void handleMessage(Message message)
    {
        if (message == null) throw new InvalidArgumentException();
        if (message.getType() != getSupportedMessageType()) throw new InvalidArgumentException();
        handleMessageInternal((T) message);
    }
} 

The only thing left to define are the messages and message handlers implementations:

public EmailMessage implements Message  {
    private string from; 
    private string to;
    private string cc;
    private string subject;
    private string messageBody;
    //getters and setters
}

public EmailMessageHandler extends AbsMessageHandler<EmailMessage> {
    public EmailMessage() {
       super(EmailMessage.class);
    }

    protected void handleMessageInternal(EmailMessage message) {
       // do what you like
    }
}

Each time you need to add support for a new channel you only need to add a new couple of classes, one to implement the Message interface and one to extend the AbsMessageHandler abstract class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM