Prima di tutto, la premessa è imperfetta. Sicuramente puoi modificare le classi in fase di runtime, ad es. in Ruby:
class Foo; end
foo = Foo.new
foo.bar
# NoMethodError: undefined method 'bar' for #<Foo:0xdeadbeef4815162342>
class Foo; def bar; 'Hello' end end
foo.bar
# => 'Hello'
Python:
class Foo: pass
foo = Foo()
foo.bar()
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# AttributeError: Foo instance has no attribute 'bar'
Foo.bar = lambda self: "Hello"
foo.bar()
# => "Hello"
D'altra parte, ci sono linguaggi basati su prototipi, che non consentono di modificare il prototipo in fase di esecuzione, in particolare i linguaggi basati su prototipo tipizzati staticamente.
Ora, per rispondere alla tua domanda:
But it seems to me that needing the ability to add new properties/methods to your classes at runtime would be indicative of the code author not fully anticipating the needs and responsibilities of the class when defining it.
Sì. È difficile fare pronostici, soprattutto sul futuro. Pertanto, a volte è difficile se non impossibile sapere quale sarà la forma di un oggetto fino al runtime. Pensa, ad esempio, a un deserializzatore XML, che crea oggetti descritti da un documento XML, la cui forma è descritta da uno schema XML, che vengono entrambi passati come argomenti della riga di comando in fase di runtime.
Il classico rant di Steve Yegge su The Universal Design Pattern è un altro esempio, dove essere in grado di aggiungere e rimuovere dinamicamente le proprietà dagli oggetti è desiderabile. Fornisce diversi casi di studio, in cui le persone hanno re-inventato prototipi e proprietà dinamiche su linguaggi che non li hanno in modo nativo, incluso all'interno del JDT di Eclipse scritto in Java e nel gioco di ruolo multiplayer di Steve Yegge, Wyvern, scritto anche in Giava. A Wyvern vengono utilizzati sacchi di proprietà con prototipi per modellare più o meno tutti gli oggetti di gioco.