Ayende @ Rahien

It's a girl

Single Responsibility Principle, Object Orientation & Active Code

Jason Folkens had a comment on my previous post:

When people combine methods and data into a class in a way such that you are recommending, I wonder if they truly value the single responsibility principle. In my mind, storing both schema and behavior in the same class qualifies as a violation of the SRP. Do you disagree with me that this is a 'violation', or do you just not think the SRP is important?

I can’t disagree enough. From Wikipedia:

An object contains encapsulated data and procedures grouped together to represent an entity.

The whole point of OOP is to encapsulate both data & behavior. To assume otherwise leads us to stateless functions and isolated DTOs.

Or, in other words, procedures and structures. And I think I’ll leave that to C.

Comments

Suedeuno
01/04/2013 02:00 PM by
Suedeuno

To add to what Ayende said, Fowler wrote about this subject in an article titled Anemic Domain Model as an anti-pattern. http://martinfowler.com/bliki/AnemicDomainModel.html

Rob
01/04/2013 02:05 PM by
Rob

I agree. I've seen a lot of this in the last couple of years...The intent of SRP is to simplify code and make it maintainable, extensible,etc....but when you take it out of a particular problem context and apply it ad infinitum...it actually increases the complexity of the codebase...sometimes by orders of magnitude...and naturally creates a serious maintenance disaster.

Jason Young
01/04/2013 02:20 PM by
Jason Young

What a given class does--that is, everything you must know about how to use it, i.e. its contract, i.e. its interface--should generally be a matter of behavior, not data or structure.

In terms of behavior then, anemic domain model and C-style structs adhere to the Zero Responsibility Principle.

Artur
01/04/2013 02:27 PM by
Artur

Agree... SRP does not mean giving the same responsibility to many parts of your code.

Bob Tabor
01/04/2013 02:49 PM by
Bob Tabor

Uncle Bob Martin’s definition of SRP: “There should never be more than one reason for a class to change.” ... and ... “If you can think of more than one motive for changing a class, then that class has more than one responsibility.” (** are my emphasis).

In his video series on Clean Coder, he gives a number of examples of what motivations could possibly exist, including seemingly blasphemous ones like the various departments inside a given organization that may desire a change ... an accounting department's view of a customer (financial) vs marketing (demographics) vs. an executive's view of a customer (aggregate / reporting).

So, when this idea was first introduced, "responsibility" meant a single motivation to change, not a single task or concept necessarily.

Markus Zywitza
01/04/2013 04:24 PM by
Markus Zywitza

Separating code and schema is a common strategy in FP however, Haskell and other functional languages use functions and algebraic data types for this. So it is an anti-pattern in OOP, but common practice in FP.

Now how will you evaluate this in C# which is an OOP language with some functional elements? Are extension methods an anti-pattern? Or Linq to Objects?

Frank Quednau
01/04/2013 06:58 PM by
Frank Quednau

Markus, this is exactly the thing why I fail at OCP in FP, how is it done?

Tom
01/04/2013 07:38 PM by
Tom

So how do you inject dependencies into the objects that are needed for their operations? What if the object is instantiated by a 3rd party component (EF, NHibernate...)?

João P. Bragança
01/04/2013 08:37 PM by
João P. Bragança

Do all methods of your object use the same dependencies? If not why not pass an interface / delegate to the method call.

Jiggaboo
01/04/2013 09:39 PM by
Jiggaboo

@BOB: I don't think you understand what Uncle Bob meant. If your class does two things, executes two task then there are at least to motivations to change that class. So single responsibility is single task. Not what you say.

Dmitry
01/04/2013 09:49 PM by
Dmitry

I totally agree. The Single Responsibility Principle and other principles are almost always talking about the behavior.

PaulRMortimer
01/04/2013 10:08 PM by
PaulRMortimer

I'm sticking with Uncle Bob's Definition. "Full Fat" domain models tend to go against the Single Responsibility Principle (SRP).

