← Lectures
Back

1 / 7

Next

Slide 1 of 7: Four words that do not agree with each other

Arrow keys move through the slides while these controls have focus.

The theatre is not shown, because your system asks for reduced motion. The lecture itself is below, in full.

Lecture · 7 slides

The principles of object orientation

Object orientation is usually taught as a list. Four pillars, then five more letters, and by the end a student can recite nine things without having been told which of them are load-bearing, which are advice, and which the people who invented them later regretted.

That is a shame, because the ideas are not equally strong and the differences are the interesting part. Some of them are close to laws: violate them and your program is wrong in a way you can demonstrate. Some are heuristics that happen to be right most of the time. At least one is a technique that its own community spent the nineteen-nineties promoting and the two decades since quietly walking back.

This lecture takes them one at a time and tries to say, for each, what claim is actually being made and how much weight it will bear. Where the answer is "less than you were told", it says so. Where a principle has survived every attempt to replace it, it says that too — and there are more of those than the current fashion for dismissing object orientation would suggest.

AssumesYou have written a class. Whether you enjoyed it is not required.

  1. Slide 1

    Four words that do not agree with each other

    The canonical list is encapsulation, abstraction, inheritance and polymorphism, and the first thing to notice is that the man who coined the term did not include three of them.

    Alan Kay named object-oriented programming in the late nineteen-sixties, and what he meant by it was messaging: independent processes that hold their own state and communicate only by sending each other requests they are free to interpret. Classes and inheritance were, in his account, incidental machinery that Simula happened to have. The four-pillar list came later, out of C++ and Java teaching materials, and it describes those languages accurately. It just does not describe the original idea.

    Neither party is wrong, exactly. But it does mean that "object oriented" names two different things, and an argument in which one person means Erlang and the other means Java inheritance is not going to converge.

  2. Slide 2

    Encapsulation is a claim about who can be wrong

    The strongest of the principles, and the one that survives every change of fashion, is the idea that some data may only be touched by code that understands it.

    java

    The point is not privacy for its own sake. It is that the class states an invariant — the balance never goes below zero — and then makes itself the only place that invariant can be broken. If it does break, there is exactly one file to read. Without that, the set of suspects is the whole program.

    This idea is not the property of object orientation. A module with a narrow export list does it, a closure does it, an opaque type in a functional language does it. What object orientation contributed was making it the default rather than an act of discipline, and that turns out to matter more than any argument about syntax.

  3. Slide 3

    Polymorphism is the one that pays for itself

    If encapsulation is about safety, polymorphism is about being able to add things later, and it is what most of the design patterns are quietly made of.

    typescript

    The function log will work with a sink that does not exist yet, written by someone who has never read it. That is the whole trick, and it is why the Strategy pattern hanging in the Gallery of Patterns is four lines of interface and nothing else: the pattern is not the code, it is the fact that the caller was written against a promise instead of against a class.

    There is a cost, and it is worth naming. Every polymorphic call is a place where you cannot tell, from the source, what will run. A program with a great deal of it is easy to extend and genuinely hard to read from top to bottom, which is a trade rather than a triumph.

  4. Slide 4

    Inheritance was oversold, and not by its critics

    The pillar that has aged worst is inheritance, and the most damning assessments of it come from inside the object-oriented tradition rather than from outside.

    The Gang of Four wrote in 1994 that you should favour composition over class inheritance, in the same book that taught a generation what a design pattern was. Java's own libraries carry the scars: the class that once represented a stack of items inherits from the class that represents a resizable array, which means a stack has a method for inserting at position three. Nobody defends that now. It was written by people who had been told that inheritance expresses reuse, and reuse was the thing you were supposed to want.

    What went wrong is that inheritance couples the child to the parent's implementation, not merely to its interface, and does so permanently. The Decorator pattern exists in this museum precisely because it does with composition what a subclass was supposed to do, and can be undone at runtime.

  5. Slide 5

    Liskov's rule is a statement about promises

    Barbara Liskov's substitution principle is the one part of the list that is close to a theorem, and it is usually taught as a slogan about squares and rectangles, which buries the point.

    python

    The rule says that if code works with a Bird, it must keep working when handed a Penguin. A subtype may accept more than its parent and promise more than its parent, but never less of either. Penguin breaks it, and the breakage is not stylistic: a caller that was correct is now incorrect, and nothing in the type system noticed.

    The useful reading is that a subclass inherits the parent's promises along with its code, and most of the pain people attribute to inheritance is really the pain of having inherited promises they had no intention of keeping.

  6. Slide 6

    SOLID is five claims of very unequal strength

    The five principles are usually recited as a unit, which flatters the weaker ones by association.

    Substitution, discussed above, is the strong one. Dependency inversion — depend on interfaces rather than on concrete classes — is really polymorphism restated as advice, and it earns its place. Interface segregation, which asks that a client not be forced to depend on methods it does not use, is sound and mostly uncontroversial.

    The other two are softer than their names suggest. The single responsibility principle depends entirely on what you decide counts as a responsibility, and that judgement is the actual design work; the principle does not make it for you. The open-closed principle, which asks that code be open to extension and closed to modification, describes a genuinely valuable property, and pursued literally it produces the layers of configurable abstraction that make a codebase impossible to read. Robert Martin, who assembled the acronym, has spent years clarifying both, which is itself informative.

  7. Slide 7

    What is worth keeping

    Strip the vocabulary away and a short list of things remains, most of which have stopped being controversial because they won.

    Keep encapsulation: state with an invariant should have exactly one guardian. Keep polymorphism, and reach for it whenever you expect new kinds of thing to arrive. Keep substitution as a discipline, whether or not your language has subclasses, because it is really a rule about not lying in a signature. Keep dependency inversion at the boundaries of a system, where the things you depend on are the things most likely to change.

    Be sparing with inheritance, and prefer it for genuine specialisation rather than for sharing code. Treat the single responsibility and open-closed principles as questions worth asking rather than as rules to be satisfied. And be suspicious of any explanation of object orientation that never mentions that its inventor meant something else — not because he was right and the textbooks are wrong, but because a field that cannot remember its own disagreements is not teaching them.

