3

I'm working on an infrastructure for inter process communication. I have created two interfaces, one to send data and another one to listen to data being sent:

public interface ISender<T> : IDisposable
{
    void Send(T dataToSend);

    void Disconnect();
}

public interface IListener<T> : IDisposable
{
    event EventHandler<DataReceivedEventArgs<T>> DataReceived;

    void Connect();

    void Disconnect();
}

I've also created another interface to allow two way communication:

public interface ICommunication<TIn, TOut> : IListener<TIn>, ISender<TOut>
{

}

But I'm not entirely comfortable with the fact that this interface is just combining the previous two interfaces and not adding anything.
To me, it feels to much like a marker interface (even though it's not).

So the question is, do you think it's bad practice, and if so, can you provide another alternative?

  • 1
    I was just watching a PluralSight course on solid principles and the instructor touched on this. He recommends using the exact process that you're describing rather than a single fat interface with methods that may not be used by all implementers. – Phil N DeBlanc Apr 12 at 7:39
  • 1
    @PhilNDeBlanc Thanks, but it's not really a question about Interface segregation which is probably what the instructor was referring to, it's more a question about the third interface. – Zohar Peled Apr 12 at 7:45
  • 1
    If you consider the fact that interfaces define behaviours (or capabilities), it's legit to say that EventListener is a sort of IListener, MailSender is a sort of ISender and a NotificationService is a sort of ICommunication 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? – Laiv Apr 12 at 8:09
  • 1
    @patrick I disagree. I have different projects. Some require send,some listen and some both. So I am gonna need it, one way or another. – Zohar Peled Apr 12 at 19:31
  • 1
    Having had more time to think about it. Don't implement ICommunication in terms of ISender and IListener via inheritance. Instead have ICommunication have properties/functions that return ISender and IListener 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 – Kain0_0 Apr 15 at 3:41
5

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.

  • Thanks for your answer. I do have projects that needs to send and receive information, but receive from one end point and send to another. The main reason why I've created the IComminucation interface is convenience - to initialize a single instance instead of two separate ones. – Zohar Peled Apr 14 at 11:09
  • @ZoharPeled: That's not worth it by itself. Now, the thing is, you can still do it, but then you need to accept that you're using a marker interface. I'm not opposed to marker interfaces but I don't think your current intentions (which is a negligible character reduction) is worth the (admittedly similarly negligible) complication of adding another interface to the codebase. – Flater Apr 14 at 11:40
3

The way to determine if this is advisable for your use case is to look at the calling code. Do you ever have a list of ICommunication in your calling code, or is that purely because you noticed some things are both and you wanted to classify it as such? A subtype relationship is a very strong form of coupling, so you don't want to create it unless you actually need to use it.

That goes for the ISender and IListener as well, for that matter. If you never have a situation where you need one but not the other in the calling code, the interface segregation principle doesn't require you to separate them just because they feel like different things and you want to classify them accordingly. An interface is a working relationship, not a descriptive one.

  • I don't have a list of ICommunication but I do have projects where I need at least one instance of it. About the segragation, I do have plenty of reason to separate the sender from the listener - I have multiple projects where some needs only to listen and some needs only to send data. – Zohar Peled Apr 12 at 11:10
  • Sounds like it's probably appropriate for you then. – Karl Bielefeldt Apr 12 at 11:55
  • Thanks or your answer. I've upvoted it but prefer to wait a little longer before accepting it to see if other opinoins will come along – Zohar Peled Apr 12 at 21:20
1

Thanks to everyone that commented or answered my question.

After carefully considering all the information everyone provided on this question, I've decided to not include the ICommunication interface after all.

I've came to this conclusion based on both Karl's and Flater's answers and on the last comments by Laiv and Kain0_0 - so instead of marking any of the existing answers as accepted, I better post an answer myself and accept it - so here are my reasons:

  • The first part of Flater's last paragraph:

    it's good practice to keep responsibilities separated as best as you can

  • From Laiv's last comment:

    Do you have components that are both things at the same time (sender and listener)? If yes, then it couldt make sense. However, don't you think such component have too many reasons to change?

  • From Karl's answer:

    Do you ever have a list of ICommunication in your calling code, or is that purely because you noticed some things are both and you wanted to classify it as such?

  • Kain0_0's first comment:

    Does IComminucation define a richer expected behaviour then IListener + ISender? Yes then this is fine, there is someone out there expecting this richer behaviour. No then just inherit each interface directly and pass the object as the IListener and as the ISender to whomever needs them.

All things considered, I've decided it's probably a bad idea to have the IComminucation interface.

  • @Laiv sorry for getting your name wrong.... Thanks for the correction. – Zohar Peled Apr 17 at 6:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.