vilshadov
vilshadov

Reputation: 31

Single Responsibility Principle the correct approach

SRP really bug me. I know how to find responsibility what I dont know is how to assemble them correctly e.g

class Modem{
  public void dial(){//code here}
  public void hangup(){//code here}
  public void send(){//code here}
  public void recive(){//code here}
}

In this common example we have connection and communication responsibility. So we divide that into 2 new interfaces

interface Connection{ 
  public void dial();
  public void hangup();
}

interface Communication{ 
  public void send();
  public void recive();
}

implement them:

class SimpleModemConnection implements Connection{ 
  public void dial(){//code here}
  public void hangup(){//code here}
}

class SimpleModemCommunication implements Communication{ 
  public void send(){//code here}
  public void recive(){//code here}
}

and at this point I dont really know how client code should look like

Do I aggregate those intefaces into Modem class ?

class Modem {
 Connection connection = new SimpleModemConnection();
 Communication communication = new SimpleModemCommunication(); 
}

main(){
 Modem modem = new Modem();
 modem.connection.dial();
 modem.communication.send();
 modem.communication.recive();
 modem.connection.hangup();
}

Do I use them directly

main(){
 Connection connection = new SimpleModemConnection();
 Communication communication = new SimpleModemCommunication(); 
 connection.dial();
 communication.send();
 communication.recive();
 connection.hangup();
}

Or is there some other way?

Upvotes: 1

Views: 177

Answers (2)

sureshvv
sureshvv

Reputation: 4412

I am afraid you may be overthinking this. A Modem needs to both connect and communicate. Unless you know of and expect a situation where these two can be separated out, there is simply no point in splitting this up. At the end, these principles should not be the primary driving force of your design. That should be your knowledge of the domain and the kind of changes you expect in the system.

So what you need to answer is if your Modem class would ever need to swap out its connection/communication subsystems?

Upvotes: 1

Filled Stacks
Filled Stacks

Reputation: 4346

This can be very confusing some times. I struggle with the same questions daily, but it's good that you think about it. I have been actively pursuing the use of the SOLID principles along with my TDD practices. One thing that it seems like you are not thinking of here is who will be using your modem, unit tests and actual object users in code.

In my mind it works like this. Modem has the responsibility of Dialling, hanging up, sending and receiving data, so those are the functions exposed to the user of you API (with the exception of Receive), whether it be your unit tests or your in code user. Therefore the following would exist.

IModem.cs

interface IModem
{
  public bool Connect(); // Seen in your code as dial
  public void Disconnect(); // Seen in your code as hangup
  public void SendData(); // Take data as parameter
  // Receive will not be public, instead I would make it call out to the user saying "I have received data for you"
}

This clearly states that the responsibility of your Modem Object is to Connect, Disconnect and Send data to where it needs to go. Now whether the modem does the connection and communication is the other question. I would say that what you have with your SimpleConnection and SimpleCommunication modules are perfect (I would change the naming a little ;) ). Connection becomes IConnectionModule and Communication becomes ICommunicationModule, as well as some function name changes. dial to Connect, hangup to Disconnect and send to SendData. Having that in mind I take the following approach.

Responsibility of my Modem: My modem will use the modules it has to connect the user to a remote host, as well as disconnect from the host or send any data necessary. The definition above then results in the following code.

Modem.cs

class Modem : IModem
{
  private IConnectionModule _connectionModule;
  private ICommunicationModule _communicationModule;

  public Modem(IConnectionModule connectionModule, ICommunicationModule communicationModule)
  {
    _connectionModule = connectionModule;
    _communicationModule = communicationModule;
  }

  public bool Connect()
  {
    bool connectionSuccess = _connectionModule.Connect()
    return connectionSuccess;
  }

  public void Disconnect()
  {
    _connectionModule.Disconnect();
  }

  public void SendData()
  {
    _communicationModule.SendData();
  }
}

Looking at the above you can lay out the responsibilities as follows:

Modem: Serves as a bridge between the user of the API, allowing the user to send and receive information.

Connection Module: Connects the modem to a host (The user will never use this, the only user to use this will be it's unit test suite and the modem)

Communication Module: Sends information to an already connected host (The user will never use this, the only user to use this will be it's unit test suite and the modem)

The reason for "hiding" the modules from the user is because no user of a modem has to know about which connection module is being used. All you should have to do is call Connect/Disconnect and the functionality should be supplied. The rest should all be "invisible". Again, this depends solely on your development style but I always try to keep things nicely separated and make sure I always have one action per call. Whether it's farmed out to a different object or done within the class itself.

I hope this helps, let me know what you think, I am always up for discussions on designs and SOLID principles.

Upvotes: 0

Related Questions