Works in the collection

The arguments above are hanging on the walls of the museum, in one form or another. These are the ones worth looking at next.

The rest of the programme

  1. Compiled, interpreted, and the space betweenA distinction that stopped describing anything decades ago, why it persists, and what is actually different about the machinery underneath.
  2. What a compiler actually doesFour jobs in a row, each mechanical, none magic: text to tokens, tokens to a tree, a tree to a judgement, and a judgement to instructions.
  3. Functional programming and object orientationTwo ways of arranging a program, what each one genuinely makes easy, and the trade-off underneath the argument that neither side can escape.
  4. Types: what they can and cannot proveA type checker proves one thing about every possible run of your program. Knowing which proposition explains both the enthusiasm and the disappointment.
  5. Null, and the mistake its inventor apologised forTony Hoare called it his billion-dollar mistake. What was actually wrong with it, what the alternatives cost, and why the fix took forty years to arrive.
  6. What abstraction costsAbstraction is sold as free and is not. What you buy, what you pay, and how to tell before writing it which of the two is larger.
  7. Immutability, and what it is not free ofValues that never change buy sharing, comparison and time travel. They are not free, and it is worth knowing where they are expensive before you commit.
  8. Recursion, and why it feels like cheatingA function that calls itself looks like an unpaid debt. What makes it terminate, what it costs on the stack, and why some problems resist any other shape.
  9. Big-O, and what it deliberately ignoresComplexity notation throws away constants, hardware and every input you will actually see. Knowing what it discards is what makes the number useful.
  10. What regular expressions cannot matchThere is a precise boundary around what a regex can recognise. It explains the famous refusal to parse HTML, and why some patterns run forever.
  11. Concurrency is not parallelismOne is a way of structuring a program, the other a way of executing it. Keeping them apart explains why async helps a web server and threads often do not.
  12. Errors: exceptions, values, and what each hidesThrowing makes the happy path readable and the failure paths invisible. Returning errors as values does the opposite. Neither side has won.
  13. Why programs are hard to changeSoftware is called soft because it can be edited. Why editing gets harder every year, what the mechanism is, and which of the usual remedies work.

← All lecturesAtrium