Previous post in this series:
SOLID – (SRP) The Single Responsibility Principle
Some of my source links in the previous two posts may not have worked in FireFox (but worked fine in IE and Chrome). These links have been fixed. It was an issue with how google was handling the redirects to the source files.
In Uncle Bob’s source material for SRP it mentions one specific example of where it doesn’t make sense to break out the responsibilities.
Here is a modified class based on the source material:
public class ConnectionManager<K, V>
{
void Connect(K EndPoint);
void Terminate();
void Send(V Data);
V Receive();
}
In the source material the object is of a modem with dial and hangup methods but this is a simplified version of that. You can see here that there are really two responsibilities in this interface. The first is that there is connection management. Connect and Terminate are one set of responsibilities that relate to handling the connection. Send and Receive don’t really have anything to do with handling the connection, they have to do with the data flow to/from the end point.
Here are the interfaces separated (based on source material):
public class ConnectionManager<K, V>
{
public IConnection<K> Connection { get; set; }
public IDataChannel<V> DataChannel { get; set; }
}
public interface IConnection<T>
{
void Connect(T EndPoint);
void Terminate();
}
public interface IDataChannel<T>
{
void Send(T Data);
T Receive();
}
Should these clearly different responsibilities need to be separated? You can read Uncle Bob’s answer in the source material. My answer? It’s a tough situation to define without a context. If this were just dealing with connecting via a single type of modem directly where we can think small I wouldn’t decouple. You end up with what Uncle Bob calls, “Needless Complexity.” A modem connects, sends and receives data back and forth and at some point disconnects. The code should be able to handle this cleanly.
Where the problem arises is that most of the time we aren’t dealing with such simple situations. If we do have a complex situation but we’ve implemented our code as in the first sample we end up with what Uncle Bob calls a, “smell of Rigidity.” For instance, what if we have a scenario where we need to send and receive multiple types? Do we create multiple connections even though the end points are the same? Are we going to have to support multiple DataChannels? In the first example trying to expand on the functionality becomes a pain and I often feel constrained in these cases.
All this comes down to a point I’ve emphasized here before. As stated in “Code Complete” one of the most important things you can do that will reap the most benefit is a thorough requirements analysis. If you fully understand the use cases you can develop for them. Then once you fully understand your requirements you can know if it makes sense to separate responsibilities.
Up next is the Open-Closed Principle (OCP).
Thanks,
Brian