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

SOLID Deconstruction

00:00

Formal Metadata

Title
SOLID Deconstruction
Title of Series
Number of Parts
133
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
The SOLID principles are often presented as being core to good code design practice. Each of S, O, L, I and D do not, however, necessarily mean what programmers expect they mean or are taught. By understanding this range of beliefs we can learn more about practices for objects, components and interfaces than just S, O, L, I and D. This talk reviews the SOLID principles and reveals contradictions and different interpretations. It is through paradoxes and surprises we often gain insights. We will leave SOLID somewhat more fluid, but having learnt from them more than expected.
Software developerArchitectureSoftwarePattern languageSingle-precision floating-point formatInversion (music)Open setClosed setInterface (computing)ChainPropositional formulaPhysical systemBasis <Mathematik>Virtual machineRegular graphSequenceTouchscreenGoodness of fitCartesian coordinate systemWordInternetworkingCodePattern languageSolid geometryPhysical lawInheritance (object-oriented programming)Physical systemUniqueness quantificationForm (programming)Regular graphSequenceSingle-precision floating-point formatOpen setVolume (thermodynamics)Electronic mailing listProgrammer (hardware)Social classBit rateOrder (biology)Constructor (object-oriented programming)Basis <Mathematik>Green's functionMathematicsInversion (music)Point (geometry)Interface (computing)Euler anglesRight angleMultiplication signComputer architectureExpert systemScaling (geometry)TheoremGravitationContext awarenessPrincipal idealBitCASE <Informatik>Virtual machineTheoryPredictabilityMessage passingSlide ruleCycle (graph theory)TwitterPlanningComputer programmingSubstitute goodDependent and independent variablesInformationRevision controlCondition numberJSONXMLUMLComputer animation
Pattern languageSequenceRegular graphProteinSingle-precision floating-point formatSoftware developerState of matterService (economics)Computer programmingWikiTerm (mathematics)System programmingCohesion (computer science)Mathematical analysisStructured programmingPhysical systemData structureSpacetimeProof theorySign (mathematics)Module (mathematics)Data integrityModul <Datentyp>Statement (computer science)Associative propertyElement (mathematics)BlogCharacteristic polynomialSingle-precision floating-point formatRevision controlLatent heatJava appletVirtual machineAdhesionMoment (mathematics)Social classCohesion (computer science)Punched cardContrast (vision)Line (geometry)Food energyWordOffice suiteService (economics)MeasurementQuicksortMathematical analysisExecution unitConnectivity (graph theory)CASE <Informatik>Physical systemContext awarenessPrincipal idealDependent and independent variablesForm (programming)BlogFitness functionProgramming paradigmBookmark (World Wide Web)AbstractionElement (mathematics)Multiplication signEndliche ModelltheorieDifferent (Kate Ryan album)Sound effectSoftware developerSolid geometryComputer programmingDirected graphSoftwarePhysicalismForcing (mathematics)Odds ratioPerspective (visual)WritingState of matterParallel portRoutingParameter (computer programming)Entire functionObject (grammar)Disk read-and-write headStatement (computer science)Data conversionCharacteristic polynomialBitTerm (mathematics)Structural analysisFigurate numberSoftware design patternModule (mathematics)IdentifiabilityLecture/Conference
Characteristic polynomialCohesion (computer science)Java appletSocial classNetwork topologyPrice indexFrame problemData modelEvent horizonSoftware frameworkUtility softwareComputing platformFunction (mathematics)Data dictionaryObject (grammar)Software developerLambda calculusLoop (music)ParsingElectronic mailing listAreaEquals signData centerInfinite conjugacy class propertyPoint (geometry)QuicksortFundamental theorem of algebraLevel (video gaming)Dependent and independent variablesStandard deviationCASE <Informatik>Library (computing)Interpreter (computing)CollaborationismPerfect groupCounterexampleUtility softwareObject (grammar)Line (geometry)Enterprise architectureData managementGame theoryEndliche ModelltheorieGame controllerSoftwarePlastikkarteNumberComputer fontLogicComputer scienceAdhesionTheory of relativitySound effectSocial classWordSubject indexingCodeSingle-precision floating-point formatKeyboard shortcutTerm (mathematics)Multiplication signState of matterLaptopBookmark (World Wide Web)Power (physics)State observerAbstractionJava appletEvent horizonEmailRevision controlFormal languageDivisorAreaFunctional (mathematics)Inclusion mapProgrammer (hardware)Computer fileExistential quantificationCohesion (computer science)Code refactoringWritingSoftware frameworkFile viewerDecision tree learningGodElectronic mailing listInterrupt <Informatik>RepetitionLetterpress printingView (database)Search algorithmGoodness of fitDisk read-and-write headComputer programmingComputer architectureShared memoryInternationalization and localizationCellular automatonOperational amplifierPhysical systemAddress spaceTheoryComputer animation
Function (mathematics)Module (mathematics)Formal grammarSoftware developerDependent and independent variablesWordVenn diagramMathematicsObject (grammar)Goodness of fitCASE <Informatik>Axiom of choiceSingle-precision floating-point formatNumberMultiplicationBit rateModule (mathematics)Form (programming)Cohesion (computer science)DiagramPerspective (visual)Social classFunctional (mathematics)Spherical capRepresentation (politics)Mathematical singularityImplementationOvalData conversionPrincipal idealValue-added networkComputer animation
Interface (computing)Software developerInheritance (object-oriented programming)Client (computing)CodeService (economics)Partition (number theory)Physical systemFunction (mathematics)Interface (computing)Characteristic polynomialCohesion (computer science)Reading (process)Line (geometry)Interface (computing)Object (grammar)CodeInterface (computing)Right anglePhysical systemNormal (geometry)Formal languageService (economics)Characteristic polynomialBitData managementFlow separationNumberGoodness of fitQuicksortParameter (computer programming)AbstractionLine (geometry)Inheritance (object-oriented programming)Field (computer science)Client (computing)Functional (mathematics)Type theoryPerspective (visual)Execution unitMathematicsRelational databaseDifferent (Kate Ryan album)Pattern languagePoint (geometry)Parallel portCentralizer and normalizerSystem administratorElectronic mailing listKey (cryptography)Universe (mathematics)WordDivisorPrincipal idealDependent and independent variablesInformationDatabasePartition (number theory)Cohesion (computer science)Computer animation
Software developerRevision controlModule (mathematics)Client (computing)Modul <Datentyp>Service (economics)Data structureView (database)ArchitectureSoftwareSystem programmingIndependence (probability theory)Inversion (music)Computer programmingImplementationTheoryProgrammierstilSubstitute goodTerm (mathematics)Positional notationCalculusObject (grammar)Encapsulation (object-oriented programming)Order (biology)Formal languageMechanism designForm (programming)Military baseAbstract data typeLogical constantRevision controlGame controllerInstance (computer science)Control flowStructural loadBitWindowAttribute grammarState observerPoint (geometry)MathematicsRootPolymorphism (materials science)Design by contractAbstractionNormal (geometry)Exception handlingPosition operatorWordChromosomal crossoverHierarchyReverse engineeringLattice (order)Different (Kate Ryan album)PlanningBit rateMultiplication signLibrary (computing)Group actionProjective planeType theoryCausalityState of matterStability theoryMixed realityLevel (video gaming)String (computer science)InjektivitätLatent heatComputer fileSoftware testingData miningParameter (computer programming)Disk read-and-write headGreatest elementCASE <Informatik>Category of beingConfiguration spaceElectronic signatureArithmetic meanComputer-assisted translationComputer programmingView (database)Software frameworkObject-oriented programmingInterface (computing)Pointer (computer programming)CodeNumberAdditionMoment (mathematics)Software bugExecution unitIdeal (ethics)WritingService (economics)Transformation (genetics)Set (mathematics)Fitness functionProper mapLine (geometry)Code refactoringSoftware developerWeightCoefficient of determinationGoodness of fitInheritance (object-oriented programming)FluidData structureModule (mathematics)Boundary value problemClient (computing)Real numberClassical physicsPlug-in (computing)Dependent and independent variablesCartesian coordinate systemPower (physics)Open setVideo gameSocial classBlogExtension (kinesiology)RhombusMereologyTraffic reportingPrisoner's dilemmaPhysical systemTelephone number mappingComputer architectureStatement (computer science)RoutingTask (computing)Functional (mathematics)Electronic mailing listImplementationLimit (category theory)Perspective (visual)Algebraic closureConnectivity (graph theory)CircleMultiplicationSheaf (mathematics)SoftwareData managementConstructor (object-oriented programming)Element (mathematics)Square numberInterface (computing)Shape (magazine)IterationTouchscreenStructured programmingInversion (music)Single-precision floating-point formatIndependence (probability theory)Context awarenessEvent horizonEndliche ModelltheorieClosed setFactory (trading post)Principal idealRegular graphSubject indexingMessage passingCountingNP-hardRight angleCalculationRepresentation (politics)Fluid staticsBookmark (World Wide Web)Run time (program lifecycle phase)Computer clusterGame theorySystem callClass diagramMatching (graph theory)Dynamical systemEvoluteSource codeExpected valueWebsiteProcess (computing)BackupFunctional programmingReading (process)Block (periodic table)File viewerDivisorFlow separationBuildingHill differential equationSinc functionSelf-organizationGodFrequencyFigurate numberStress (mechanics)Computer animation
Transcript: English(auto-generated)
Good. Always good to start the day on a blue screen. Things I'm supposed to remind you about. Simple rating system at the end of the talk. It won't involve blue. It'll involve red, amber, and green.
Green is good. If you struggle, if you're slightly color blind like me, ask a colleague to put a green one in for you. OK, so it turns out, well, with any luck, this is the right talk. I've just had to check which one I'm doing first. Is this the right talk? Are you here for this one?
Good. Solid deconstruction. First question, I guess, is who's come across the solid principles before, either by name or in depth? Good. Who has not? That was the wrong question I asked the first time. OK, good. Who thinks they know something about them?
Something I'm being really liberal with this, OK? Like more than zero, OK? Not are you an expert in this stuff. OK, fine. So what I want to do here is actually go through the solid principles, assuming that you have some prior knowledge of them, and kind of, well,
take them apart. Because they're not quite right. In some cases, we find that there are actually two ideas rolled into one, which is ironic given that one of them is actually SRP. And in other cases, so that kind of bumps up. You get two for one, which is great.
On the other hand, in other cases, we find that one is subordinate to another. So it's not really a first class practice. And then in one case, we find it's not even a practice at all, which is going to upset a few people, because I do hear this being used at companies a lot. I'd actually mentioned it last year, but I've got more detail on it this year.
OK, so my parents were generous enough to give me an internet unique name. So if you haven't had kids yet, could I offer that as advice? And I did this for both my children. I did actually Google their names before we gave them their names and didn't get any hits. And it's like, right, that's it. And my older boy recently, whilst his friends were busy trying to figure out what to call themselves
on Instagram and Twitter, I said, why don't you just try your name? And he said, oh, my god, it's actually available. I said, yeah, yeah, that paid off 14 years ago. So it's worth it, OK? Anyway, I'm fairly easy to spam and stalk.
Are these involved? Yes, this is relevant, actually. These two books I co-authored a few years ago, Pattern-Oriented Software Architecture, volumes four and five. I'm not going to go into depth on these, but the pattern stuff is relevant, as indeed is this book, 97 Things Every Programmer Should
Know. There's a couple of points in here that will come out in the talk. But let's talk about solid. So it was originally salty, but then Michael Feathers
realized it could be rearranged and told Uncle Bob this. And so we end up with single responsibility, open close, list cost substitution, interface segregation, dependency inversion. Now, I'm not going to tackle them in this particular order because there are a few things that
need to come before other things to help us. But my first minor gripe, it's a relatively minor gripe, is that we do overuse the word principle a lot. Are they principles? I'm not entirely sure. If we look at what the dictionary says, a fundamental truth. I'm not really sure that these qualify.
They're good, but they're not fundamental in the way that things like gravity and other things like that are. Foundation for a system of belief, perhaps that's about right. Morally correct behavior and attitudes. While some people would like to think that, and they will do this in code reviews
and try and impose the morally correct use of solid, or rather, the idea that solid is morally correct, I'm going to question that because I don't think most people who claim to know what solid constitutes actually have a solid enough understanding, so to speak. A general scientific theorem or law
that has numerous special applications. Well, I wouldn't really call it that. We're kind of lacking some evidence here. It doesn't make any very strong predictive, well, it doesn't have very strong predictive features. A natural law forming the basis for the construction or working of a machine. And we kind of get a bit closer to that,
but accept the fact that it's not natural. So what is a suitable word? Well, conveniently enough, there's another one that begins with P. So that means you can still talk about SRP and be good. A pattern. A pattern is a context-specific piece of advice. If you change the context, the applicability changes. One of the examples that I often use when talking about this
is I will tell people, OK, this is how I teach my kids or taught my kids how to cross the road. I always tell them to approach the road, stop, look right, look left, look right. And depending on where I am, a lot of people will suddenly say, ha, I see what you mean.
Because that's really bad advice if you happen to be in Germany, for example, which is where I am for the rest of the week. So it turns out that the advice is not intrinsically bad. It is contextually good. Change the context, it turns out to be bad advice. This is applicable to the scale of architecture and in the detail of code.
And it turns out that these are conditional on context. And it also turns out that we can mess about with the idea of the word pattern a bit, dig into the dictionary again, a regular form or sequence to serve them in the way in which something happens or is done. Well, yes, actually that is what we're after when we talk about things like solid and other aspects.
An example for others to follow. Well, yeah, we're not saying anything about moral correctness. But this is certainly the idea behind them. And then the more design patterns you want a particular recurring design problem that arises in specific design context and presents a well-known proven solution for the problem. OK, so that's the kind of vocabulary bit out of the way.
Or is it? It turns out words matter. And I'm going to say on the whole in software development we use an awful lot of words. But we're fairly rubbish at using them. We name things very, very poorly. I'm not just talking about identifiers. I'm talking about paradigms. I'm talking about architectural concepts.
We often use the wrong words. Why is this relevant? Well, we're going to see in a moment. So single responsibility. If we go to the sink of all human knowledge, Wikipedia, and see what it has to say on this, it observes that in our programming, SRP, single responsibility principle states
that every object should have a single responsibility. And that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility. This sounds like something that is entirely reasonable. It's very, very difficult to argue with that. You nod your head. You go, yeah, this is good. Why would I want it any other way?
The term was introduced by Robert Martin. Martin described it as being based on the principle of cohesion, as described by Tom DeMarco in his book, Structured Analysis and System Specification, which it turns out I have a copy of because I like old books. It's not just that they smell nice, but there's something about them. And I managed to pick up a copy of this. Having read it many years ago,
I managed to find it in Powell's in Portland, Oregon. And so if we dig into it, we find he has a few things to say on things like cohesion. And he's also, Tom DeMarco doesn't pull any punches. His vocabulary is quite excellent in places.
We can see, just look at that. Figure 101 is an abominable design. He's not just sort of saying it's a poor design. It's not the best design. It's abominable. It's a great word. People don't use it enough. But let's see what we're saying about cohesion. It's a measure of the strength of association of the elements inside a module. Whatever our modular construct is,
whether we use the concept of module to refer to a class, whether we use it to relate to a concept of a file, a package, a component, whatever it is, whatever modular construct binding unit that we have available to us, the strength of association of the elements within that. A highly cohesive module is a collection of statements and data items that should be treated as a whole
because they are so closely related. Any attempt to divide them up would only result in increased coupling and decreased readability. Now, this is great. This is the stuff that people normally talk about, cohesion, coupling, and so on. But there is something else that Uncle Bob added to it. He added a number, and he added another word. He said responsibility, and he said there shall be only one of them, okay?
It turns out that this is not what Tom DeMarco was saying, okay? Although we can see they are obviously related, it is not Tom DeMarco's principle or approach to cohesion. He was actually saying something slightly different. It turns out there are many forms of cohesion. And cohesion is one of those things
that people struggle with. People often have a good idea and a good intuitive understanding of the other one, coupling. In fact, coupling is a really good example of one of the abstract ideas in software that you can actually feel and will make a difference to your day-to-day work.
You increase the coupling. It increases the build time, okay? You increase the coupling, and you can see the ripple effect as everything is pulled out of the version control. In other words, it's something that is tangible. You can actually measure the energy associated with coupling, okay? It has a physical impact,
whether it's you looking at your watch or whether it's the activity of the network. Whatever it is, coupling means something very, very physical to people. Cohesion seems a little more abstract and intellectual. But Glenn Vanderburg, in a blog a few years back, which I recently discovered has gone offline,
but thanks to the wayback machine, you can find it, I had a conversation with Glenn. He directed me. I mentioned that I found that a lot of people struggle with the concept of cohesion, and he said, well, conveniently enough, I actually wrote a blog on it. He'd written one just six months before. It was 2011. And he looked at it from a linguistic perspective,
drawing a parallel with the word coherent. These two words, coherent, cohesive, they follow the same route. We refer to a sound line of reasoning, for example, as coherent. You describe somebody else's thinking or their argument or their reasoning as coherent.
The thoughts fit. They go together. They relate to each other. And this is exactly the characteristic of a class that makes it coherent. The pieces all seem to be related. They seem to belong together. And it would feel somewhat unnatural to pull them apart. Such a class exhibits cohesion. Glenn also draws a parallel with another word
or a contrast with another word. If you have the idea of cohesion, what about the idea of adhesion? Cohesive, adhesive. What's an adhesive? Sticky, glue. When you use adhesive, you are putting things together
that probably don't belong together. So you now have better vocabulary. You can go back into the office later this week and instead of merely describing somebody's class as uncohesive, if it seems to be just a ragtag pile of stuff, you can say, ooh, it's pretty adhesive, this class. They'll look at you blankly for a moment and then they don't want to admit
they haven't got a clue what you're talking about and they'll just let it slide. But you'll have one up on them there. So we have a very strong concept here. Okay, so what are examples that don't fit this? My favorite one, and trust me, it doesn't get better in each successive version of Java, and this is not news.
I mean, certainly I was saying this 20 years ago. Java.util. What's in it? The collections framework. The legacy collection classes. Okay, I can kind of see that those could go together. The event model, which has, let me think, nothing to do with the collections at all, ever.
Date and time facilities, which don't really have anything to do with the aforementioned collections or events. Internationalization, because it doesn't really deserve a package on its own. If you live in California, the rest of the world, that's just a util, really. And if this is not already miscellaneous enough for you,
there's also miscellaneous utility classes. So it's a miscellany that contains a miscellany. Fantastic. And yeah, this was just doomed to failure from the start. My personal experience on this is pretty much anything that is named util, util's utility
is not a thing you want to leave into a system. It's another way of saying to do, rename me please. Divide me, refactor me, do something, because this is not good. And this is not a language specific thing. That my first experience was of a util's dot lib in a Fortran library in the 1980s.
And it's just gone downhill from there. So anything called util, we can pretty much guarantee is fairly useless in terms of its cohesion. It generally has nothing that binds the things together. They don't naturally belong together. In fact, if we dig out dictionary definition, utility, the state of being useful.
Well, okay, I'm gonna make a simple observation. Software is utilitarian. So therefore, in theory, my whole code base should go in util. Anything that doesn't belong in there goes in the art package, and we can just simply delete it, okay? Software is, by definition, tool-driven. It is about utilities.
Useful, especially through having several functions. Ironically enough, that does appear to be the case. And functional rather than attractive. Yeah, it's pretty ugly. However, don't fall into the trap of thinking that they must be called util for them to fall into this sort of adhesive trap.
Because I visited a company a few years ago. I made this point, and they said, oh, we've kind of addressed that problem. And I said, oh, how did you address it? And they said, we renamed it library. I said, ah, yeah, that's just sort of relabeling. You've actually still got the same problem. So this is clearly an example
of something that violates our fundamental understanding cohesion, and certainly doesn't fit the idea of what we would consider to be a single responsibility. We see it, as I say, with things that are not named like that. In the C library, there's a perfect example that's been there for decades,
or perfect counterexample. Standard lib.h, C89 has 18 standard headers. This one claims to be the library. I don't know what the other 17 are doing. Or rather, I do, because there is the interesting question. I do this even with experienced C programmers. Sometimes I ask them, which header file is a certain function in? And they struggle.
Because the criteria for inclusion is not constructive. What do I mean by that? The only way you can figure out if something's in standard lib is if it's not somewhere else. Okay, that's often the criteria for any util or any standard lib. If you can't find it anywhere else, it's there. Well, that's a really bad searching algorithm,
and it's not a very habitable way of approaching your code. So we see these counterexamples. So all is good. We understand single responsibility. We have the idea of responsibility. Actually, we don't, because we never actually said what a responsibility is. Responsibility is a word that is evocative.
It's very suggestive. But I don't know how to measure it. And it turns out that this party line of single responsibility is relatively new. Grady-Booch, Object Solutions, published at around the same time
that Uncle Bob originally wrote his engineering notebooks that made up, ultimately contributed to Solid. 1995. Now, Grady-Booch, he knows a few things about OO. He's got OO in his name. I mean, come on.
So he's been doing this stuff for a while, and he was very influential in my adoption and learning of abstraction techniques, OO, software architecture, and so on, dating back to the 80s. And he made this comment, and this was very typical of the era,
late 80s, early 80s, early 90s. Every class should embody only about three to five distinct responsibilities. There was a technique, a simple modeling technique, that, if you've ever wondered, where did the agile people get the idea for using index cards from?
It turns out that this is actually, there we go, the tool that annoyed a lot of software tool vendors, the index card, turns out surprisingly cheap and highly available. And you can put any font on it that you have available.
It's great. There was a technique that initially popularized the use of these, CRC cards. Okay, you divide a card into three, and this would represent an object, effectively. You'd look at it from the point of view, or a class at that level. You'd have the class name, the responsibilities, plural, of that class,
and the collaborators. The other classes whose objects that it would participate with to achieve its ends. So it's always very interaction-driven. The idea of objects forming a network through which the behavior is distributed is very key. But the breakdown of responsibilities,
and that's the point, it was plural. The idea is clearly you didn't want to have too many, that's why you use an index card. Because unless you have tiny, tiny writing, you're only gonna get about, oh, I don't know, about three to five on. Okay, they have to be related. But the idea of responsibility is not formally defined in computer science or indeed anywhere else. And we often run foul of it in other cases.
This is an extract from, so it's not just, so the Wikipedia entry, as indeed many people who talk about solid is wrong on one thing. This is not an object-oriented principle, and actually Uncle Bob doesn't claim it as such. He talks about it as a general software design principle.
And here we've got an extract from Peter Norvig's nice little Lisp interpreter. It's about 90 lines of Python. Just for reference, he tried implementing it in Java with the same functionality, and I think it hit about 1,400 lines. So that's why people don't use Python
for enterprise programming, because you can't get the numbers up, okay? It's quite important here. And at the heart of any interpreted language and any Lisp system, you'll find the REPL. There it is, REPL, which normally stands for read, eval, print,
but he's actually even added the fact that it does a prompt as well, so it's a prep, a REPL. So what is the single responsibility of a REPL? Look at its name. It's got at least three, okay? And now, this is where you start playing word games.
Oh, yes, well, it interprets. Well, yeah, that's not a bad word, but that kind of, you need to, you've just now wandered off into the area of word games. Sometimes our words help us with precision. They allow us to uncover and reveal the concept that is just right for what we need. But sometimes we end up playing word games instead.
We see this in a lot of code bases, manager. What does it do? It manages stuff, or controller. It controls things. Well, you've just described kind of most programming, yeah? Logic thing, I haven't seen that yet in code, but it wouldn't surprise me
if somebody comes up with that. The point here is that this clearly, what this does is it actually takes a number of responsibilities. Its responsibility is to bind together these functions. So you could say at that level, I found my one responsibility, but ultimately you can do that for anything, okay? So although I tend to use the term SRP in this case,
because people, it's very suggestive, as I said earlier. It has power in its sort of simplicity, but actually when you push it really hard, you discover that it doesn't quite work at that level. But there's a deeper irony, is actually this is not at all what Uncle Bob was talking about.
So he talked about SRP in a number of places, including 97 things. One of the most foundational principles of good design is gather together those things that change for the same reason, and separate those things that change for different reasons.
This principle is often known as the single responsibility principle, or SRP. In short, it says that a subsystem module class, or even a function, should not have more than one reason to change. Now notice, this is not the same as the stuff we've been discussing. Oh, sure, there's a big overlap. It's a huge, great Venn diagram overlap
where we can say what we generally understand to mean by responsibility, and reason for change, there's a very big overlap. There's a number of designs that would fit both criteria and both perspectives, but single reason for change is not the same as the idea of a responsibility, okay?
Particularly when you're dealing with objects that deal with encapsulated representation, which I hope you do, if you change something about that, and you change their implementation, well, you've now got multiple reasons for changing. I've got the choice of implementation, but I've also got the external responsibilities that I might extend the class with and modify.
So there is a case there, you have to be very, very careful with these things. This is a useful criterion. It is a form of cohesion, but there are multiple forms of cohesion. Rate of change or reason for change is one of them. So here's, that's a, basically we got two for one with this one, but we've also discovered the word responsibility is quite loose.
We can kind of use it in conversation, but if you try and push it too hard, you may find that it all falls apart. So let's skip to interface segregation. Now, interface segregation, what does it mean? Well, separate out your calling interfaces, the interfaces you're going to call into.
According to what? Well, the kind of relatively defunct Corva services actually did have some interesting design principles. It had a really good design document, and unfortunately the Corva services didn't really follow. And they talked about interface inheritance, subtyping. Interface inheritance is used whenever one can imagine
that client code should depend on less functionality than the full interfaces. Services are often partitioned as a several unrelated interface when it is possible to partition the clients into different roles. This name also went, so in the 1990s, this was also referred to as role decoupling.
That's a pattern name that came from the catalysis method. So you would decouple according to roles. So you have a single object that might be used in two different ways from two different perspectives. Okay, you might have an object there that one piece of code is responsible for registering handlers, and another piece of code is responsible for pumping in the main data
that will trigger those handlers. They've got two different perspectives on the same object. And those are two different interfaces. They don't have to both see the whole interface. They can narrow it down. For example, an administrative interface is often unrelated and distinct in the type system from the interface used by normal clients. That's a sort of fairly classic example and related to the one that I've just mentioned.
If you look at this closely, you may be familiar with, you may be able to draw a parallel with the idea of normalization. Now, a lot of people think that normalization is something that is specific to databases, relational databases. It's not. Normalization is basically a dependency management technique.
You can apply it to a number of things. You can do it to argument lists, to discover hidden abstractions and objects, and you can do it to interfaces. And just as people often say that the idea is that a field should depend on the key, the whole key, and nothing but the key,
you can say it like this. The dependency should be on the interface, the whole interface, and nothing but the interface. And there's this idea that everything that you wanna use there is there. So if you stumble across an interface that presents itself as 100 methods, it is very, very unlikely that in this universe, or the one next door, that that even begins to make sense as a single, coherent interface.
That's interesting. This sounds really familiar. Sounds hauntingly familiar. It sounds like, oh yeah. So if we just change a couple of words, this is exactly the characteristic of an interface that makes it coherent.
The pieces all seem to be related. They seem to belong together, and it would feel somewhat unnatural to pull them apart. Such an interface exhibits cohesion. In other words, if you've already applied SRP, you have nothing to do, because SRP applies to all the units. There is no interface segregation principle,
because you've already factored things out. You've already factored out the reasons for change. You can see it in this very simple example. If I've got something that's line-oriented IO, and I can choose to read or write, the chances are I probably want to read or write. You have a line reader, and you have a line writer. But there's also an interesting,
so there's a point here that simply SRP applied to interfaces is interface segregation. It's not a separate principle at all. But there's a language dependency here as well. So this code is Java, and let me show you, let me show you this interface in Ruby. And that's in Python.
And, ah, right. So there is also a language dependency here that is kind of bound up in this. So interface segregation, kind of a bit of a weak, sort of a weak contender. Now, this is kind of another two for one.
Liskov substitution. And the only one named after a person, poor old DiMarco didn't get his name into the SRP, but Barbara Liskov managed to get her name into this one. The term was actually first coined by, to the best of my knowledge, by Jim Copley in a book published 25 years ago this year.
Advanced C++ programming styles and idioms. And it relates to a paper that Barbara Liskov wrote in 1987. And there is a little book, Theory of Objects, which is mostly unreadable.
It introduces a sigma calculus. If you like reading formal notations, this is the book for you. If you don't and you struggle to sleep, this is the book for you. But the first section, I think it's divided into five sections, five or six sections, the first section is actually written in English, okay,
and is actually quite readable. And they've got a number of points that they make. And one of them, they say, right, in a purest view of object-oriented methodology, dynamic dispatch is the only mechanism for taking advantage of attributes that have been forgotten by subsumption. In other words, when you have a subtype,
if I'm looking at an object, I cannot see all of its features necessarily because of the hierarchy, okay? So I can see that I'm using a line reader or I'm seeing I'm using a line writer, but I don't know if it's a file line reader or a file line writer or whatever. And so the only way I can take advantage
of the things that it knows, like how to read from a file, is by calling its methods. And dynamic dispatch is what allows us to get the right method that then takes advantage. I never get my sticky pause on the underlying things, but the idea is that, and this is the interesting thing, that what we're seeing here is polymorphism
is a form of encapsulation. This is news to a lot of people because normally in their head, polymorphism is filed in the same place that inheritance is. Nope, inheritance is down the corridor, okay? Encapsulation and polymorphism are two sides of the same coin. Inheritance, one of its aspects is simply to enable polymorphism
in certain classes of language. The idea is that polymorphism allows you to use an object without knowing its representation, which is what we call encapsulation normally. So there's a very interesting crossover there. The position is often taken on abstraction grounds. No knowledge should be obtainable about objects except by invoking their methods.
In the purest approach, subsumption provides a simple and effective mechanism for hiding private attributes. So that's kind of an interesting observation that people are not always aware of. Most people have the experience of inheritance. If you come to a legacy code base, your typical experience of inheritance is that it's a pain in the backside
and it's highly coupled. But it doesn't have to be that way. It turns out that you can use inheritance mechanisms to encourage polymorphism, i.e. the use of interfaces in statically typed languages. And you can actually reduce the coupling and improve the encapsulation. Your hierarchy is encapsulated, if you like.
Okay, so that's kind of the basic idea there. Right, let's talk about Liskov then. What Barbara Liskov said in her H7 paper was this. A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type, the supertype, plus something extra.
What is wanted here is something like the following substitution property. If each object O1 of type S, you can tell she's an academic, there is an object O2 of type T, such that for all programs P defined in terms of T, the behavior of P is unchanged when O1 is substituted. Okay, great. So we've got this kind of a, you can substitute it. Now, the way people normally explain it is they don't use this text.
Or rather, you see this in a few articles and people will quote this paragraph. They always quote this paragraph. I've quoted this paragraph in articles. They always quote, nobody ever bothers reading it. And they just move on and say, look, here's a class hierarchy, yeah? You wanna structure it so that you move towards generalization at the top and specialization, and therefore there is substitutability
with respect to the more general idea. The more specific you become, you can still substitute it where a general concept is expected, okay? So that all sounds incredibly reasonable. A minor point, by the way, if you do draw, if you do draw your class diagrams,
a revelation I had a few years ago is we've been drawing class diagram, we've been drawing class hierarchies wrong all these years. Most people would draw this one up there. That is wrong. It's visually incorrect. This aligns with these. Let's just say this is an interface. These are concrete classes. That's abstract. This is a layered hierarchy.
Show the layers. Don't put things that are unrelated. Don't mix it just because it's one step from the root. The fact that it's one step from the root is irrelevant, okay? It's the fact that it has something in common with these. These are things you create. This is something you inherit from to fulfill some implementation, and this is a contract you fulfill, for example.
So class hierarchy should be layered, but that's not really the point of this talk. There's a whole load of good stuff that we can say there, but there is a consequence to this. It does mean that you need to be very careful about your relationships, and the normal one that people talk about is the is-a relationship. Now, Barbara Liskov is basically saying
something a little bit stronger. People normally say a derived class. We should be able to say this is an instance of this is an instance of that, yeah? One of these is a that, and then if you read most books, you find out that cats are instances of pets,
and dogs are instances of pets, but cats are not instances of dogs, okay? And apparently this, I don't know, does anybody work in that domain? No, didn't think so. So which is also one of the reasons that people tend to get this one wrong. There's an example that I think unites both academic writing on this topic
and industrial practice, and it is this one. A 3D point inheriting from a 2D point. I've seen this in books teaching you how to do object orientation, and I've read it in academic papers on type theory. It's wrong.
It's just wrong. From an incorrect idea, you can pretty much prove anything. This is just wrong. The irony of this is in the MSDN. So the irony of this is that this is in a piece that tries to teach you how to override the equals methods and gets it completely wrong. You cannot possibly do equals correctly
for a hierarchy that looks like this, okay? This piece of the MSDN has not changed for a long time. Somebody needs to tell the folks at Microsoft, okay? This is just bad practice. Yeah, that never made any difference. Don't bother telling them. Okay.
So yeah, don't do this. A 3D point is not a 2D point. A 2D point is not a 3D point. I can take a 3D point and project it onto a plane. That's called a projection. It's not inheritance. It's a function, okay? It's a function. It's not a language construct that is based on the type system.
It's a very different kind of concept. So whilst I may be able to convert a 2D point into a 3D point with respect to something or convert a 3D point into a 2D point with respect to something, that is a functional transformation and you get to choose the with respect to. It's not a type system transformation.
So yeah, you might say, well, I don't write systems like this. You'd be surprised how many examples can be mapped onto this in terms of code. Now Barbara Liskov was much stronger and stricter on this. And she, if you like, her approach is a proper strict form of ISA
to the point that we can actually implement this in terms of tests rather than thinking of it from a kind of an abstract type theoretic way. What does it mean from a testing point of view? Okay, what does the idea of substitutability mean from a testing perspective? And also, how does it conform
to what we might also refer to as a principle, the principle of least astonishment? Right, so a recently used list, one of my favorite little examples, recently used list has, it's like your recently opened files. It has a list of strings. It holds a list of strings. It holds them in stack order.
The only thing most recently opened is at the head. But it's also got another property in that the elements are unique. In other words, when you reopen a file that has already been, it already exists in the recently used list, it doesn't occur twice. It just gets moved. I'm also going to say that
a null is not a string. Sometimes libraries are a little ambiguous about this. So I'm gonna say that's not allowed. If you're struggling with the idea that null does not represent an empty string, think of this as an empty cup, and this is not a cup. You can tell the difference. There's a big difference between the two, okay?
Don't confuse them. So we've got this basic idea. Okay, so let's write some tests for this. Oh, no, sorry. There's some implementation. We're using a list, and we're wrapping it. It's an object adapter. We're just forwarding a couple of things, doing a null check here, and fine, okay.
Nice and simple. Now, some people will kind of look at this and go, you know what? There's a bit of typing there. I can save myself a bit of typing. I can save myself a bit of typing by using the power of inheritance. There we go. We've made everybody's life easier. Or have we? Because all I've had to do is override add, everything else I get for free,
including, and this is the first thing we need to notice, method spam, okay? Stuff you get from your base class, that's spam. Most of it is spam. You don't just get those two, count and this. You get everything else that you don't want and does not conform to the idea of a recently used list. So you get method spam. But even without the method spam,
let's just focus on the functionality that we intended. The ability to call count, the ability to use an indexer, specifically the ability to use add. So let's write some tests. So we're gonna write a set of tests for a list. And we're gonna initially, so we're gonna initialize it to something.
Here's the three test cases I'm interested in. Addition of non-null item is appended. That's true of an ordinary list, okay? So standard .NET list, I can, when I add an item that's not null, it gets appended. That's what add means. If I take a regular list, adding means append.
Addition of null is permitted. You can add nulls into a regular list. And addition of duplicate items, those are appended as well. And that's the whole point with a list. I can concatenate the same value again and again and again. So what we find is when we pass in a list,
all the tests pass. When we pass in a recently used list that has inherited from list, and therefore in the type system appears to conform to it, and all of its signatures, all of its method signatures appear to conform to it, the problem is each one of those test cases fails. That's what people mean
when they talk about substitutability. It's not a general kind of is-er. It's not to do with pets and cats and all the rest of it. It's a very specific. If I were to substitute this code and use it in the same way with the same expectations, would it still work? If you told me up here in the base class that this method doesn't return a null,
that's a contract that I've got. If you suddenly return null in a derived class, you've broken the contract. It is not Liskov substitutable at that level. So if you're familiar with contracts, Liskov substitution subsumes that. Contracts are a small corner of it. Liskov substitution is the big picture at this level.
Okay, so that's a fairly strict view. But what did Barbara Liskov actually say? She said all of this, which I'm not going to reread. I'm going to highlight this bit. Because the bit is when you reread this paper, this bit's near the beginning.
She says a whole lot of other stuff that's useful. What is wanted here is something like the following substitution property. Does she say what is wanted here is the following substitution property? No, she's offering a suggestion. It's something like this. She doesn't say it must be this. It's not the final word on it.
It's something like this. And then what is the thing that is going to be like? For all programs P defined in terms of T, the behavior of P is unchanged when O1 is substituted. Let me just focus on one word, unchanged. This is a surprise to a lot of people because basically what we're saying
is that if you use inheritance to extend the behavior of your code, then you've just gone and changed it. If I take a feature and I say, I'm gonna have this feature in this runtime, but I'm going to add logging to this feature, but look, it supports the same interface, tough. That doesn't support LSP because you just changed the behavior of program P,
which kind of is gonna upset an awful lot of framework developers. It's just like, here's a framework. What can I do with it? Absolutely nothing. You cannot inherit from it or do anything, or if you inherit from it, just don't do anything at all. Add methods that won't be used.
So this, the people in the know tend to define this as strict LSP, and it does have an applicability. It's also worth pointing out that Barbara Liskov was not writing about object orientation. She was actually writing about abstract data type systems, which are related but not identical. A difference I won't go into here, but there is an important observation there.
This is what we would refer to as strict LSP. The version I just did with the tests is common understanding of LSP. If you like a weaker form of LSP, that's your kind of basic plug compatible. Okay, so let's deal with open-closed. This one is not a principle, so I guess I could skip it,
but where does it come from? A lot of people think it comes from Uncle Bob. No, it doesn't. It comes from Bertram Meyer, who coined this in the late 1980s and introduced it in a book that kind of developed. Yeah, there's some really interesting ideas in this book.
Most of them are wrong. It took me a few years to realize that. He has a really hokey way of doing inheritance that involves multiple inheritance and should send most people screaming. But in it, he introduced a number of principles, and there's some good thinking here, and there's also some slightly odd thinking. But in it, he introduces the open-closed principle
along with a number of other principles. And what he has here is the principle stated that a good module structure should be both open and closed. Closed because clients need the module services to proceed with their own development, and I'll come back to it again in a moment. Open because there's no guarantee we'll include right from the start every service potentially useful to some client.
Okay, let's peel these apart. Let's deal with open and closed separately. A good module structure should be closed because clients need the module services to proceed with their own development. And once they've settled on a version of the module, should not be affected by the introduction of new services they do not need.
Hmm, that's interesting. We're not gonna get very far developing software like this because I think it's perfectly safe to take a piece of code that I've been working on and change it and give it to somebody and change it again. I can change it in a compatible way. If it's a colleague, I don't have to change it in a compatible way. If we haven't published the code, there's no issue. It's not escaped the building, so to speak.
Why is he worrying about this? Well, he's taking a very idealized view of development, and this was written in the 1980s, but he's also solving another problem indirectly, perhaps without stating it so forcefully because as far as I'm concerned, I'm perfectly happy to have different versions of things.
How do you, there's a thing, versions. How do we, I don't know, control them? If only we had some kind of system that could control versions. What was the most popular version control system back in the 1980s?
Backup, yeah. Copy and paste. Yeah, that's pretty much it. Or nothing, yeah. Oh, there were a few VCS systems around, but my question was very specific, which was the most common one? Most common practices. The whole idea of, the ideas of release management
and package management, all these kinds of, no, not yet formed. This was actually addressing a general problem by the language kind of route. But there is a difference here, and it's totally glossed over by the open-closed approach, and it's actually the important one. Open-close distracts people. I don't actually want my module structure
to be closed at all. I want to change it, otherwise I'm not gonna develop things. I wanna be able to refactor things. Yeah, this predates refactoring, okay? And the idea here is that I shouldn't just be stuck with the mistakes that I made in the past. And there are ways that I,
there's pieces of code that I can change easily, and there's pieces of code that I can't. This is not the distinction. This is not a helpful distinction. As a principle, it's definitely not a principle. What's the real distinction we're after? Martin Fowler gave us that distinction. Published interface is a term I used first in refactoring to refer to a class interface that's used outside the code base that it's defined in.
And here we are in refactoring, and what did he say? There is no problem changing a method name if you have access to all the code that calls that method. Even if that method is public, as long as you can reach and change the callers, you can rename the method. In other words, if it's your code and you have no external dependencies, your module structure is open and is open to change.
Do not close it. That is not a principle. It's a bad practice, okay? Premature closure is a terrible thing. You should allow things to nurture them carefully before releasing them into the world, because once you've released it out there, as Josh Block observed, public APIs are like diamonds.
They are forever. It's very difficult to pull it back, particularly when you don't know who's using it. So the idea is that the stuff that's on the inside of the boundary can be as fluid as anything, not solid, as fluid as anything, and on the other side of the boundary, that's where you have to start caring about things like compatibility. So he goes on to say there is a problem
only if the interface has been used by code that you cannot find and change that is beyond your boundary of control and responsibility. When this happens, I say the interface becomes a published interface, a step beyond public. So the distinction between published and public is actually more important. The reason is that with a non-published interface, you can change it, update the calling code since it is all within a single code base.
So this is about team structure. It's about project organization. It's not about language at this level, and we are engaged in something that really doesn't fit the rather naive idea of you close your modules. Okay, so what does he actually mean by the other one?
Good module structure should be open because there's no guarantee we will include right from the very start every service potentially useful to some client. Again, I'm going to use words like version control and refactoring. This is a non-problem. I don't really need to do this once I'm inside when I'm within a particular boundary,
and I don't think there's any problem with updating stuff. Certainly, I'm gonna have to fix bugs with things, so I do want to release that, but the idea here is that when you add things, and I think many of us have used library systems, libraries that have been updated,
and the fundamental components in those libraries have been updated, and our code has remained compatible, and it all is good. So what's the big deal here? Well, what does he mean by open? What he means is in Java speak, I would have done it in C sharp,
but colon in the middle of the screen is not very exciting, so extends actually tells you what he means. What he means is unambiguously when he talks about an open module structure, he means inheritance. He does not mean anything else. So his idea is you just use inheritance to extend stuff. Oh, we missed a few things. Inherit and add them.
Now that, if the multiple inheritance didn't have you running for the hills, then that one should do. That's the kind of the pain that you're going to experience in a legacy code base. So this idea of openness is a recipe for nastiness and technical debt.
And Rob Murray observed well over two decades ago, a myth in the object-oriented design community goes something like this. If you use OO technology, you can take any class someone else wrote and by using it as a base class, we'll find it to do a similar task. We're also cautioned by Josh Block. Design and implement for inheritance or else prohibited.
Being a good base class is actually quite a hard job. It's a lot easier to just prevent people with interfaces and then delegate from within. By now, it should be apparent that designing a class from inheritance places substantial limitations on the class. Again, we see this from the Liskov perspective.
But there's something else here. So let's talk about what Uncle Bob said because I've said all this Bertrand Meyer stuff and some people say, yeah, yeah, that's all fine, but I know OCP through reading Uncle Bob's books and his blogs and it's obviously different. So here's what Uncle Bob said 20 years ago in a copy I've got at home, the C++ report.
Bertrand Meyer gave his guidance as long ago as 1988 when he coined the now famous open-close principle. So it's the same, that's what he's trying to say. To paraphrase him, software entities, classes, modules, functions, et cetera, should be open for extension but closed for modification. And I'm one of the people that's kind of criticized it and Uncle Bob wrote a blog.
Once again, he repeats the Bertrand Meyer revelation and he said, I've heard it said OCP is wrong, unworkable, impractical, and not for real programs with real work to do. Let me think. I've said it's wrong, unworkable, and impractical. I didn't say the last one. Okay, so three out of four.
The rise of plug-in architectures makes it plain that these views are utter nonsense. On the contrary, a strong plug-in architecture is likely to be the most important aspect of future software systems. The first sentence has absolutely nothing to do with the second and third sentence.
We're suggesting here that plug-in architectures are open-closed. Not at all. They're nothing to do with open-close principle. Open-closed means inheritance. It does not mean plugability. That's absolutely fundamentally wrong and you can actually read that in the Bertrand Meyer. Plug-in architectures are very useful but you also don't want every piece of your code to be pluggable.
I'm sure you've encountered that. And it's a nightmare. Oh yes, this is perfectly configurable. Yeah, I've got a function called perform. Okay, and it takes 20 arguments and there's an XML file out there that's also responsible for helping it configure. It does anything you want. Yeah, entirely useless. Totally pluggable.
So let's look at an example. The example based on the one Uncle Bob uses in the 96 paper but switched to C sharp. The good old shape example. We've done the 2D one so let's move on to shapes. And let's have a hierarchy where we've got shapes, we've got squares, we've got circles
and then we've got a method for drawing square and drawing circle. And then we go back and we say right, here's a method to draw all shapes and we're gonna iterate through this and we're gonna do a whole lot of type checking. If the shape, if S is a square, then cast it to square and then draw square. If it's a circle, then cast it to circle, draw the circle.
And the point on this one that Uncle Bob says, this is closed for extension and closed for modification. Yep, that's pretty much right. But if we were to make draw polymorphic and over-rideable, then we end up with this. I now no longer have to do any nasty type checking and we now just call this and we say, yeah, it's great.
And he said, this is open for extension and closed for modification. There's only one problem. It's not open for extension, it's closed for extension. You cannot extend a function, at least not in the languages that we're concerning ourselves with. You don't extend functions, okay? You are not extending the behavior of that.
It's still going to draw shapes. You don't get to decide, you know what, I wanted to do some logging whilst I was doing that. No, you don't get to extend it. You are not extending that function. So it's not open for extension. And this is kind of interesting and it takes us back to this. I remember reading this the first time, I completely missed her.
Functions should be open for extension but closed for modification. Nope, it's classes, classes and inheritance. That's what open-closed is about. It's about nothing else. And we may use words like open and closed in other parts of our code but that's not what OCP is about. So what is OCP about? What's it doing there? We've talked about what it is.
Well, the big discovery is that actually it's not a kind of design principle at all for code. You don't have to do OCP, you can never apply it because it's part of the language. The double requirement to be open and closed looks like a dilemma and classical module structures offer no clue but inheritance solves it. A class is closed since it may be compiled,
stored in a library, baselined and used by client classes but it is also open since any new class may use it as a parent adding new features. You need to understand the context of this book. He's not just teaching his view on object-oriented design in this book, he's also designing a language. This is a language design principle and as a language design principle, we can do that.
If you're using a language that supports inheritance, you're already using the OCP because it's a language design principle, it's not an application code design principle. So he was talking about how to design a language, not how to design a program. That's a very simple misunderstanding but it's kind of propagated into a mythology these days.
So what that leaves us with is, let's go back to this and revisit that. The class hierarchy is open for extension and closed for modification because of the way that I've written the draw square stuff or rather it's closed for modification
because we're gonna say you can't modify the code. It's open for extension. Sure, the calling code is ugly but it's still open for extension. I can go and add a new thing in there unless I seal off the classes. And if I change it to this, then that's still open for extension and closed for modification because it's the hierarchy in the language construct. That's what's going on.
If you want to see what it looks like if you don't make it open for extension, it looks like this. Okay, I seal off the class, I make an enum and then I have a huge great switch statement inside the draw that does the right thing. Okay, and this is closed for extension and closed for modification. In other words, the minute you have inheritance,
you have just done the open bit, okay? That's what Bertrand Meyer was actually saying. So there is a principle we can learn from this but it's none of this at all. It again comes from the refactoring book. Don't publish interfaces prematurely. That's the message, okay?
Keep things kind of as close as possible, very reluctant to release them to the outside world but this is not a class design principle. This is more of a broad system and team level idea. So it's a valuable one but it's not really at the class level where people normally think of their solid principles. Okay, let's close with, oh yes, and importantly, there's a cultural aspect here.
You modify your code ownership policies to smooth refactoring. So dependency inversion, what is that? Well, dependency inversion, I actually have relatively few gripes with except perhaps the name in object oriented programming. Dependency inversion refers to a specific form
of decoupling where conventional dependency relationships established from high level policy setting modules to low level dependency modules are inverted for the purpose of rendering high level modules independent of the low level module implementation details. Okay, high level modules should not depend
on low level modules. They should depend on, both should depend on abstractions. Abstractions should not depend upon details. Details should depend upon abstractions. This is good. This is solid, so to speak but my minor gripe is with the name because it obviously reflects a period of time. Inversion, the action of inverting
or the state of being inverted, reversal of the normal order, and in reverse of normal, yeah, the word normal pops up a lot. 20 years ago, it was perhaps unusual to see as much code with inversion of control or I mean inversion of control and it's a kind of original sense here
where you use callbacks for things. Instead of polling things, you receive callbacks in order to do things, okay? And certainly there was quite a lot of that around as people started getting into window code or did other forms of event handling but that has become one of the dominant ways
of applying OO and therefore dependency inversion is not an inversion of the normal order. It's actually has become the normal order, yeah? So the name is anchored in a particular worldview that comes from structured programming but it's perhaps the term is no longer applicable
but it turns out that there are some interesting things that we can still take away from it and see where it's already, why we've already seen this principle before in a couple of places. So the first thing is when you have some kind of layered structure, what defines layering is that you have code that uses code in layers beneath it.
You don't have code that's down here referring to code elements up here. Sometimes I see this in code bases and we say, oh yeah, our code is layered except for that bit. We have a name for that architecture. It's called not layered. It's not a tricky name but it's just not layered.
Now, we've got two ways of seeing how values flow through the system. If I put all my constants or all my config details down at the bottom, we end up hardwiring everything below. So we kind of see down to the bottom of the code base. Alternatively, I pass things in from above, parameterized from above. This is a crazy idea.
People always wonder, how do I make my code do other things? I need to parameterize my code to do other things. How do we do that? Parameters, the clue is in the name. It's about passing arguments. And people have created some fairly baroque approaches to passing arguments. And some of the dependency injection frameworks are quite extreme on this.
But dependency injection sounds a lot more exciting than passing arguments around using XML. So the name's kind of stuck. But this has nothing to do with dependency injection. This is the general idea. If you do functional programming, you do this all the time as well. If you look at old structured programming books, this is what they were suggesting.
And indeed, a lot of our old books were as well. You pass stuff in. If I want to change your behavior, I give you something. You don't go wandering off to some sodding great singleton. That's the only good singleton you're ever gonna find. Okay? And that's mine and it's empty. I've got another bottle at home and it's full, but that's also mine.
So what we find here is that the rate of change thing is kind of interesting. What we're actually looking at here is that you want to make sure that in a structure like this, that if there's a lot of detail, and this is highly changeable down here,
what you do is you pull out the implementation stuff over there. You depend on the abstraction. So this is kind of pulling out and you pass in the implementation, but we only ever see the abstraction. So basically what we're saying is all the details are subsumed. And we do this for reasons of stability. Now that point about subsumption,
we actually saw and heard about in LSP. The other aspect to do with separating out causes for change, because what we're saying here is this is a stable abstraction and that is the volatile thing. So dependency inversion sounds very, very familiar because what we've done is we've just gone and rediscovered a single reason for change.
In other words, dependency inversion is simply SRP and LSP meeting each other at the altar and having a bit of a marriage. It's not actually a separate concept at all, but it's nice to have a name for it. Okay, so on that note,
that is the end of this talk. I'm gonna be around for the break if you have any questions. Any thoughts? And I'm also doing another talk in about 20 minutes. I hope that's been useful, thought-provoking. Maybe you didn't agree with everything. In fact, probably if you did agree with everything,
you weren't listening. But certainly it should shake up a little bit. And the green things are outside and I didn't get them to remove the red and yellows, but just ignore those. Thank you very much. Thank you very much.