Scott Hanselman & Bob discuss this @3 mins in the following podcast

http://www.hanselminutes.com/145/solid-principles-with-uncle-bob-robert-c-martin

Dev
01/04/2013 11:56 PM by
Dev

Are you going to resume the excellent design patterns series any time soon? Seem to got stuck at Command pattern for a while now...

I found the series a great help in better understanding of patterns especially from the .net point of view. If you ever decided to publish this in a book form I would be very happy to be your first customer!

KCs
01/05/2013 07:06 AM by
KCs

I highly recommend this article (and by the way the whole blog):

http://www.carlopescio.com/2012/07/life-without-stupid-objects-episode-1.html

It's not for the TLDR kind of people.

Bob Tabor
01/05/2013 06:13 PM by
Bob Tabor

@Jiggaboo … I think we're missing each other on the semantics of the term "tasks" -- it seems to mean different things to each of us, however I think (hope) we're talking about the same thing in general. I would say this. Listen to the link @PaulRMortimer includes in his post (above) from Uncle Bob … that's how I understand SRP. Would you agree with Uncle Bob's description of SRP from that excerpt or not?

flukus
01/05/2013 11:47 PM by
flukus

"The whole point of OOP is to encapsulate both data & behavior"

That's a surprising statement considering how much time you have spent disagreeing with it over the years. The active record pattern, for instance, encapsulates data and behavior.

"To assume otherwise leads us to stateless functions and isolated DTOs."

Isn't this exactly what you've been advocating for? IE, your articles on command/query architectures end up exactly this way. A query (dumb), is executed by another class method (stateless) and returns a model (dumb).

The "enterprise" app I'm working on at the moment is a mess of spaghetti code precisely because it mixes data and behavior. eg:

var dto = new CompanyDTO(); dto.Load(56); //load company string s = dto.Name; //Name is set in the load function

Ayende Rahien
01/06/2013 07:57 AM by
Ayende Rahien

Flukus, Most of the time, you want to have data in the object, and NOT pass around DTOs. Sure, CompanyDTO is wrong, but not because it has data & behavior. Because it has the wrong behavior. Company (not DTO) should have the company data and company behavior (for example, payment policy, etc). Loading the data is not a related behavior, can & should be external.

Patrick Smacchia
01/06/2013 10:32 AM by
Patrick Smacchia

In a sense I understand what Jason Folkens meant. With the experience over the years, I favor immutable data objects, and pure controllers (i.e classes with no state and no side effect) . Immutable data objects contain contracts. They sometime also contain side effect free behaviors (like ToString() for example) but it is not a rule (not an exception).

Doing so doesn't mean I left aside encapsulation, interfaces and inheritance, hence I am not a C programmer :)

But doing so makes the code highly testable and much less error prone.

Gene Hughson
01/06/2013 03:39 PM by
Gene Hughson

I have to agree with Patrick, data objects and stateless controllers are a far cry from traditional C apps.

Additionally, I'm not seeing the disadvantages I would expect from something deemed an "anti-pattern". The main argument seems to be that this style is not theoretically pure...not an argument I'm sympathetic to.

stacey
01/06/2013 04:23 PM by
stacey

This seems a little above my head, but it looks to me as if the argument is whether or not data-storage models should contain any of the logic pertaining to the operations they perform. Is that anywhere in the right ballpark?

Jack Andersen
01/06/2013 04:27 PM by
Jack Andersen

Well, that being an object representing a identity, state and behavior..

Misconception occurs when you slice and dice and SRP isn't maintained..

Company should represent company only.. Loading, etc has nothing to do with Company itself..

I often see this violated, typically by inheritance, and not cutting the cake correctly.

Wayne M
01/07/2013 12:49 AM by
Wayne M

I think what Ayende is trying to convey is to apply some common sense when talking about SRP. Logically yes, you could make the argument that any kind of behavior in a class violates SRP, but that's taking it a bit extreme.

