Il Principio di Substiution di Liskov è di gran lunga la migliore "euristica" che conosca per determinare se una sottoclasse diretta è una buona idea.
Supponiamo che tu abbia una classe base Foo e le classi derivate da Bar1 a Bar9. In poche parole, l'LSP afferma che qualsiasi blocco di codice che utilizza Foo deve funzionare correttamente, indipendentemente da quale dei nove tipi di Bar in realtà Foo .
Dal punto di vista dell'utente, ciò significa che il codice che utilizza la classe base dovrebbe essere in grado di funzionare quasi esclusivamente con quella classe base, senza preoccuparsi di quale sia il tipo effettivo dell'oggetto.
Dal punto di vista del realizzatore, ciò significa che ciascuna sottoclasse deve implementare l'intera interfaccia della classe base, incluse tutte le proprietà semantiche implicite, senza sorprese o restrizioni aggiuntive.
Se la sottoclasse che stai pensando di scrivere non seguirà queste regole, è un'indicazione molto strong che non dovresti usare l'ereditarietà.
Per quanto riguarda l'esempio della barra degli strumenti, non penso che abbia molto senso utilizzare l'ereditarietà in questo modo. Preferisco avere le classi Button, una generica barra degli strumenti che può contenere qualsiasi disposizione di pulsanti, quindi rendere BasicToolbar e ExtendedToolbar essere oggetti con un membro della barra degli strumenti a cui aggiungono i pulsanti nei loro costruttori. Se condividono un gruppo di pulsanti, rendere le classi BackButton / LikeButton / UpvoteButton con un membro Button e lasciare che le barre degli strumenti li contengano. L'utilizzo dell'eredità in qualsiasi punto di questa disposizione non farà che aumentare le restrizioni inutilmente rigide su come riordinare queste classi in futuro.