You are right, 8 parameters -all of them strings- make the constructor a good candidate for a code refactoring.
Essential attributes first
Look at the message model and guess what attributes are essential to initialize a valid instance. Only subject and body? The source too? The targets?
Reduce the number of arguments to the essential. Add setters for the rest.
Encapsulation
Consider encapsulating related parameters. For example, a,b and c might go together within a new class.
Use creational patterns
Instead of initializing messages directly, do It from factories or builders.
Inheritance
With the time you will realise that messages are excellent candidates for inheritance. Segmenting messages by attribute generates a little overhead because you will find yourself often asking for the type if(message.getType() == ...)
.
Inheritance works perfectly alongside with the previous points (creational patterns, encapsulation) and with next one.
Abstractions
Use abstractions. Don't work directly with concrete classes. For example, if Message
is the superclass and CloudMessage
a subclass, don't use CloudMessage
in the interfaces. Do work as much as possible with Message
.
Brief example
The interface
public interface Message{
String getBody();
String getSubject();
}
The abstraction
public abstract class AbstractMessage implements Message{
private String body;
private String subject;
AbstractMessage(String subject, String body){...}
public String getBody(){...}
public String getSubject(){...}
}
Concrete message
public class CloudMessage extends AbstractMessage{
private String[] tokens;
CloudMessage(String subject, String body, String[] tokens){
super(subject, body);
this.tokens = Arrays.copyOf(tokens,tokens.length);
}
public String[] getTokens(){
return Arrays.copyOf(tokens,tokens.length);
}
}
The Factory
public MessageFactory{
private MessageFactory(){}
public static Message createCloudMessage(String subject, String body, String[] tokens){
// Validations...
return new CloudMessage(subject, body, tokens);
}
}
Note that I didn't mention the Reader. The message will arrive at you somehow and the reading can be delegated to the Reader. The Factory could get the reader as a parameter instead of those Strings I used. For example:
The Factory and MessageReader
public MessageFactory{
private MessageFactory(){}
public static Message createCloudMessage(MessageReader messageReader){
// Validations...
return new CloudMessage(
messageReader.getValueFor('subject'),
messageReader.getValueFor('body'),
messageReader.getValuesFor('tokens'));
}
}
Consider the Builder pattern instead of the Factory (or both) if you find your messages initialization to be too complex.
public static Message createCloudMessage(MessageReader messageReader){
// Validations...
return new CloudMessageBuilder()
.subject(messageReader.getValueFor('subject'))
.body(messageReader.getValueFor('body'))
.tokens(messageReader.getValuesFor('tokens'))
//...more fields whether they are present or not.
.build();
}
}
Worth to mention here, TolerantReaders
Finally, note also that each component contributes to the separation of concerns (SRP).