The core question here is whether you're ever going to have an explicit requirement for an object to implement both interfaces.
When you don't, that means that you're only doing it to save some characters in the class definition, which is not enough of a justifiable reason to create this pseudo-marker-interface.
However, if there is an actual need for an object which does both, then it does become relevant.
Suppose you're trying to create a method in which you need to send and receive data. Since these are two separate responsibilities, you're likely going to split these out anyway:
public void Synchronize(ISender<int> sender, IReceiver<int> receiver)
{
sender.Send(myInt);
myInt = receiver.Receive();
}
Even if you wish to use a class that just happens to implement both interfaces, you can simply pass in the same object:
Synchronize(myCommunicationObject, myCommunicationObject);
At this point, you don't need the joined interface, because the send/receive responsibilities are two separate tasks. However, if your business logic requires that you exchange data with the same object (for whatever reason), then the above method makes it impossible to prevent that sender
and receiver
are the same object (and no, an equality check and possible exception is of course not a good way to solve this).
This is where the joined interface becomes relevant, because you now have an explicit need for a single object with both send/receive capabilities. When you create this interface, it becomes possible to enforce sending/receiving over the same channel:
public void Synchronize(ICommunication<int> channel)
{
channel.Send(myInt);
myInt = channel.Receive();
}
This may not apply to your current use case, but there are cases in which this would be a valid solution.
The core of the answer here is that it's good practice to keep responsibilities separated as best as you can, but when there is an explicit need for two responsibilities to be conjoined, then creating that conjoined responsibility is the most sensical approach.
EventListener
is a sort ofIListener
,MailSender
is a sort ofISender
and aNotificationService
is a sort ofICommunication
that handles events and send emails. Such a service needs to convey with a specific contract which is the result of mixing other 2 different contracts. If you need to provide with such abstraction (facade), what's the problem?ICommunication
in terms ofISender
andIListener
via inheritance. Instead haveICommunication
have properties/functions that returnISender
andIListener
objects. Why? The concepts are at different levels of analysis. A communication object has send/listen aspects, but a communication is more than just that there are implicit rules about when to send, when to listen, whether the send/listen are even to/from the same outside entity. These extra rules are sure to break LSP, and will bleed implementation details into the domain