SRP states that UNRELATED behavior should be in its own class. For instance, ActiveRecord violates SRP because data access has nothing to do with whatever you're modeling (let's say a Product class for simplicity's sake). On the other hand if your Product class had a bit of logic to calculate its own discount or total (e.g. price * qty), that does not violate SRP because that sort of behavior is related to the Product class.

Daniel Lidström
01/07/2013 01:37 PM by
Daniel Lidström

It seems very few developers actually understand object oriented development. I've not seen it applied at my workplaces (.NET) since I left the C++ world. My theory is that combining object oriented development with storing those objects in a relational database is what has been messing things up. I'd like to say that it is not viable to do object oriented development and at the same time store those entities in a relational database (i.e. do o/r mapping). Of course, as you would agree, this is where RavenDB fits in. I guess that's a topic for many other posts :-)

Bruno Juchli
01/08/2013 06:26 AM by
Bruno Juchli

I believe that the SRP leaves many things unexplained. I believe the idea of SRP was good. But the phrasing and communicating leaves so many things open to interpretation (what constitutes a responsiblity?). There's always discussion about it, and half the time people thinking they've understood probably didn't. I would vote to strike SRP from all books and replace it by a more goal oriented description*. We want maintainable, extensible and testable code. Using a test-driven approach we probably pretty soon (in a matter of weeks or months) learn how to achieve simple tests. Given that this is probably the single most important thing about OO development, it should be worth the time, shouldn't it?

*please note that i did not give a complete goal oriented description. I admit it's a lot of speculation without something to compare...

Patrick Huizinga
01/08/2013 09:23 AM by
Patrick Huizinga

Bruno, I think you should see SRP as a guideline. Just like 'long methods are bad' is a guideline.

In both cases you wage full blown wars over the exact definition. Or you can just keep it in mind while developing and let your judgement decide whether or not you violated it in every situation.

nick
01/08/2013 09:53 AM by
nick

@Patrick Huizinga, @Rob

I have a similar opinion to you guys and I'm doing a presentation about it.

Wanna review my slides? https://github.com/NTCoding/Presentations/tree/master/SOLIDisa_guideline

Jack Andersen
01/08/2013 04:13 PM by
Jack Andersen

Yes, I too consider SRP being a guideline..

What I've learned through the years is to incorporate it into your mindset, and not just as a checklist, looking at "what is best practice"..

And I agree that OO in general often is misunderstood as a whole. How to model your objects, communicate, making them lightweight and testable.

Quick example is a junior developer I've worked with, doing a huge class hierachy, with validation and buisness rules, that combined with ORM state and behaviour..

So what I do, is that I ask the Question.. Looking at the real world (and we try to convey, the real world)... When doing simple OO, when modelling, somekind of entity.. When posting that mail in the mailbox, does it send itself? Or do we need a mailman to handle this?

I've had great succes with that notion of looking at objects as objects in the real world - talking to juniors.

jmorris
01/13/2013 05:35 PM by
jmorris

The encapsulation of data and then methods that work on that data (traditional OOP definition of a class) has nothing to do with SRP. SRP ensures that data and methods encapsulated within that class/object have a single purpose - cohesiveness.

Dmitry Diukariev
04/07/2013 07:28 AM by
Dmitry Diukariev

Don't make things complex. It's very easy. Every object provides some public interface (which abstracts details) and might have PRIVATE, hidden state which dictates it's behavior. All methods modifying or reading private state should belong to the object, otherwise you will have to reveal the internal state and loose the benefit of abstraction. Everything else that uses abstract contract (operates on public interface) should be moved away. The reason for this is that if you put it together it makes your code much less flexible. For example you have class Customer with property Name. If you put logic of validation of this property or logic of storage of this property into Customer itself you have limited yourself to having only one possible validation per customer. Now consider situation when you have different rules of what makes Customer name valid dependent on context. With regards to SRP I agree to jmorris. SRP states just what high cohesion requires.

Comments have been closed on this topic.