Some insights inspired from theory help a lot with writing CRUD apps. (Also of course, inspired from outside and then explored further by `theoreticians'.)
The most successful of these are now invisible. Either because they are available libraries or so ingrained in the background of what we are doing.
See for example regular expressions, parsers, garbage collection, databases, file systems, CPUs.
For CRUD apps, "Out of the tarpit" (http://shaffner.us/cs/papers/tarpit.pdf) is an interesting paper that I hope describes how the mainstream programmer will build CRUD apps in perhaps ten years time. (I already used a variant of their techniques in a previous job, and it was rather pleasant.)
But yes, in practice knowledge of theory often acts more as a gatekeeper for getting into the likes of Google than you will use it. But when you do spot some opportunities to replace endless workarounds and tedious manual monkey-work with an elegant overarching principle, it often makes all the hours spent studying worth it.
As an great practical example: shake (http://shakebuild.com/) is a build-system that was born out of our frustrations with Gnu Make at Standard Chartered Bank.
Similarly, Bloomberg used to use C++ to describe financial derivative contracts. Those derivative contracts don't fit well into class hierarchies. Switching to an embedded DSL approach embedded in a functional language made the software easier to write and more robust to common errors especially when maintaining.
I am not against thinking in terms of coupling and cohesion but I found the following different and interesting ways to think about separating software:
The common theme in both these viewpoints is that the biggest enemy is state, and who is responsible for it, rather than just responsibility on its own.
"Out of the Tarpit" (http://shaffner.us/cs/papers/tarpit.pdf) is a paper with a similar bent arguing that we can eliminate complexity with a functional/relational approach to designing software.
> I honestly don't see how you could implement it without object oriented design. Surely it makes sense to have a Diagram class that encapsulates a list of strokes and pictures? Isn't it easier if the Diagram class exposes addStroke() and removeStroke() but does not reveal how it's implemented? And shouldn't I have a separate view class which encapsulates how much zoom and pan the user has applied to the diagram?
Data structures and operations on them are indeed a useful concept. Not limited to oop, though.
> Could you implement Undo and Redo actions so neatly without a command pattern?
Persistent data structures make it even easier. You just keep a list of references to the old states around.
> I actually get a little thrill when I think about how cleanly this design addresses the requirements. Could I get that feeling if this were implemented in a functional programming style?
I can't predict your feelings, but what you described could very well be done in eg Haskell. (You just wouldn't use oop classes, but you can abstract over similar concepts.)
Have a look at http://shaffner.us/cs/papers/tarpit.pdf for some thoughts on software design. (The paper advocates using relations. I have seen them work very, very well in functional settings. Just the opposite of ORMs.)
There are some fairly aspirational claims about how it might be different in this paper, which is a great read:
There has already been some significant progress on this front. E.g., SQL and logic programming let you describe what you want to happen, and let the computer figure out some of the details. Any compiler worth using does this, too. Smarter machines and smarter programs will mean smarter programming languages.
Oddly enough, to this day most programming languages encourage a graph or hierarchical model.
Taking Codd serious suggests using relations inside programs as well.
If you are referring to object-oriented polymorphism, "Out of the Tarpit" comments directly on this AT http://shaffner.us/cs/papers/tarpit.pdf page 12, the conclusion being that OO suffers from unnecessary state complexity.
In addition, the glut of ORMs demonstrate the various approaches to solving the impedance mismatch between the relational algebra and object-oriented architecture with no clear winning strategy.
Instead, the proposal from the "Out of the Tarpit" paper is "functional relational programming" which fuses relational algebra operations and declarative functional programming.
I invite you to take a look at how Project:M36 handles these requirements at https://github.com/agentm/project-m36/blob/master/docs/reach...