Ci sono punti da tenere a mente per ogni approccio, e potrebbe non esserci un chiaro vincitore.
Campo protetto
Se non costante, un campo protetto consente alla sottoclasse di modificare direttamente lo stato della classe genitore. Ciò significa che una sottoclasse può essere in grado di violare le invarianti o le convalide del genitore. Lo stato potrebbe essere in grado di cambiare in tempi inopportuni, il che potrebbe causare problemi (o potrebbe non esserlo).
In altre parole, questo approccio potrebbe causare il comportamento incoerente di una classe, probabilmente in modo negativo.
Detto questo, alcuni stati potrebbero essere trattati come volatili e va bene cambiarli quando vuoi.
Getter
In realtà non è molto diverso. I metodi possono essere ignorati a condizione che non vengano contrassegnati come non sovrascrivibili (ad esempio finale, non virtuale). La differenza è che mentre un campo può essere modificato direttamente, una combinazione getter / setter può far rispettare invarianti e convalide sul campo in modo trasparente.
Lo stato rappresenta direttamente una proprietà della classe base, o capita che tutte le sottoclassi abbiano bisogno dello stato ma in realtà non appartengono alla classe base? Ad esempio, supponiamo che tutti gli animali possano parlare. L'animale sa di poter parlare, ma il come appartiene alla sottoclasse. Forse un metodo getter astratto è appropriato: Animal può usarlo, ma l'effettiva implementazione deve essere definita in ogni sottoclasse.
Se lo stato non cambia, potresti anche essere in grado di definire una costante nella classe base. Forse la sottoclasse passa un valore al costruttore della classe base che poi lo imposta in pietra in quella costante. Ciò consente alla classe base di utilizzare anche la costante perché è definita nella classe base.
Non c'è un modo per farlo, e la comprensione del modo giusto per una determinata situazione richiede esperienza. Speriamo che questo aiuti a spiegare perché ciascuna opzione funziona e in che modo sono differenti.