I think I ended up going with a Player API with tapes, records, CDs etc. Some could skip, others only fast-forward. I was still unhappy with it, but it was better than most examples.
I like to point folks at writing an IntelliJ plugin. I find that some of the best architected OOP programs become nothing but a collection of plugin interfaces and base classes.
But you really need to understand "is a" vs "has a", not burn your base class, let architecture emerge following the rule of 3, rather than quickly jumping on the wrong abstraction to fight duplication.
Sandi Metz really says it better than I can: https://www.youtube.com/watch?v=8bZh5LMaSmE