Questo è il codice in questione, i commenti lo indicano:
class Actor extends Entity {
private MutableVector2f position;
private MutableIdentity identity;
public Actor(MutableVector2f initialPosition, MutableIdentity initialIdentity) {
super(initialPosition, initialIdentity); // !! pass to supertype !!
this.position = initialPosition; // !! reference in subtype !!
this.identity = initialIdentity; // !! "" "" !!
}
public void moveTowards(Vector2f targetPosition) {
// !! change position member, purpose of keeping reference !!
}
public void changeNameTo(String name) {
identity.changeNameTo(name);
}
}
Sembra un odore di codice. È? Forse c'è un problema con la struttura dei tipi?
Ci sono delle cadute verso questo tipo di design?
La classe base Entity
viene utilizzata per qualsiasi oggetto con una posizione:
abstract class Entity {
private Vector2f position;
private Identity identity;
public Entity(Vector2f position, Identity identity) {
this.position = position;
this.identity = identity;
}
public Vector2f getPosition() {
return position;
}
public Identity getIdentity() {
return identity;
}
}
Il sottotipo Actor
ha una posizione mutabile per ridurre la quantità di oggetti creati (potrebbe avere centinaia di migliaia di attori che si muovono e cambiano identità)
Il sottotipo WorldObject
ha una posizione e un'identità immutabili:
class WorldObject extends Entity {
public WorldObject(ImmutableVector2f position, ImmutableIdentity identity) {
super(position, identity);
}
}
Vector2f
è un'interfaccia che implementa "ImmutableVector2f and
MutableVector2f":
interface Vector2f {
float getX();
float getY();
}
MutableVector2f
espone metodi come add
, multiply
e normalize
.
Identity
segue la stessa struttura (è un'interfaccia, ha sottotipi immutabili e mutabili)
Ritengo che ciò possa violare LSP. Lo scopo delle interfacce è impedire la modifica da una prospettiva Entity
.
Non vuol dire che Entity
è immutabile, ma per scoraggiare la mutazione a quel livello di astrazione, poiché i sistemi che usano Entity
dovrebbero solo leggere valori.
Mantenere un riferimento nella sottoclasse impedirà la necessità di lanciare Entity#getPosition()
, ma sembra sbagliato. Rendere le proprietà generiche potrebbe portare a una lunga lista di parametri di tipo se dovessi continuare a scalare Entity
in questo modo.
È possibile che Entity#getPosition()
debba essere astratto e i sottotipi dovrebbero mantenere le proprie variabili di posizione? Mi richiederebbe di dichiarare i getter nei sottotipi (codice duplicato), quindi non sono un grande fan dell'idea.