I'm refactoring a code base to have something more easier to read and follow SRP. However I'm at the point I'm unsure what the best design is.
Currently it looks like this: A thread receives a "command" to execute. The "command" is always the same, it takes the form of an encoded message to send to a WS. The message first need to be "decoded" to send the proper data to the WS. For that first step, I have a factory that will give me one of the implementation of the OutMessageParser to parse the message into one of the object to send.
public interface IOutMessageParser
{
OutMessage CreateOutMessage();
}
public abstract OutMessage
{
abstract string GetTcpMessage(); //Used to get the message to sent to a WS
}
So, when I receive the command in the thread, I'm parsing it that way - note that the factory will pass constructor arguments depending on the message, so some OutMessageParser receive different construtor argument and they have internal properties. Then for each OutMessageParser, the CreateOutMessage method will retrieve data from DB and create an OutMessage :
var outMessage = OutMessageParserFactory.GetOutMessageParser(messageReceived).CreateOutMessage();
Then a service will send the message to the client that will try to send the message. The client returns a status (SENT, ERROR, SOCKET_EXCPETION, EXCEPTION, etc, etc)
senderService.SendMessage(outMessage)
The SendMessage looks like this :
public class SenderService
{
//Constructor, internal properties, etc.
public void SendMessage(OutMessage outMessage)
{
var sendStatus = _wsClient.SendDataMessage(outMessage.GetTcpMessage());
switch(sendStatus)
{
//TODO Now what ????
}
}
}
Now, the tricky part is that, depending on the type of sendStatus (the enum) AND the concrete type of OutMessage, the DB needs to be updated, basically two main scenario exists - either the sent is successful or it's not. For any given scenario, the linked DB objects of the OutMessage needs to be updated in a certain way.
So my idea is to create an interface like this :
public interface IOutMessageCommand
{
// these methods will update DB objects
void ExecuteMessageSent();
void ExecuteMessageNotSent();
}
And for each OutMessage having a corresponding implementation, when creating the OutMessage, an ICommand is created as well. Then the service would look like this :
The SendMessage looks like this :
public class SenderService
{
//Constructor, internal properties, etc.
public void SendMessage(OutMessage outMessage)
{
var sendStatus = _wsClient.SendDataMessage(outMessage.GetTcpMessage());
switch(sendStatus)
{
case SendStatus.SENT :
outMessage.Command().ExecuteMessageSent()
break;
case SendStatus.NOT_SENT:
case SendStatus.EXCEPTION:
outMessage.Command().ExecuteMessageNotSent()
break;
}
}
}
However this seems off... Having a simple object holding its command seems weird to me, especially that when creating the command I'll need some info from the OutMessage instance properties (passed through the Command constructor) before calling executing its methods. Is that an anti-pattern ? Because the OutMessage have the responsability to create a IOutMessageCommand instance - but it should not be its responsibility. Is there another way to do ? I've thinking about a factory to create the Command, but I'd like to avoid having a big switch over the instanceOf(OutMessage) - that would look ugly and not very practical.
Thanks in advance.
EDIT :
For now the solution I'm using is like this :
public class SenderService
{
private readonly WsClient _wsClient;
internal SenderService(WsClient wsClient)
{
_wsClient = wsClient;
}
public void SendOutMessage(OutMessage outMessage)
{
outMessage.SendStatusType = _wsClient.SendDataMessage(outMessage.GetTcpMessage());
IOutMessageVisitor outMessageVisitor;
switch(outMessage.SendStatusType)
{
case SendStatusType.EXCEPTION:
case SendStatusType.PROTOCOL_ERROR:
case SendStatusType.NOT_SENT:
case SendStatusType.RECV_TIMEOUT:
outMessageVisitor= new NotSentVisitor();
break;
case SendStatusType.SENT_COMPLETED:
outMessageVisitor= new SentVisitor();
break;
default:
outMessageVisitor= new NotSentVisitor();
break;
}
outMessage.Accept(outMessageVisitor);
}
}
Along with the following modification, for each implementation of OutMessage I have an accept method:
internal override void Accept(IOutMessageVisitor visitor)
{
visitor.Visit(this);
}
And the IOutMessageVisitor looks like this :
internal interface IOutMessageVisitor
{
void Visit(v3456Message message);
void Visit(v23Message message);
void Visit(v1Message message);
// And so on for more than a dozen different message types
}
Finally both IOutMessageVisitor implementations performs, for each Visit methods, specific DB updates.
I'm not happy with this solution, but that's the best I came up with for now.