I have a class:
#Getter
#Setter
public class Message {
private String sender;
private Set<String> receivers;
private String text;
}
The Message class can be extended by EmailMessage and SMSMessage having their own additional fields.
There are 2 services - EmailService and SMSService.
They both do the following operations:
Take the message object which contains sender and receiver user ids.
EmailService calls another service to transform the user ids to email ids. SMSService does the same to transform the user ids to phone numbers.
The Message object should be transformed to EmailMessage and SMSMEssage in their respective services.
Send the EmailMessage and SMSMessage.
I've been going through many design patterns to solve this problem. But I didn't find any such pattern to transform the fields of an object and/or convert the parent object to a child object.
The only design patterns that come close are Strategy and Decorator.
I'm using Strategy Pattern for using either EmailService or SMSService at runtime. It is solving just one part of my problem. Will Decorator Pattern help for solving the other?
Edit: Adding the current implementation
public abstract class AbstractMessageService<T extends Message> {
#KafkaHandler(isDefault = true)
public final void consume(Message message) {
T convertedMessage = getMessageConverterStrategy().convert(message);
send(convertedNotificationMessage);
}
protected abstract void send(T message) throws Exception;
protected abstract MessageConverterStrategy getMessageConverterStrategy();
}
#KafkaListener(
topics = "topicName",
groupId = "email-group")
public class EmailService extends AbstractMessageService<EmailMessage> {
#Override
protected void send(EmailMessage message) {
// Some logic to send email.
}
#Override
protected MessageConverterStrategy getMessageConverterStrategy() {
return new EmailMessageConverterStrategy();
}
}
#KafkaListener(
topics = "topicName",
groupId = "sms-group")
public class SMSService extends AbstractMessageService<SMSMessage> {
#Override
protected void send(SMSMessage message) {
// Some logic to send sms.
}
#Override
protected MessageConverterStrategy getMessageConverterStrategy() {
return new SMSMessageConverterStrategy();
}
}
public interface MessageConverterStrategy<T extends Message> {
T convert(Message message);
}
In the SMSMessageConverterStrategy and EmailMessageConverterStrategy, I want to use other design pattern to do the conversion of Message to SMSMessage and EmailMessage respectively.
Because you have to convert almost everything, I think Message and Email and SMS are not related. I think you should use Factory method like this:
public class Test {
public static void main(String[] args) throws Exception {
Message m = new Message();
Email email = Email.from(m);
EmailService.sendMail(email);
}
}
class Message {
private String sender;
private Set<String> receivers;
private String text;
}
class Email {
private String senderMailId;
private String text;
private Set<String> receiverMailIds;
//make it private
private Email() {
}
public static Email from(Message m) {
Email email = new Email();
email.senderMailId = ""; //call MappingService.toEmailId(m.getSender());
//do rest of the conversion and processing
return email;
}
}
class EmailService {
public static void sendMail(Email e) {
//send the mail
}
}
I would suggest the use of Factory method pattern.
A MessageService which would be implemented by SmsService and EmailService.
interface MessageService {
Message transform(Message message);
}
class SmsService implements MessageService {
#Override
SmsMessage transform(Message message) {
// do appropriate transformation and return.
}
}
class EmailService implements MessageService {
#Override
EmailMessage transform(Message message) {
// do appropriate transformation and return.
}
}
Then use a factory to create the appropriate service. (Assuming an enum MessageType):
public class MessageServiceFactory {
public MessageService getService(Message m, MessageType type){
switch(type) {
case SMS:
return new SmsService(m);
case EMAIL:
return new EmailService(m);
default:
// throw some exception.
}
}
}
Then to do the actual transformation:
Message convertMessage(Message m, MessageType type) {
return new MessageServiceFactory().getService(m, type)
.transform();
}
Related
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:
Should we make Message abstract - EmailMessage,SMSMessage etc ?
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?
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.
I have a messaging producer (RabbitMQ) and depending on what kind of message i have to send, i need to change the routing key and exchange at runtime.
Given this i'd implemented a strategy to load each class with specific properties, but it's not appear a good solution.
For example:
public class MyProducerStrategy1 extends RabbitMessagingProducer {
private static final String ROUTING_KEY = "order1";
private static final String EXCHANGE = "myexchange1";
#Override
String getRoutingKey() {
return ROUTING_KEY;
}
#Override
String getExchange() {
return EXCHANGE;
}
#Override
public void sendMessage(Message message) {
super.sendMessage(message);
}
}
public class MyProducerStrategy2 extends RabbitMessagingProducer {
private static final String ROUTING_KEY = "fullfilment";
private static final String EXCHANGE = "myexchange2";
#Override
String getRoutingKey() {
return ROUTING_KEY;
}
#Override
String getExchange() {
return EXCHANGE;
}
#Override
public void sendMessage(Message message) {
super.sendMessage(message);
}
}
public abstract class RabbitMessagingProducer implements MessagingProducerStrategy {
#Autowired
private RabbitTemplate rabbitTemplate;
abstract String getRoutingKey();
abstract String getExchange();
#Override
public void sendMessage(Message message) {
rabbitTemplate.convertAndSend(getExchange(), getRoutingKey(), message);
}
}
Does it make sense? or there's another approach to load there properties and have maybe one class?
Do not create multiple class for this scenario.
load the files from a property file based on the message.
Another option is to create a static map of values with message types as the key and routing key as the value. Fetch the values from the map based on the message type.
I have enum like this:
public enum Email{
WELCOME,
LOGIN
}
EmailService has following method signature:
public void sendEmail(String from, String subject, String[] to, Map<String, String> props, String templateFileName)
I want to have API like this:
Email.WELCOME.send(from, subject, to, welcomeProp1, welcomeProp2....)
Email.LOGIN.send(from, subject, to, loginProp1, loginProp2....)
To acheve this I tried to add specific methods to enum fields:
public enum Email{
WELCOME{
public void send(String param1,String param2,String param3,String param4){
....
}
},
LOGIN{
public void send(String anotherParam1,String anotherParam2,String anotherParam3){
....
}
}
}
But I found out that I could not invoke this methods outside of enum. To acheve it I need to create abstract method and override in each enum value. But problem that signature of these methods are different and it is impossible to do it.
Any ideas?
You can use polymorphism for in such case. First of all, define some interface, for instance:
public interface Message {
}
Then you should add an abstract method to the enum:
public abstract void send(Message message);
After that, create two child classes - the first one is for WELCOME and the second one is for LOGIN:
class WelcomeMessage implements Message {
private String param1;
private String param2;
private String param3;
private String param4;
// constructor, getters
}
class LoginMessage implements Message {
private String anotherParam1;
private String anotherParam2;
private String anotherParam3;
// constructor, getters
}
Then add implementations of send() method:
public enum Email {
WELCOME {
public void send(Message message) {
WelcomeMessage wm = (WelcomeMessage) message;
....
}
},
LOGIN {
public void send(Message message) {
LoginMessage lm = (LoginMessage) message;
....
}
}
}
UPD.
#gstackoverflow mentioned that it's possible to invoke methods with "incorrect" implementation. I guess I know how to prevent this.
public enum Email {
WELCOME {
protected void sendInternal(Message message) {
WelcomeMessage wm = (WelcomeMessage) message;
....
}
protected Class<? extends Message> getSupportedClass() {
return WelcomeMessage.class;
}
},
LOGIN {
protected void sendInternal(Message message) {
LoginMessage lm = (LoginMessage) message;
....
}
protected Class<? extends Message> getSupportedClass() {
return LoginMessage;
}
};
public static void send(Message message) {
for (Email email : values()) {
if (email.getSupportedClass().equals(message.getClass()) {
email.sendInternal(message);
}
}
}
protected abstract void sendInternal(Message message);
protected abstract Class<? extends Message> getSupportedClass();
}
One solution is to make all the methods take exactly the same object, they just use different parameters from it.
class EmailDetails {
String param1;
String param2;
String param3;
String param4;
String anotherParam1;
String anotherParam2;
String anotherParam3;
}
public enum Email {
WELCOME {
public void send(EmailDetails details) {
System.out.println("Welcome!!");
}
},
LOGIN {
public void send(EmailDetails details) {
System.out.println("Log in!!");
}
};
public abstract void send(EmailDetails details);
}
public void test(String[] args) {
Email.WELCOME.send(new EmailDetails());
Email.LOGIN.send(new EmailDetails());
}
You will generally find that many of the fields are common to all uses.
I have the following class structure:
a base class 'Message' which contains some common members (/fields),
'Imessage' interface that has some methods that all messages should implement,
a lot of different message classes that extends (inherits) the base 'Message' class and have a lot of fields, an enum for each message type and a factory class which given an enum creates an instance of the proper message class.
The issue is that I'm not sure where/how to implement the setting of values for each of the message classes.
It can't be in it's constructor because in the factory method i need to build generic instances.
Should i implement a 'Create' method for each of the messages that will set all of it's members?
public static Message buildMessage(MessageType messageType)
{
Message message = null;
switch (messageType) //TODO: add all messages!
{
case CONNECT:
message = new ConnectMessage();
break;
case CONNECT_RESPONSE:
message = new ConnectResponseMessage();
break;
case DISCONNECT:
message = new DisconnectMessage();
break;
case FLOWSTART:
message = new FlowStartMessage();
break;
default: return null;
}
return message;
}
The factory pattern should return a valid, fully populated object so adding a Create method for each type of Message object would not be ideal (unless it is called from within buildMessage).
I don't know what you mean when you say:
It can't be in it's constructor because in the factory method i need
to build generic instances.
It's completely valid to initialise an objects like this:
Message message = new ComplexMessage(type, value, something, somethingElse);
Message message = new LessComplexMessage(type, value);
In which case your buildMessage method can take in all objects required to build all the sub types of your message.
If this becomes too complex because there are too many variations of required fields then it might be worth upgrading to the Builder Pattern:
http://javarevisited.blogspot.co.uk/2012/06/builder-design-pattern-in-java-example.html
This is one possible solution. You can write a constructor in Message and override it in the subclasess.
public class Message {
private final String commonField;
public Message(String commonField){
this.commonField = commonField;
}
}
And in the subclassess
public ConnectMessage(String commonField){
super(commonField);
//initialize
}
And in the factory do
public static Message buildMessage(MessageType messageType, String commonValue)
{
Message message = null;
switch (messageType) //TODO: add all messages!
{
case CONNECT:
message = new ConnectMessage(commonValue);
break;
case CONNECT_RESPONSE:
message = new ConnectResponseMessage(commonValue);
break;
case DISCONNECT:
message = new DisconnectMessage(commonValue);
break;
case FLOWSTART:
message = new FlowStartMessage(commonValue);
break;
default: return null;
}
return message;
}
If message definitions are rigid and you are not creating combinations of messages or configuring their properties, then why have a factory class ?
On another note, The enum alone is capable of much:
public class Main {
public static interface IMessage{
public String getName();
}
public static class GoodMessage implements IMessage{
#Override
public String getName() {
return "Good";
}
}
public static class BadMessage implements IMessage{
#Override
public String getName() {
return "Bad";
}
}
public static interface IMsgGen{
public IMessage create();
}
public static enum Messages implements IMsgGen {
GOOD_MSG(GoodMessage.class),
BAD_MSG(BadMessage.class),
CUSTOM_MSG(null){
#Override
public IMessage create() {
return new IMessage() {
#Override
public String getName() {
return "Custom";
}
};
}
};
private final Class<? extends IMessage> mClass;
private Messages(Class<? extends IMessage> aClass) {
mClass = aClass;
}
#Override
public IMessage create() {
try {
return mClass.newInstance();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(),e);
}
}
}
public static void main(String[] args){
IMessage msg = Messages.GOOD_MSG.create();
System.out.println(msg.getName());
msg = Messages.BAD_MSG.create();
System.out.println(msg.getName());
msg = Messages.CUSTOM_MSG.create();
System.out.println(msg.getName());
}
}
I have a Java client which wants to communicate with a device through messages over serial communication. The client should be able to use a clean API, abstracting the ugly details of the serial communication. The client can send many types of messages through that API and gets responses. I'm searching for advice which way is best to implement this API.
For simplicity, say we have only two message types: HelloMessage which triggers a HelloResponse and InitMessage which triggers an InitResponse (in reality, there are many more)
Designing the API (that is, the Java abstraction of the device) I could have:
One method per message type:
public class DeviceAPI {
public HelloResponse sendHello(HelloMessage){...}
public InitResponse sendInit(InitMessage){...}
... and many more message types ....
This is nicely type safe. (It could also be many times the same send() method, overloaded, but that's about the same). But it is very explicit, and not very flexible - we cannot add messages without modification of the API.
I could also have a single send method, which takes all message types:
class HelloMessage implements Message
class HelloResponse implements Response
...
public class DeviceAPI {
public Response send(Message msg){
if(msg instanceof HelloMessage){
// do the sending, get the response
return theHelloResponse
} else if(msg instanceof ...
This simplifies the API (only one method) and allows for additional Message types to be added later without changing the API. At the same time, it requires the Client to check the Response type and cast it to the right type.
Client code:
DeviceAPI api = new DeviceAPI();
HelloMessage msg = new HelloMessage();
Response rsp = api.send(msg);
if(rsp instanceOf HelloResponse){
HelloResponse hrsp = (HelloResponse)rsp;
... do stuff ...
This is ugly in my opinion.
What do you recommend? Are there other approaches which give cleaner results?
References welcome! How did others solve this?
Here is a way to do it in type-safe (and extensible) way using generics:
public interface MessageType {
public static final class HELLO implements MessageType {};
}
public interface Message<T extends MessageType> {
Class<T> getTypeClass();
}
public interface Response<T extends MessageType> {
}
public class HelloMessage implements Message<MessageType.HELLO> {
private final String name;
public HelloMessage(final String name) {
this.name = name;
}
#Override
public Class<MessageType.HELLO> getTypeClass() {
return MessageType.HELLO.class;
}
public String getName() {
return name;
}
}
public class HelloResponse implements Response<MessageType.HELLO> {
private final String name;
public HelloResponse(final String name) {
this.name = name;
}
public String getGreeting() {
return "hello " + name;
}
}
public interface MessageHandler<T extends MessageType, M extends Message<T>, R extends Response<T>> {
R handle(M message);
}
public class HelloMessageHandler
implements MessageHandler<MessageType.HELLO, HelloMessage, HelloResponse> {
#Override
public HelloResponse handle(final HelloMessage message) {
return new HelloResponse(message.getName());
}
}
import java.util.HashMap;
import java.util.Map;
public class Device {
#SuppressWarnings("rawtypes")
private final Map<Class<? extends MessageType>, MessageHandler> handlers =
new HashMap<Class<? extends MessageType>, MessageHandler>();
public <T extends MessageType, M extends Message<T>, R extends Response<T>>
void registerHandler(
final Class<T> messageTypeCls, final MessageHandler<T, M, R> handler) {
handlers.put(messageTypeCls, handler);
}
#SuppressWarnings("unchecked")
private <T extends MessageType, M extends Message<T>, R extends Response<T>>
MessageHandler<T, M, R> getHandler(final Class<T> messageTypeCls) {
return handlers.get(messageTypeCls);
}
public <T extends MessageType, M extends Message<T>, R extends Response<T>>
R send(final M message) {
MessageHandler<T, M, R> handler = getHandler(message.getTypeClass());
R resposnse = handler.handle(message);
return resposnse;
}
}
public class Main {
public static void main(final String[] args) {
Device device = new Device();
HelloMessageHandler helloMessageHandler = new HelloMessageHandler();
device.registerHandler(MessageType.HELLO.class, helloMessageHandler);
HelloMessage helloMessage = new HelloMessage("abhinav");
HelloResponse helloResponse = device.send(helloMessage);
System.out.println(helloResponse.getGreeting());
}
}
To add support for a new message type, implement MessageType interface to create a new message type, implement Message, Response and MessageHandler interfaces for the new MessageType class and register the handler for the new message type by calling Device.registerHandler.
I've got a fully working example now of what you want:
To define the types of messages:
public interface MessageType {
public static class INIT implements MessageType { }
public static class HELLO implements MessageType { }
}
Base Message and Response classes:
public class Message<T extends MessageType> {
}
public class Response<T extends MessageType> {
}
Create custom init messages and responses:
public class InitMessage extends Message<MessageType.INIT> {
public InitMessage() {
super();
}
public String getInit() {
return "init";
}
}
public class InitResponse extends Response<MessageType.INIT> {
public InitResponse() {
super();
}
public String getInit() {
return "init";
}
}
Create custom hello messages and responses:
public class HelloMessage extends Message<MessageType.HELLO> {
public HelloMessage() {
super();
}
public String getHello() {
return "hello";
}
}
public class HelloResponse extends Response<MessageType.HELLO> {
public HelloResponse() {
super();
}
public String getHello() {
return "hello";
}
}
The DeviceAPI:
public class DeviceAPI {
public <T extends MessageType, R extends Response<T>, M extends Message<T>> R send(M message) {
if (message instanceof InitMessage) {
InitMessage initMessage = (InitMessage)message;
System.out.println("api: " + initMessage.getInit());
return (R)(new InitResponse());
}
else if (message instanceof HelloMessage) {
HelloMessage helloMessage = (HelloMessage)message;
System.out.println("api: " + helloMessage.getHello());
return (R)(new HelloResponse());
}
else {
throw new IllegalArgumentException();
}
}
}
Note that it does require an instanceof-tree, but you need that to handle what kind of message it is.
And a working example:
public static void main(String[] args) {
DeviceAPI api = new DeviceAPI();
InitMessage initMsg = new InitMessage();
InitResponse initResponse = api.send(initMsg);
System.out.println("client: " + initResponse.getInit());
HelloMessage helloMsg = new HelloMessage();
HelloResponse helloResponse = api.send(helloMsg);
System.out.println("client: " + helloResponse.getHello());
}
Output:
api: init
client: init
api: hello
client: hello
UPDATE: Added example on how to get input from the messages the client wants to send.
You could have a system of message handlers, and your DeviceAPI could choose which handler is suitable for the incoming message; and delegate it to the appropriate message handler:
class DeviceAPI {
private List<Handler> msgHandlers = new ArrayList<Handler>();
public DeviceAPI(){
msgHandlers.add(new HelloHandler());
//Your other message handlers can be added
}
public Response send(Message msg) throws Exception{
for (Handler handler : msgHandlers) {
if (handler.canHandle(msg)){
return handler.handle(msg);
}
}
throw new Exception("No message handler defined for " + msg);
}
}
The HelloHandler would look like:
interface Handler<T extends Message, U extends Response> {
boolean canHandle(Message message);
U handle(T message);
}
class HelloHandler implements Handler<HelloMessage, HelloResponse> {
#Override
public boolean canHandle(Message message) {
return message instanceof HelloMessage;
}
#Override
public HelloResponse handle(HelloMessage message) {
//Process your message
return null;
}
}
Ditto for your other messages. I'm sure you could make it more elegant, but the idea still remains the same - donot have one monster method with ifs; instead use polymorphism.
I don't think this is ugly at all:
if(rsp instanceOf HelloResponse){
HelloResponse hrsp = (HelloResponse)rsp;
...
else if ...
as long as you don't have like 100 different responses. You can encapsulate many kind of responses in one, depending on the data they have. For example:
class GenericResponse{
private String message;
private int responseType;
...
}
I have developed some multiplayer games and this is a good way to do it.
In case you have too many different types of messages, you just can use generic java types, like the example of skiwi above.
hope it helps