I'm currently new to the topic of Clean Code and SOLID principles.
We have written a UseCase for a specific task.
using BusinessLogic.Classes.BusinessLogic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BusinessLogic.UseCases
{
public class DoASpecificTask<T> : Interfaces.IUseCase
where T : Interfaces.Connections.IQuery
{
T _Query;
Interfaces.Connections.IConnection<T> _Connection;
object _Parameters;
string _ServiceName;
Interfaces.ISendMessage _MessageSender;
public DoASpecificTask(T query, Interfaces.Connections.IConnection<T> connection, object parameters, string serviceName, Interfaces.ISendMessage messageSender)
{
_Query = query;
_Connection = connection;
_Parameters = parameters;
_ServiceName = serviceName;
_MessageSender = messageSender;
}
public void Execute()
{
Interfaces.Connections.IQueryResultList resultList = ExecuteReadDataOnConnection<T>.GetList(_Query, _Connection, _Parameters);
if (!ValidateQueryResultListItems.Validate(resultList))
{
EndImp3Service.EndService(_ServiceName);
_MessageSender.SendMessage('Message','for@bar.com');
}
}
}
}
As far as I understood SOLID, a solution / class / method should be open for expansion, closed for changes (Open/Closed Principle). Our issue now is, that we have created an unspecific, general interface for sending out a message
Interfaces.ISendMessage:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BusinessLogic.Interfaces
{
public interface ISendMessage
{
void SendMessage(string message, string recipient);
}
}
Actually, we want to send out a message by email, so we created another interface ISendEmail, which implements ISendMessage. An email has also a parameter subject, which is not implemented in the general interface ISendMessage (method SendMessage()).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BusinessLogic.Interfaces
{
public interface ISendEmail : ISendMessage
{
void SendMessage(string message, string recipient, string subject);
}
}
Now my question is, how can we be unspecific in the UseCase to stay open for expansion? It could be, that at some point we want to send out WhatsApp or Telegram messages instead of an email. If I implement a property of ISendMessage in the UseCase, I do not have the parameter subject.
I mean this line in the UseCase:
_MessageSender.SendMessage('Message','for@bar.com');
Because we specified the general interface ISendMessage, we cannot provide a subject in the method. If we change ISendMessage to ISendEmail, we are no longer open, aren't we?
How can this be done or do we have misunderstood something completely wrong?
Update
First of all, thanks for your comments and solutions. I think, it is exactly the issue, that we as SOLID beginners, need to understand, when to be specific and not trying to make a program that can do anything in any case. Of course, SOLID does not mean, that no code is changed at all, but the question is where the code needs to be changed.
I think, in all provided solutions the problem is only shifted. In any case, I need to specify which exact type I use in the UseCase. If I would use the solution of @Martin Verjans, I can use the type IMessage, but then I have to be specific in the parameters. By using generics, I have the same issue. The solution of @SomeBody, does not fulfill Open/Closed Principle.
Our goal is to achieve to be unspecific in the UseCase, so that we don't need to change the UseCase itself, if we decide to send WhatsApp messages instead of email. And actually I don't see an option for this.