0

Given the following interface

interface Modem {
    public void Dial(string pno);
    public void Hangup();
    public void Send(char c);
    public char Recv();
}

Uncle Bob writes

However, there are two responsibilities being shown here. The first responsibility is connection management. The second is data communication.

...

Should these two responsibilities be separated? That depends upon how the application is changing. If the application changes in ways that affect the signature of the connection functions, then the design will smell of Rigidity because the classes that call send and read will have to be recompiled and redeployed more often than we like.

Can anyone explain to me how is the highlighted statement true? Why would I have to recompile code that calls the sending/receiving methods if the connection signature changes? I could have injected a ready made connection to the code using it.

  • Isn't it his son, Uncle Bob's son? – Celdor Jun 30 '16 at 12:04
7

Let's say that for some reason we'd like to extend the Dial method to accept a structure

struct Endpoint
{
    public string pno;
    ...
}

class Modem
{
    public void Dial(Endpoint ep);
    ...
}

This would cause the whole class to be recompiled - depending on the language and compiler - hence the Send and Recv are. If you'd adhere to the SRP you could - for example - change you modem class to

class Modem
{
    void Dial(Endpoint ep);
    void Hangup();
    Stream ConnectionStream();
}

with a stream that is responsible for sending and receiving data. This avoids to recompile Send and Recv whenever Modem changes, but only the - very simple - method that returns our stream.

Anyway, I think the main advantage of the SRP is not the fact that some code is not recompiled under some circumstances - I'd gues that it does not really matter that much with the computational power available nowadays - but cleaner and more concise code.

  • 5
    +1 for the last paragraph, cleaner code is the main driving force these days behind implementation choices – ratchet freak Sep 1 '14 at 8:44
  • I get that one. My question is, that if I have code like: someMethod(Modem m) { m.Send('a'); } Why would I have to recompile this code in case if the connection signature changes? Or is He only referring to the possible problems caused by actually being able to have calls to Dial in this case? – dsplynm Sep 1 '14 at 8:53
  • 1
    @SzűcsLászló: The compiler will see that the class of someMethod depends on Modem and that Modem has changed in some way, but it can't determine if that change is relevant without recompiling that class. – Bart van Ingen Schenau Sep 1 '14 at 10:31
3

I think the worst part of the interface is the fact that there is temporal coupling.

Clients of the interface are expected to call methods in the right order, and this order isn't enforced by the type system, so it can only be infered by trial-and-error or by pre-existing domain knowledge.

My initial thought is to keep a clean separate interface for the connection that can be used to send stuff:

public interface IConnection // don't pay too close attention to the name, there are better
{
    void Send(char c);
    char Recieve();
}

I want client code using the connection to simply have to do stuff like:

using (var connection = probablyAnInjectedFactory.Dial(someNumber))
{
    // do stuff with the connection
}

Stuff like actually opening and closing connections should be encapsulated in the implementing type I think. A connection could either be opened in the constructor (via some injected dependency) or by a factory. Closing the connection is neat to capture in the IDisposable pattern (I think it's called AutoCloseable or something like that in Java?)

Some adjustments should of course be made depending on the project and requirements. Maybe connection instances need to be reuseable or pooled etc. The main point I'm trying to get across is: If you're working in a statically typed language, USE it. Make it LITERALLY IMPOSSIBLE to compile code with bugs (yes I know it's impossible, I mean it as an asymptotic goal). Make the RIGHT way to use your types the ONLY way to use them.

In the end, this saves countless hours for developers who no longer need to search through poor or deprecated documentation to figure out in what magic order to call methods. In this case, I think this is a way more valuable thing than Uncle Bob's strict definition of SRP.

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.