We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

The Single Responsibility Principle.

00:00

Formal Metadata

Title
The Single Responsibility Principle.
Title of Series
Number of Parts
110
Author
License
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Let's do a deep dive into the first of the SOLID principles. What is a responsibility? Why should it be single? How do we make it single? We'll examine all these issues in extreme detail. We'll look at some old code to see whether it conforms or not. We'll also look at various design patterns and strategies that help us conform to this principle.
Software developerPhysical systemExponentiationDependent and independent variablesSelectivity (electronic)Food energyDisk read-and-write headOptical disc driveCodePrinciple of maximum entropyMathematicsDiffuser (automotive)RandomizationPhysicalismCentralizer and normalizerUniverse (mathematics)Special unitary groupNumberOrder (biology)Multiplication signAreaWordMoment (mathematics)Single-precision floating-point formatEntropie <Informationstheorie>Degree (graph theory)CASE <Informatik>FrequencyDifferent (Kate Ryan album)
CalculationInclusion mapCondition numberProgrammschleifeSingle-precision floating-point formatCodeDependent and independent variablesTraffic reportingView (database)File formatSoftwareDatabaseQuery languageMathematicsNeuroinformatikData managementFunctional (mathematics)NumberRule of inferenceString (computer science)Physical systemEmailGame controllerModule (mathematics)Statement (computer science)CalculationDecision theoryDivisorBit rateDifferent (Kate Ryan album)DecimalTotal S.A.Ferry CorstenCompilation albumData structureSequelPoint (geometry)Loop (music)Process (computing)Form (programming)Computer animation
CalculationWide area networkTunisTraffic reportingTouchscreenDependent and independent variablesSingle-precision floating-point formatModule (mathematics)Point (geometry)MultiplicationPosition operatorProcess (computing)SoftwareView (database)Rule of inferenceMathematicsData managementFile formatRoundness (object)Multiplication signPhysical systemCodeNumberTotal S.A.Game controllerDecision theoryPrincipal idealState of matterCodeComputer clusterSequelComputer animation
Software developerTouch typingDifferent (Kate Ryan album)MathematicsData managementTraffic reportingDependent and independent variablesSoftwareModule (mathematics)Rule of inferenceSingle-precision floating-point formatMixed realityMultiplication signSet (mathematics)Endliche ModelltheoriePrincipal idealSocial classComputer animation
CalculationRankingWide area networkNumeral (linguistics)MIDILemma (mathematics)Software developerIterationNormed vector spaceMenu (computing)Test-driven developmentTraffic reportingPhysical systemMechanism designDifferent (Kate Ryan album)DatabaseDependent and independent variablesFunctional (mathematics)Object (grammar)Element (mathematics)SubsetTouchscreenGraph (mathematics)Expected valueMultiplication signRule of inferenceView (database)File formatBitRepresentation (politics)String (computer science)IntegerGroup actionSocial classCartesian coordinate systemRight angleDescriptive statisticsUniform resource locatorSoftware testingResultantBusiness objectHecke operatorSingle-precision floating-point format1 (number)Computer programmingNumeral (linguistics)CodeData structureCASE <Informatik>Sound effectOrder (biology)MereologyLine (geometry)System callStatisticsFingerprintFrequencyAlgorithmSoftware developerFood energyRoundness (object)Data storage deviceComputer animation
Software developerOvalPlateau's problemWechselseitige InformationMenu (computing)Execution unitTask (computing)CodeData managementTraffic reportingSocial class2 (number)DatabaseMessage passingSingle-precision floating-point formatCartesian coordinate systemWeightFlow separationSequelFlash memoryDifferent (Kate Ryan album)Repository (publishing)Dependent and independent variablesPhysical systemRule of inferenceProcedural programmingGame theoryMathematicsMereologyCASE <Informatik>Pattern languageData storage deviceFunctional (mathematics)SoftwareHand fanMultiplication signQuery languageComputer architectureFundamental theorem of algebraSign (mathematics)INTEGRALRight angleGroup actionBusiness objectFrequencyMoment (mathematics)Computer fileElectronic mailing listLine (geometry)Projective planeSoftware architectureRippingElement (mathematics)State of matterEqualiser (mathematics)Machine codeData structureStructural loadCalculationNumberIntegrated development environmentComputer clusterComputer animation
Software developerIntelWechselseitige InformationCodeGraph coloringMaxima and minimaPosition operatorProgrammer (hardware)Functional (mathematics)Module (mathematics)ImplementationCartesian coordinate systemNumberCausalityDependent and independent variablesSingle-precision floating-point formatMathematicsTouchscreenRight angleOrder (biology)Forcing (mathematics)Vapor barrierSource codeGame theoryPeg solitaireComputer filePhysical systemChainMereologyNeuroinformatikTotal S.A.Software developerProduct (business)Hecke operatorTraffic reportingMultiplication signMenu (computing)Flow separationGroup actionControl flowRule of inferenceData structureData managementMultiplicationLogicView (database)Web pagePrincipal idealSystem callGoodness of fitCore dumpMarginal distributionCryptanalysisFilm editingComputer animation
Software developerModule (mathematics)Game theoryoutputDependent and independent variablesRule of inferenceVideo game consoleInterface (computing)Logistic distributionSystem callOrder (biology)StapeldateiDataflowAlgorithmTouchscreenFlow separationUser interfaceCodeSoftware developerWebdesignPlastikkarteMultiplication signOperator (mathematics)MathematicsSoftware testingMehrplatzsystemArithmetic meanTest-driven developmentComputer programmingWordCode refactoringMereologyScheduling (computing)SoftwareMoment (mathematics)Line (geometry)Mechanism designArmMessage passingTerm (mathematics)Electronic program guide1 (number)DivisorMultiplicationCryptanalysisComputer animation
Transcript: English(auto-generated)
Good afternoon. Good afternoon. It's late. We've got one more talk to do. Then we're going to find some beer somewhere.
Imagine that I had a thousand coins. And I threw them out onto the floor. How many would land heads up? How many would land tails down? About half, someone says. About half would land heads up and about half would land tails down.
Probably within 20 or so. 22 to the 20th is about a million. So the odds are within one in a million that the number of coins will be between 480 and 520 heads.
If I were to walk around that room for a day or two, just kicking the coins, paying no attention to them,
how would the number of coins heads up change? I may have kicked all the coins by the time I'm done. But the number of heads would remain roughly 480 to 520. There would be no noticeable change. If all you were looking at was the number of heads, you would find no noticeable change at all.
The system would be in maximum entropy. Maximum entropy is when no matter what you do to it, it doesn't appear to change. You just flip things around randomly and nothing appears to change. That is random behavior. That is maximum entropy.
If you knew that these coins were in the room and you knew that I had been kicking them around, and you came in one day and you found only 200 heads and 800 tails, you would assume that I had been in there and intentionally turning coins over.
You would suspect some kind of non-random agency, some kind of selection had been taking place. This is not necessarily so, but the odds would be so astronomical that only 200 heads were there,
that you would draw that conclusion anyway. What could you say about the day before? You walk in there and there are 200 heads and 800 tails.
What would you say about the day before? Can you say anything about the day before? Yes, as it turns out, you can. What is the most likely situation for the day before? The most likely situation for the day before is 520 heads between 520 and 480 heads.
That is the most likely situation in any case. So regardless of what you found today, you would have to look back in time and say, well, it should still be about 480 to 520 heads.
Entropy, we think of entropy as always increasing. That is not the case. Entropy tries to find a nice stable area and then not change. In fact, the most likely situation for any system is maximum entropy.
That is the definition of maximum entropy. Our universe does not behave that way. Our universe behaves in a way where, first of all, we live in remarkably low entropy. Remarkably low. There is far too much order in this universe. The earth goes around the sun.
The sun goes around the center of the galaxy. There are several galaxies and they are all distinct. The universe is composed of order and that is extremely unlikely. The way the universe ought to look is a nice diffuse random gas at about 4 degrees K with no difference anywhere. That would be maximum entropy.
And we have gotten used to this notion that entropy gradually increases. But the only reason we think that is true is because we happen to live during a period of remarkably low entropy. And so, of course, as the coins get kicked around, the entropy gradually increases.
Far more disturbing than that is the fact that as we look back in time, we suspect that entropy was even lower. Never should be that way. You go back in time, entropy should be higher because the odds are much more likely that we should be in a high entropy system.
And yet, our physics tells us that as we go back in time, entropy gets lower and lower and lower until the moment of the Big Bang where we are at zero entropy. An impossible situation. The number of zeros in the exponent of a zero entropy situation.
The odds against a zero entropy situation are unbelievable. That's kind of the central problem of physics today. But never mind any of that. Because we have to talk about the single responsibility principle. We'll have to come down to Earth and deal with mundane issues like code.
We can't deal with this physics anymore. Well, we could because it's getting late. And I may wander. But one of the principles of software design, one of the so-called solid principles,
is called the single responsibility principle. This principle says something very interesting about code. Has anybody seen any view code that has SQL in it?
Ooh, look at the number of people who have seen this. Now this violates the single responsibility principle. Never mind that. It's also just bad. Why is it bad? What's wrong with putting SQL in your view code?
Why don't we just always do that? Why do we try to separate things? Why don't we just write all of our systems in the view module? Just a bunch of HTML and junk with SQL in it and maybe some if statements and while loops.
Why don't we just merge it all into one great big module? Somebody said it would be a nightmare. Somebody said they did that. Somebody else said it would be a nightmare. Well, why would it be a nightmare? What's so bad about that?
You answer the question. The answer is easy to answer. It would be impossible to maintain something like that. It would be possible to make changes because everything would be combined with everything else. Where would you find anything? The single responsibility principle is a simple idea.
It says things that change for the same reasons should be grouped together. Things that change for different reasons should be separated. The view code changes for very different reasons than the database.
The SQL queries will change at a completely different rate and an entirely different reason than the HTML. And they'll both change for different reasons than the business rules. So we want to separate those so that things that change together are in the same modules
and things that change for different reasons are in different modules. Suppose I showed you a module that generated a report. And this module that generated a report looped through a data structure, computing totals,
making decisions about those totals, determining whether certain columns should be turned red or green, and then printed the report based on all of the computations it had done. And let us also say that this function that we're writing controls the format of the report and has the header strings in it and a bunch of other strings that you put in there to denote certain statuses.
Maybe there's a nice column that has an X in it for certain conditions and a plus in it for other conditions and a minus in it for still other conditions. And this module contains the plus and the X and the minus strings.
And maybe there's headers across the top and our module contains those strings. Would that violate the single responsibility principle? The answer is of course it would. Because the calculations will change for reasons that are different than the strings.
The strings are cosmetic. They're going to change for one reason. Some guy is going to look at them and say, that shouldn't be an X, it should be a star. Someone else will look at the business rules and say, you're adding the wrong factor here.
You should be adding a factor of .026, not a factor of .024. Someone else will look at the way the columns are arranged on the report and say, those two columns should be swapped. And so the report function will change for different reasons.
Because of different people who would change the business rules. What person would demand that the business rules be changed? Some business analyst or some manager or somebody out there who is concerned with the business.
They would demand that the business rules change. Who would demand that the format of the report changes? Some clerk, some clerk whose job it is to read the report. And finds that it's inconvenient to read the report the way it's formatted.
And so they would come back to us and say, could you move those columns? Is there some way you could get those columns? Does this number have to have five decimal places? Can it have two, please? They're completely different people. That's what a responsibility is.
The single responsibility principle talks about responsibilities. A responsibility is a person. It's not something that the code does. It's a person. A person who wants to make a change in the code.
My code has a responsibility to that business guy who wants the rules changed. My code has a responsibility to the clerk who wants the format changed. And so the single responsibility principle says any module should be responsible to only one person.
Now that's a slightly confusing statement. Because what if you're the only guy? What if you're the guy who reads the report and judges the business rules? Does that mean that I can now cram all that stuff into one module?
No. Because even though you're one guy, you're playing multiple roles. You have the role of the business decider, and you have the role of the report consumer. So the single responsibility principle really says that a module should be responsible to one and only one role.
Regardless of how many people are actually implementing that role. If I put SQL in my HTML, then I've got the role of the DBA and the role of the guy looking at the screen in the same module.
And that means that two different people, at least, will ask me to change my module. Now what happens if the guy who is the report consumer asks me to make a change?
He wants me to swap the two columns on the report. But let's say that my module violates the single responsibility principle and mixes the business rules in with the report formatting. If I swap those two columns, am I going to affect the business rules?
Might I, by accident, fiddle with the business rules and alter them? Make a bug? Who's done this before? Change something completely inconsequential, some formatting thing, only to find
out that you have inadvertently broken a very important business rule. Who's done this? Look at the round here. Virtually everyone has done this. Why? Because we violated the single responsibility principle. Now look at that from the point of view of the people who use your software. The people who use your software say, man, can you just change the position of those two columns?
And when you do, the totals are wrong. And they look at you like, what did you do? I just swapped the columns. Yeah, but the totals are wrong.
Yeah, but that's because the code is all in one place and I forgot to take those totals and swap them too. Well, you idiot. Imagine the fear in the minds of the managers and the customers as they make a simple request and it turns into this malfunction.
What conclusion can they make about us? What could they possibly think about us? The only conclusion they can come to is that we've lost control of our software and we don't know what the hell we're doing. And so what will managers do to prevent us from continuing to screw up the software?
Has anybody worked in a system where there was one module and every time you touch that module, it didn't matter for what reason, a whole bunch of stuff broke.
And after a while, the managers are going, Christ, did you touch that module again? Yeah, we touched that module. Well, you broke the system. Yeah, well, that module's a mess. Well, you know, we can't tolerate this anymore. Customers are calling us and they're upset. So nobody changed that module. Official rigidity sets in.
The managers have decided no one touches that module. It's too risky. And now what do you do? Now you want to swap those two columns. How do you do it if you can't touch that module? You write a new report. Everybody likes that idea. I can't touch that module. I guess I'll just redesign it.
I'll just redesign it. How will I redesign it? Well, I'll mix the business rules and the formatting again. But I'll be better about it this time. Single responsibility principle says that if you're designing your software, you consider who is using it.
Who are the roles that are using that software? And how can you isolate the software so that when one guy asks for a change, that change will not affect anybody else? And that's a very interesting question to ponder.
How can you separate the software so that the people who request one kind of change will be unaffected by the people who request a different kind of change? That's what the single responsibility principle is all about. Imagine this class. This class has four methods in it.
It's named employee. The methods are calculate pay. That one just calculates the guy's pay. This is a payroll application, so that one's loaded full of business rules. We've got another one called save. That one saves it on the database.
We call save and it stores the employee at some persistent location. The next one is describe employee. That one generates a string with a small description of the employee in a particular format.
The intent is to call that function for every employee in order to assemble a large report. And the fourth function is find by ID, which goes to the database and finds the employee based on that ID. How many responsibilities do you count?
How many different roles people are being served by that class? The answer to that is pretty straightforward. We've got two database functions, save and find by ID, so that's probably one role. And that's most likely the DBA or the system architect or one of those guys who deals with the persistence mechanism.
Who is the responsibility there? The calculate pay function, well that's probably some accountant somewhere who's responsible for figuring out what the business rules are for this payroll application. And the describe employee method, well that's some clerk or group of clerical people who need to read that report.
So this class, simply by the names of its functions, must violate the single responsibility principle. Now you look at that class, it looks perfectly normal.
Who's ever seen a class like that? It's got built in database functions, got some business rules stuff, a little bit of formatting for the view. Anybody seen business objects that have methods which the view calls and it formats things for the view? By the way, the view shouldn't know about your business objects at all.
The view shouldn't know anything about the internals of your application. The view should be this stupid thing that sits on the periphery and asks dumb questions of some stupid data structure that your application has prepared for it.
You don't want the view reaching in like an octopus and touching all your business objects. Because if you do, then you start writing methods in your business objects to serve the view. And the business objects have no business knowing anything about the view. So here we've got a class that's clearly got three responsibilities.
And yet something in that class, something about the way that class structure appeals to us. We like it. We like that. We like the fact that the employee can calculate its pay. We like the fact that the employee can save itself to the database.
We like the fact that it can generate its own element of a report. Isn't that, after all, what objects are supposed to do? Aren't you supposed to put all the methods in an object that that object is supposed to know how to do? Yeah, you are, but that makes for a really coupled, nasty system.
That's one of the reasons that object-oriented systems get so hard to maintain. Because we jam so much stuff into single classes that don't belong there. How could we separate these into different classes? Well, there's a whole bunch of ways.
For example, on a side note, I didn't know that was up there, but since it is, I'll talk about it.
Who does test-driven development in here? Ooh, more people than in the last one. That's good. About half of you are doing test-driven development. And about half of the people with their hands in the air are lying. Not because they aren't doing it. It's just that they don't know what test-driven development really is. Now, how many of you get the feeling that test-driven development is slow?
A few of you do. You're right. It feels slow. This graph is an interesting graph. It was put together by Jason Gorman. And he gave it to me because I asked him for it. He said he did this interesting experiment.
With himself. Nobody else. Just him. He wrote the Roman numerals kata three times. No, six times. Excuse me. Six times. Six times. Over six days. He wrote it once per day.
Now, the Roman numerals kata is a pretty standard little problem. You hand an integer to a function, and it hands you back the string that represents the Roman representation. So you hand in a one, it gives you an i. Hand in a two, it gives you two i's. And so on. It's a fun program to write, by the way. Just try writing that one time.
You'll sit there for several hours trying to figure out, well, how the heck do I do this? Anyway, he had figured out how to write it a long time ago. He had written it many, many, many times before. So this was not an exercise in him trying to solve the problem. It was an exercise in him just writing the code that he already knew how to write. He wrote himself an acceptance test.
Which, once he was done, he could pump it into the acceptance test. And the acceptance test would tell him if he had completed it properly or not. Then, over six days, he wrote it once, with TDD, once without, once with, once without, once with, once without.
Trying to control for the difference. And the result is what you see on the screen. Notice that the bottom line there is 22 minutes. So it goes from 22 minutes to 30 minutes. It's just showing you the top part of the graph. It's a trick that lots of statisticians like to use in the newspaper when they want to exaggerate the effect of something.
They just show you the top part of the graph. Well, that's what he's done here in order to exaggerate the effect. Still, the effect is very interesting. In every case when he did test-driven development, it was several minutes shorter.
About three minutes. In every case when he did not use test-driven development, it was a few minutes longer. That's a remarkable outcome. Especially since he already knew the algorithm. Notice, however, that throughout the period, he got better.
Every day it got a little bit better. Which you'd expect he'd been practicing. Every day was another practice round. Why did the non-TDD take longer? Because in every case, when he thought he was done, he wasn't. And he had to go back and add something.
With TDD, when he thought he was done, he was done. So his acceptance tests always passed. On the TDD side, they didn't pass the first time on the non-TDD side. All of that is very interesting and kind of expected. What wasn't expected was his impression.
Something that's not on the graph. His impression was that the TDD was slower. He said it felt slower. Even though it was faster, it felt slower. But never mind that.
That was just on my screen by accident. Yes. Let's try a... How would you fix this?
How would you fix that? How could you get those responsibilities out of there? Sorry?
The repository pattern. Well, that would be interesting for the database. If I got the save and find functions, and I put them in another class called the employee repository, then my code, when it wanted to save an employee, would not go to the employee and say, save yourself.
Save yourself! The code would go to the employee and say, here, let me take you and give you to the repository. Then it would say to the repository, save it. The same would be the case with the find by ID. The find by ID would be part of the repository.
You would go to the repository and say, find this employee by ID, and the employee would come back out. That would be very nice, because then the employee would not have any of that database functionality. You would never go to a database and say, save yourself. You would instead save it through a repository. All right, fine. That helps us with the database.
How about the describe employee and calculate pay stuff? We have to separate them somehow. How could we separate them? Here is the thing about reports.
Where there is one, there will be more than one. Reports proliferate. They are like gerbils. No, they are more like tribbles. They are born pregnant.
As soon as you have a report, you know you will have more reports. Then they will split into even more reports. Imagine what this class is going to look like a year from now. Describe employee in state one. Describe employee in state two.
Describe the employee for the hourly report. Describe the employee for the commission report. Describe the employee for the benefits report. And you wind up with a long list of describe methods or report methods inside your business object. Anybody seen one of those? Nice little business object.
Cute, nice, tight little business rules. Pretty little thing. And then the list of reports just grew. Well, what we would like to do is somehow get those reports out of there. And we could do that by creating yet another class called the employee reporter.
And what you would do is you would take the employee and hand it to the employee reporter and say, please report on him. And very likely we would have several different reporter classes. One for the hourly report and another one for the commission report. And still another one for the benefits report so that we would not even be coupling them all together.
Now you think about a structure like that and you say, well, wait a minute. If I do that, then all my classes are just going to have one method in them. Well, not quite. Won't be one method. It will be two or three or five.
And that's not a bad thing. How many methods should a class have? As small a number as possible. This is a good answer. I don't want to take a class and load it up with a whole bunch of methods. Anybody seen a class that's obviously a garbage can for all the methods you don't know where to put?
Usually a class like this is called the resource manager. Sometimes it's called the database. The database is this thing that sits in the center of our system. It's the important part. It's the middle of the system. And so we put into it everything that we don't know what to do with.
How the database will do that. No, I don't want the database in the middle of the system. The database is a detail. I want it sitting off on the outside. I don't want the business objects to know that there is a database. I don't want the database anywhere near my application. It should be an appendix hanging off the side.
Because databases get inflamed and you need to remove them. And replace them with something else. Anybody fiddling around with NoSQL? Ooh, look at that. All in the same group over here, too. NoSQL database is the new fad, right?
You have to have fads in software from time to time. NoSQL databases are databases that don't use SQL. They're not relational. I think this is a good idea. I like the idea. I, in fact, am using a NoSQL database for one of the projects that I'm involved with at the moment. What's not a good idea is for my application to know that it is a NoSQL database.
My application shouldn't care. It shouldn't care that it's a SQL database. It shouldn't care that it's a NoSQL database. My application should have no idea what kind of database I'm using. The database is just a detail off to the side, managed by a few interesting classes like the repository classes.
And if I ever want to change the database, I just have to find those classes and rip out the one implementation and put in a different one. In fact, it should be trivial, relatively trivial, for you to come in one day and say,
This database we've got sucks. Let's put a new one in and see if that's any better. And you should be able to do that over the period of maybe a week or so. Change a few lines of code in a few different files. Okay, let's try Mongo. What's Mongo?
It's a document store. No, it isn't. It's the planet where Ming the Merciless ruled. Right? They call this database Mongo. Okay, fine. Flash Gordon went to Mongo with Dale Evans and Professor Zarkoff. Got to get these things straight.
So maybe we're using some database. This is a .NET kind of conference, isn't it? How many of you are doing .NET stuff? See there? This is kind of a .NET-y like conference. A lot of .NET people here. And what database do you guys use? Sorry. You shouldn't have asked that.
That's kind of like asking what IDE you use. Worthless question. You've all got to enthrall to the one. Anyway, what if you wanted to change that database? Yes, you're right.
The secret police would be at your door within seconds. But what if you wanted to change that database and decide to use Mongo or Couch? What a great name for a database. Couch. You ought to be able to do that. You ought to be able to do it as an experiment. Just to try it. How would that work?
Most of us, however, have systems that are so entangled with the database the very thought of even trying to change it from one vendor to another is so hideously horrible that we won't even think about it. And because of that, the next system we write, we won't have thought about it.
And so we will entangle it again. That's a huge violation of the single responsibility principle because database schemas change for reasons that are completely independent of all the business rules. Who's seen stored procedures with a whole load of business rules in them? What kind of nonsense is that?
The business rules are sitting in the database? What? Why? Now I can understand putting integrity checks in stored procedures, nice fast queries in stored procedures. That I get. That's okay. But business rules?
Business rules don't belong in the database. If you put your business rules in the database, you know who that makes happy? The database vendor. Because he knows he's got you then. Just try to change. Just try because I've got your business rules.
This principle goes towards architecture. It is a fundamental element of software architecture.
The goal is to separate things that change for different reasons and group together things that change for the same reason. And we do that so that our systems can be robust against change. When some guy out there asks us to change our system,
we can say to him, sure. And be perfectly clear that nothing else in the system will be affected. There are several symptoms of bad design. One such symptom is called rigidity. Rigidity is the tendency of a system to resist change
by forcing you to make a change in multiple places. Now, if a system is very well designed, and a customer comes to you and says, I'd like to change that report to do something else.
How many modules should change? The customer comes to you and says, I want to change the menu and how it works.
I'd like to change the page structure, the navigation structure of the system. How many subsystems should change? The view. You'll have to change the view.
But should you have to change any business rules? No. Should you have to change any database? No. Any logic inside the core? No. Just the navigation structure. Anybody seen business rules that were tied to the navigation structure? Because the programmers knew that certain things would be entered on that page,
and certain other things would be entered on this page, so that by the time we got to that page, we would know we had those things. And if we changed the navigation structure, those assumptions wouldn't be true anymore. The business rules get tangled with the navigation structure of the system.
That is a violation of the single responsibility principle. We have coupled the business rules to the navigation structure of the system. That makes a system rigid. A system that is rigid forces you to change it in many places when you only want to change it in one.
Other things that make things rigid. Lots of coupling. Has anybody had the experience where you need to make a change? You touch a source file, and you think, oh, okay, I can make the change right there, just in that source file. And you save it, you compile it, and then you think, wait, there's this other file over here that uses this one.
And you bring that one up, and you realize, yep, I've got to make a change over here too, because it calls that one. And you make a change, and then you realize, wait, there's these files over here that use that one, and you start following this chain of dependencies back through the code. Weeks go by.
Eventually you've touched every module in the system. You're still not done. That is a rigid system. A system that forces you to work backwards through a chain of dependencies and causes you to modify far too much.
A system can get rigid because of the single responsibility principle being violated. But worse than rigidity is fragility. Fragility occurs when you touch a module, and everything appears to work, except that something completely different
in a completely different part of the system breaks. How can that happen? By the way, who's seen this happen? Yes. It happens because of couplings, long-distance couplings. A module happens to serve two responsibilities, but they're very separate responsibilities.
So you make a change to the module, and you make the change. It certainly changes the responsibility you thought it would, but there's some funky thing that happened, and you change that module, something else got changed. How can that happen? Imagine that I have a module. This module serves a responsibility over there.
Let's call them the accountants. And there's another group of people over there, roles. Let's call them the clerks. Now it turns out that there's a function in this module that serves the accountants that is identical to a function in a module that serves the clerks.
They have the identical implementation. Perhaps they compute a total. And the programmer looks at that and says, why, that's duplicated code. I'm supposed to get rid of duplicated code. So he takes the function in this module,
and he says it's the same as this one, and he causes the clerk code to call the accountant module. And now he's eliminated the duplicate code. He thinks he's done a good thing. But then later on there's a change, a change to the business rules. The total needs to be altered. But only the accountants want the total altered.
The clerks do not. The clerks want to use the old way. And you go and you say, oh, the accountants want the total changed, and you go to the module and you say, oh, there's the function that calculates that. I'll change it. And you generate the reports for the accountants, and they're perfectly happy. And you've forgotten that you had also bound this to the clerks.
And the next time there's a production run, the clerks all go, what the heck happened here? All our totals are wrong. Whoa. That was a violation of the single responsibility principle. Should the developer have eliminated that duplicate code?
We talk a lot about, oh, we've got to get rid of all duplicated code. Duplicated code is evil. Should the developer have gotten rid of that duplicate code? Answer, no. It wasn't duplicate. The fact that they looked the same, the fact that they had the identical implementation, was an accident.
They served separate masters, so they can't be duplicate. A function that serves one master cannot be the duplicate of a function that serves another master because they can change for different reasons. So violating the single responsibility principle can cause systems to be fragile, cause them to break in bizarre and strange ways
that make all the managers go, holy crap, these guys don't know what the hell they're doing.
Let's imagine an application. Who's ever played Mastermind? Lots of you have played Mastermind. Now, those of you who have not played Mastermind, this is a game where you have colored pegs. There are two players, the code maker and the code breaker.
The code maker takes four colored pegs and secretly puts them behind a little barrier in a certain order. The code breaker then takes turns. He lays out four pegs in what he thinks is the code that's behind the screen. He lays them out in a certain order. And then the code maker scores that guess.
The code breaker looks at the score and determines what next code to guess. He tries to guess a code, and then the code breaker scores that. And I'll describe the scoring in just a minute. And you repeat this until the code breaker breaks the code.
The scoring is very simple. In the game of Mastermind, they use white and black pegs for this. So you get a white peg. The code breaker gets a white peg. If he chooses a color that is in the code, but that color is in the wrong position,
he gets a white peg for that. And he'll get a white peg for every color that is in the wrong position. He gets a black peg for every color that's in the right position. And he gets no peg at all if he's chosen the wrong color. The code breaker then must analyze those clues and create a guess.
And the goal is to guess the code in the minimum number of turns. Now we're going to write an application. We're going to imagine an application. This application plays Mastermind. It plays the code maker side. So the computer will make the code.
And the player will guess the code. What are the responsibilities? Okay, we've got the scoring. Scoring is a responsibility. Okay, that's good.
And we need to know how to do that scoring in an interesting way. So what would we call the role that knew how to score the code? The scorer. He's the scorer. Now is there a person that that ties to?
Yes, there is a person that that ties to. It's the guy who's designed the algorithm. The guy who specifies the scoring algorithm. He's the one who's going to make changes to that code. If you want to know who's the responsible party you figure out who's going to make changes to that code.
There's another responsibility. How is this game played? Is it played on a console? Textually? Is it played on a browser? Someone has to define the user interface.
We'll call that person the designer. That's the new term, by the way. Design now means something very different than it used to mean. Design used to mean software design. Now it means web design. So we'll call this person the designer. The designer is the one who's going to figure out what commands get entered, what buttons get clicked,
what appears on the screen. This has nothing to do with the rules of the game. Nothing directly to do with the rules of the game. It's all cosmetics. And there should be a batch of code that serves that responsibility. There's another responsibility. The way the game is played, the order that things occur in,
the logistics of making the code, guess, score, guess, score, guess, score, win. We'll call that the game master. And that's a different role that some person plays.
The person who decides on the scoring algorithm is different, or at least the role is different, from the person who designs the screen layout, from the person who designs the flow of the game. Who, for example, in this batch of three responsibilities, who would decide that we should lay bets?
Which of these responsibilities would be the one to say, we should lay bets on the next guess? Would it be the designer? No. Designer doesn't have any input on stuff like that. Would it be the code, the decoder,
the guy who does the scoring, the scorer? No, he doesn't care about that. This is the game master. That's the guy who's going to do that. So if we had to add that kind of feature, we would go to the module that serves the game master and put that kind of code in. Oh, we'd have to go to the designer and tell the designer, too, because he'd have to come up with some interface for it.
So there'd be two modules that got adjusted there. Perhaps we have multiple interfaces. Maybe we've got a console interface and a web interface. Oh, and we've got two designers. Getting it separated like that means that when we change the design,
the rules of the game don't change. If we have multiple user interfaces, when we change one, the others don't change. If we change the scoring mechanism, nothing else changes. We manage to separate things nicely. Getting that separation into your code is hard,
because when we get code working, when we're working on our code, we finally get it working, even if we're using test-driven development. We finally get it working with all the test passing, and then you take a step back and you look at this code, and it's all tangled up. The responsibilities have been merged.
That is the moment we do refactoring. When do you do refactoring? Always. Always. Do you ever put refactoring on a schedule? No. There is never a refactoring line item on the schedule, never on any part of any schedule does the word refactoring appear.
This would be like putting, washing your hands after going to the bathroom into a schedule. Refactoring is just something you do all of the time. But here we get to the end of a program like Mastermind, and we've been refactoring,
and we've been cleaning and refactoring and cleaning, and then we've got it all working, and we take a step back. And we look at the separations. Have we separated the responsibilities properly? Generally we will not. Generally we should then refactor and decouple and refactor and decouple to isolate those responsibilities in a very nice way.
It's a final step in any kind of development of a feature or a module. The last operation separates into very clear responsibilities. It's easy to say, well, I've got it all working and the code is nice and clean. True. But is it well separated?
Thank you for your attention. I appreciate you all coming this evening. Go get some beer. I'll see you another time. Put your cards in the thing.
Just the green ones, please.