
The authors take a novel approach to understanding object-oriented languages by introducing object calculi and developing a theory of objects around them. The book covers both the semantics of objects and their typing rules, and explains a range of object-oriented concepts, such as self, dynamic dispatch, classes, inheritance, prototyping, subtyping, covariance and contravariance, and method specialization. (From back-cover copy)
Two more:
1) A Theory of Objects by Abadi and Cardelli.
2) Putting Metaclasses to Work by Forman and Danforth (Guido Van Rossum recommends it on Amazon!).
However, it turns out it was in SIMULA 67, taken from a 1965 proposal by Tony Hoare for record handling in Algol, which I haven't been able to find yet. That is, it was proposed in a context where objects had fields, but not methods, and thus not protocols either. SIMULA 67 had overriding of superclass methods if they were marked “virtual”, as in C++.
Turning back to Window and Number, an alternative using composition instead of inheritance puts the shared and unshared code in different objects. The Window class becomes one class only, with a field indicating what its contents are, to which it delegates paint messages and handling of input events; Number becomes a wrapper that expects its contents to implement < and ==, perhaps, and implements the other four methods on top of them.
How does this differ from the approach using inheritance? It's a great deal more hassle to change your mind about which methods are delegated to the wrapped object, but much easier to be sure that other refactorings are correct, because it's much easier to tell which methods could potentially be “overridden by a subclass”. It affords the possibility of changing the contents of a Window over time—particularly useful in languages like Python where reloading code after a modification creates new classes rather than modifying the existing ones. It costs an extra allocation and an extra method call on every delegated method (fatal in the case of SmallInteger, normally a subclass of Number, but SmallInteger is already a collection of hacks for efficiency; giving it an independent implementation of the Number protocol is reasonable).
Perhaps we should regard inheritance as an efficiency and convenience hack for cases where memory is tight or we're initially exploring an OO design, one we should remove later to improve maintainability once it's more or less clear how to divide up the responsibilities.
Smalltalk-80 used inheritance for its collections library, in a way that does not respect LSP, perhaps understandably because Barbara Liskov was on the other coast, and CLU was a very different language from Smalltalk. Some more recent languages like Java modeled their collections after Smalltalk’s. I don't think it's entirely fair to ding Josh Bloch for this.
Rigorously modeling inheritance (with overriding, open recursion, and covariant self type) turns out to be quite challenging; I recommend Abadí and Cardelli's A Theory of Objects to those who are interested.
The most interesting thing I'm looking at right now with regard to software design is Jackson's Alloy model checker, which does a kind of abstract-interpretation exhaustive test of your high-level design to verify that in examples to a certain size, your desired properties hold. This is of course a different level of abstraction from the factoring of the implementation to eliminate duplication, but it can tell you which contemplated protocols are fatally flawed.
If you really want to know what OOP is / was / can be, read Cardelli and Abadi's A Theory of Objects. The first half is available online, and covers the parts you'd care about: http://books.google.com/books?id=4xT3LgCPP5UC
There is absolutely a theory behind objects, I urge you to read Luca Cardelli and Martin Abadi's monograph "A Theory of Objects", you'll find all the answers you're looking for there with both rigorous proofs and examples from languages (real and imagined).
OODBs did not fail because of a lack of theory, nearly all of the people writing and using RDBs have a poor grounding in the theory -- see Fabian Pascal's contention that all of the failings of extant RDBs are because they are not relational enough, that SQL is not relational.
OODBs failed because the commercial Smalltalk vendors who had quality products all faded from view, and the term was hijacked by marketing departments pushing bullshit.
The solution I see [Google, eBay, Amazon, etc.] using is to use the DB as raw simplistic storage with strong guarantees, and to push all of the joining and caching up into an application layer. There's no way to "tell the database more information" without using a programming language.