[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.programming

Thoughts on the Single Responsibility Principle.

Daniel Pitts

3/7/2016 4:26:00 AM

Hello,
I was recently thinking about the Single Responsibility Principle, and
one of the thoughts I had was that it an be applied to *all* levels of
software crafting.

I think it is relatively self-evident that methods/functions should "do
one thing", and that classes should "encapsulate one concept".

But, I want to take the idea further. Namespaces/packages should
contain only things relevant to one responsibility. And a "Project"
should also be limited in its responsibility.

The difference is that the smaller the scope, the more specific the
responsibility should be.

For example, a projects responsibility might be "Provide email access",
a package in that project might be "provide SMTP support", a class in
that package might be a "Manage SMTP Connection", and a method on that
class might have the responsibility of "Send authentication".

I'd even say that statements (in imperative languages) should also have
one responsibility. This means statements such as "foo = ++bar;" are
breaking the SRP, as the statement is responsible for incrementing bar
*and* copying the result to foo.

Maybe what I'm saying here is so self-evident that I shouldn't have
brought it up, or maybe what I'm saying is just plain wrong, or even
oversimplified. I'd like feedback.

Thanks,
Daniel.


6 Answers

Dmitry A. Kazakov

3/7/2016 8:24:00 AM

0

On 07/03/2016 05:25, Daniel Pitts wrote:

> I was recently thinking about the Single Responsibility Principle, and
> one of the thoughts I had was that it an be applied to *all* levels of
> software crafting.
>
> I think it is relatively self-evident that methods/functions should "do
> one thing", and that classes should "encapsulate one concept".

Self evident also is that this does not work for no less evident
reasons. But otherwise appropriately formulated, Occam's razor works for
SW design.

> I'd even say that statements (in imperative languages) should also have
> one responsibility. This means statements such as "foo = ++bar;" are
> breaking the SRP, as the statement is responsible for incrementing bar
> *and* copying the result to foo.

Nope. That depends on the concept here. The concept can well be
"increment and copy" indivisibly. E.g. in concurrent programming atomic
increment+latch is an important fundamental concept.

The morale is, the "concept" is the problem space entity. Otherwise,
when considered in isolation it is pretty much arbitrary. What is single
responsibility in one context could be multiple in another as you move
along abstraction levels and computational frameworks.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-...

Richard Heathfield

3/7/2016 2:50:00 PM

0

On 07/03/16 04:25, Daniel Pitts wrote:

<snip>

> I'd even say that statements (in imperative languages) should also have
> one responsibility. This means statements such as "foo = ++bar;" are
> breaking the SRP, as the statement is responsible for incrementing bar
> *and* copying the result to foo.
>
> Maybe what I'm saying here is so self-evident that I shouldn't have
> brought it up, or maybe what I'm saying is just plain wrong, or even
> oversimplified. I'd like feedback.

I think the SRP is just plain wrong. Please bear with me while I paint a
picture. (A picture is not proof! But it can illustrate a truth, if
we're careful.)

I hope we can all agree that putting the entire program into a single
function is a mistake, for all but the very simplest of programs. Let's
say that we have a million things to do, and that each thing takes one
line. The function would be a million lines long, which is ludicrous. We
can think of this as being a very tall, very narrow program.

Okay, so let's go the other way. Let's have a million functions instead.

We now have a very short, very wide program. And it's just as ridiculous.

But we can make a lot more sense of the program if it has, say, a
thousand functions, each a thousand lines long. Now the program is still
quite tall, but not unmanageably so. And it's still quite wide, but not
unmanageably so.

That's my picture. Now for my argument. So you can stop bearing with me
now. :-)

If we had a Single Responsibility /Strategy/, I could live with that.
Let's try to make each function do one small job and do it well, but
let's accept that there will be times when it makes more sense for the
function to do a bigger job, because it makes some /other/ job simpler.

But a Single Responsibility /Principle/ brooks no argument. We cannot
say, with Groucho Marx, "these are my principles, and if you don't like
them... well, I have others". We must stick to our principles, right?
And if we don't stick to them, our code will fail the review. And so we
have /no choice/ but to go with the very short, very wide program. Every
function will be a one-liner. Every function must take only one
parameter. Every function must return only one value. Banish the IF!
It's ridiculous. It leads sensible, thoughtful people to wonder whether
we should turn

foo = ++bar;

into

foo = bar + 1; ++bar;

It will turn short functions into long functions, and then it will
insist that those long functions be turned into two or more shorter
functions, and we end up with a million one-line functions.

NO, sir! I will cheerfully accept that a Single Responsibility Strategy
can help to make maintenance easier when it is applied as /one/ of a
number of tools for writing better software. But I cannot accept it as a
principle.

These, ladies and gentlemen, are my strategies, and if /I/ don't want to
go where they lead, I have /other/ strategies.

It isn't a great one-liner, is it? But that's kind of the point. I'd
rather have a thousand great one-thousand-liners than a million great
one-liners.

--
Richard Heathfield
Email: rjh at cpax dot org dot uk
"Usenet is a strange place" - dmr 29 July 1999
Sig line 4 vacant - apply within

LudovicoVan

3/7/2016 4:59:00 PM

0

On Monday, March 7, 2016 at 4:25:46 AM UTC, Daniel Pitts wrote:
> Hello,
> I was recently thinking about the Single Responsibility Principle, and
> one of the thoughts I had was that it an be applied to *all* levels of
> software crafting.

The SRP is some variant of what in software design is called Cohesion: in short, the one fundamental engineering principle of design is Modularity, which is achieved in software by maximising Cohesion and minimising Coupling. That said, two remarks:

Design principles apply at the design level, not at all levels: the maximum level of granularity remains a functional unit (a unit of "responsibility"). But, for example, at the level of coding we rather have Readability and altogether another story.

But those principles, at any levels, are only half of the story, software artefacts are *intrinsically* complex, not only in their structure, and not even only in their behaviour, but in the very construction process: it was called Development for a reason, it is a dynamic process that rather requires that we always keep a balance between Optimality and *Redundancy*! (E.g.., as a rule of thumb, I do not extract code into a separate function until I have at least 3 places using it...)

HTH,

Julio

Jos V

3/9/2016 5:31:00 AM

0

On 03/06/2016 11:25 PM, Daniel Pitts wrote:
> Hello,
> I was recently thinking about the Single Responsibility Principle, and
> one of the thoughts I had was that it an be applied to *all* levels of
> software crafting.
>
> I think it is relatively self-evident that methods/functions should "do
> one thing", and that classes should "encapsulate one concept".
>
> But, I want to take the idea further. Namespaces/packages should
> contain only things relevant to one responsibility. And a "Project"
> should also be limited in its responsibility.
>
> The difference is that the smaller the scope, the more specific the
> responsibility should be.
>
> For example, a projects responsibility might be "Provide email access",
> a package in that project might be "provide SMTP support", a class in
> that package might be a "Manage SMTP Connection", and a method on that
> class might have the responsibility of "Send authentication".
>
> I'd even say that statements (in imperative languages) should also have
> one responsibility. This means statements such as "foo = ++bar;" are
> breaking the SRP, as the statement is responsible for incrementing bar
> *and* copying the result to foo.
>
> Maybe what I'm saying here is so self-evident that I shouldn't have
> brought it up, or maybe what I'm saying is just plain wrong, or even
> oversimplified. I'd like feedback.
>
> Thanks,
> Daniel.
>
>

You've mostly got it, but you have to be pragmatic. Especially when you
start trying to define what constitutes a "project" and what constitutes
a line of code. Your prime directive should be managing complexity;
modularity and the SRP go a long way towards that.

But when you start trying to break up a single project into multiple
projects you'll start to find ways in which the divisions aren't so neat
and where you start trying to invent complicated ways to make it work.
Depending on the goals of the end product, this might make sense, but
you have to be pragmatic. Facebook is a single "project" with a single
responsiblity, "social networking", but that's so broad as to be almost
meaningless. The web interface encompasses Chat, blogging, alerts,
entire applications, ad serving, etc., and a lot of those pieces have
nothing to do with "social networking".

You also start running into problems if you try to break lines of code
up by "single responsibility".

incrementedBar = bar + 1; // Exhibit A
foo = incrementedBar;
bar = incrementedBar;

But wait! You've fixed the problem with ++bar, but now you're stuck with
"Exhibit A" that has the same problem!

You just need to be pragmatic. SRP is a good rule-of-thumb way of
looking at code, but your focus should be on making sure that the code
doesn't get too complex for humans to understand.

--
Jos

LudovicoVan

3/9/2016 2:48:00 PM

0

On Wednesday, March 9, 2016 at 5:30:39 AM UTC, Jos V wrote:
> On 03/06/2016 11:25 PM, Daniel Pitts wrote:
> > Hello,
> > I was recently thinking about the Single Responsibility Principle, and
> > one of the thoughts I had was that it an be applied to *all* levels of
> > software crafting.
> >
> > I think it is relatively self-evident that methods/functions should "do
> > one thing", and that classes should "encapsulate one concept".
> >
> > But, I want to take the idea further. Namespaces/packages should
> > contain only things relevant to one responsibility. And a "Project"
> > should also be limited in its responsibility.
> >
> > The difference is that the smaller the scope, the more specific the
> > responsibility should be.
> >
> > For example, a projects responsibility might be "Provide email access",
> > a package in that project might be "provide SMTP support", a class in
> > that package might be a "Manage SMTP Connection", and a method on that
> > class might have the responsibility of "Send authentication".
> >
> > I'd even say that statements (in imperative languages) should also have
> > one responsibility. This means statements such as "foo = ++bar;" are
> > breaking the SRP, as the statement is responsible for incrementing bar
> > *and* copying the result to foo.
> >
> > Maybe what I'm saying here is so self-evident that I shouldn't have
> > brought it up, or maybe what I'm saying is just plain wrong, or even
> > oversimplified. I'd like feedback.
>
> You've mostly got it, but you have to be pragmatic. Especially when you
> start trying to define what constitutes a "project" and what constitutes
> a line of code. Your prime directive should be managing complexity;
> modularity and the SRP go a long way towards that.
>
> But when you start trying to break up a single project into multiple
> projects you'll start to find ways in which the divisions aren't so neat
> and where you start trying to invent complicated ways to make it work.
> Depending on the goals of the end product, this might make sense, but
> you have to be pragmatic. Facebook is a single "project" with a single
> responsiblity, "social networking", but that's so broad as to be almost
> meaningless. The web interface encompasses Chat, blogging, alerts,
> entire applications, ad serving, etc., and a lot of those pieces have
> nothing to do with "social networking".
>
> You also start running into problems if you try to break lines of code
> up by "single responsibility".
>
> incrementedBar = bar + 1; // Exhibit A
> foo = incrementedBar;
> bar = incrementedBar;
>
> But wait! You've fixed the problem with ++bar, but now you're stuck with
> "Exhibit A" that has the same problem!
>
> You just need to be pragmatic. SRP is a good rule-of-thumb way of
> looking at code, but your focus should be on making sure that the code
> doesn't get too complex for humans to understand.

Agreed overall, just you need a lot of study, too: e.g. principles are not just rules of thumb (which are rather called best practices, and so on: there is a *discipline* there)! You need *both*, study and practice, and a commitment to do things properly or it is useless anyway: software engineering is the most complex engineering that there is, with just trial and error in 10 years you'd still be trying to get your head around for loops...

Julio

ram

3/9/2016 10:02:00 PM

0

Daniel Pitts <usenet@coloraura.com> writes:
>I think it is relatively self-evident that methods/functions should "do
>one thing", and that classes should "encapsulate one concept".

I think one should do the following refactor:

slice_and_eat_bread()
{ attach( slicer, bread );
slice = slicer.slice( 1 );
consumer.take( slice );
consumer.eat(); }

============>

slice_bread()
{ attach( slicer, break );
slice = slicer.slice( 1 ); }

eat_bread()
{ consumer.take( slice );
consumer.eat(); }

slice_and_eat_bread()
{ sliceBread();
eatBread(); }

After the refactor, the method »slice_and_eat_bread« still does
/two/ things - in a sense, it slices /and/ it eats.

But OTOH we can see it only encapsulates /one/ concept, that is,
the high-level /sequencing/ of the two steps. (It decides in
which order these operations are done.)

It does not mix the responsibility to determine the sequence
of the lower operations with the resposibility to implement
those operations anymore, which now is done by two other functions.

We can remove the smell »_and_« by a renaming refactor:

slice_bread()
{ attach( slicer, break );
slice = slicer.slice( 1 ); }

eat_bread()
{ consumer.take( slice );
consumer.eat(); }

slice_and_eat_bread()
{ sliceBread();
eatBread(); }

============>

slice_bread()
{ attach( slicer, break );
slice = slicer.slice( 1 ); }

eat_bread()
{ consumer.take( slice );
consumer.eat(); }

use_bread()
{ sliceBread();
eatBread(); }