Ho lavorato sul refactoring del codice e penso di aver fatto il primo passo nella tana del coniglio. Sto scrivendo l'esempio in Java, ma suppongo che potrebbe essere agnostico.
Ho un'interfaccia Foo
definita come
public interface Foo {
int getX();
int getY();
int getZ();
}
E un'implementazione come
public final class DefaultFoo implements Foo {
public DefaultFoo(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getZ() {
return z;
}
private final int x;
private final int y;
private final int z;
}
Ho anche un'interfaccia MutableFoo
che fornisce mutatori corrispondenti
/**
* This class extends Foo, because a 'write-only' instance should not
* be possible and a bit counter-intuitive.
*/
public interface MutableFoo extends Foo {
void setX(int newX);
void setY(int newY);
void setZ(int newZ);
}
MutableFoo
Potrebbero esistere un paio di implementazioni (non le ho ancora implementate). Uno di essi è
public final class DefaultMutableFoo implements MutableFoo {
/**
* A DefaultMutableFoo is not conceptually constructed
* without all values being set.
*/
public DefaultMutableFoo(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int getX() {
return x;
}
public void setX(int newX) {
this.x = newX;
}
public int getY() {
return y;
}
public void setY(int newY) {
this.y = newY;
}
public int getZ() {
return z;
}
public void setZ(int newZ) {
this.z = newZ;
}
private int x;
private int y;
private int z;
}
Il motivo per cui li ho divisi è perché è ugualmente probabile che ciascuno venga usato. Ciò significa che è altrettanto probabile che qualcuno che utilizza queste classi desidererà un'istanza immutabile, poiché è desiderabile che ne abbia una mutabile.
Il caso d'uso principale che ho è un'interfaccia chiamata StatSet
che rappresenta alcuni dettagli di combattimento per un gioco (punti ferita, attacco, difesa). Tuttavia, le statistiche "efficaci", o le statistiche effettive, sono il risultato delle statistiche di base, che non possono mai essere modificate e delle statistiche allenate, che possono essere aumentate. Questi due sono collegati da
/**
* The EffectiveStats can never be modified independently of either the baseStats
* or trained stats. As such, this StatSet must never provide mutators.
*/
public StatSet calculateEffectiveStats() {
int effectiveHitpoints =
baseStats.getHitpoints() + (trainedStats.getHitpoints() / 4);
int effectiveAttack =
baseStats.getAttack() + (trainedStats.getAttack() / 4);
int effectiveDefense =
baseStats.getDefense() + (trainedStats.getDefense() / 4);
return StatSetFactory.createImmutableStatSet(effectiveHitpoints, effectiveAttack, effectiveDefense);
}
gliStat addestrati vengono aumentati dopo ogni battaglia come
public void grantExperience() {
int hitpointsReward = 0;
int attackReward = 0;
int defenseReward = 0;
final StatSet enemyStats = enemy.getEffectiveStats();
final StatSet currentStats = player.getEffectiveStats();
if (enemyStats.getHitpoints() >= currentStats.getHitpoints()) {
hitpointsReward++;
}
if (enemyStats.getAttack() >= currentStats.getAttack()) {
attackReward++;
}
if (enemyStats.getDefense() >= currentStats.getDefense()) {
defenseReward++;
}
final MutableStatSet trainedStats = player.getTrainedStats();
trainedStats.increaseHitpoints(hitpointsReward);
trainedStats.increaseAttack(attackReward);
trainedStats.increaseDefense(defenseReward);
}
ma non sono aumentati subito dopo la battaglia. L'uso di determinati oggetti, l'utilizzo di alcune tattiche, l'uso intelligente del campo di battaglia possono garantire un'esperienza diversa.
Ora per le mie domande:
- Esiste un nome per suddividere le interfacce tra accessori e mutatori in interfacce separate?
- Suddividendoli in questo modo è l'approccio "giusto" se è altrettanto probabile che vengano utilizzati o esiste invece un modello diverso e più accettato che dovrei usare (ad es.
Foo foo = FooFactory.createImmutableFoo();
Che potrebbe restituireDefaultFoo
oDefaultMutableFoo
ma è nascosto perchécreateImmutableFoo
ritornaFoo
)? - Ci sono aspetti negativi immediatamente prevedibili nell'uso di questo modello, salvo complicare la gerarchia dell'interfaccia?
Il motivo per cui ho iniziato a progettarlo in questo modo è perché sono dell'idea che tutti gli implementatori di un'interfaccia dovrebbero aderire all'interfaccia più semplice possibile e non fornire nulla di più. Aggiungendo setter all'interfaccia, ora le statistiche effettive possono essere modificate indipendentemente dalle sue parti.
Fare una nuova classe per il EffectiveStatSet
non ha molto senso in quanto non stiamo estendendo la funzionalità in alcun modo. Potremmo cambiare l'implementazione e creare un EffectiveStatSet
composto di due diversi StatSets
, ma ritengo che non sia la soluzione giusta;
public class EffectiveStatSet implements StatSet {
public EffectiveStatSet(StatSet baseStats, StatSet trainedStats) {
// ...
}
public int getHitpoints() {
return baseStats.getHitpoints() + (trainedStats.getHitpoints() / 4);
}
}