J'ai travaillé sur la refactorisation de code, et je pense que j'ai peut-être fait le premier pas dans le terrier du lapin. J'écris l'exemple en Java, mais je suppose que cela pourrait être agnostique.
J'ai une interface Foo
définie comme
public interface Foo {
int getX();
int getY();
int getZ();
}
Et une mise en œuvre comme
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;
}
J'ai également une interface MutableFoo
qui fournit des mutateurs correspondants
/**
* 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);
}
Il existe quelques implémentations MutableFoo
qui pourraient exister (je ne les ai pas encore implémentées). L'un d'eux est
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;
}
La raison pour laquelle je les ai divisés est qu'il est également probable que chacun soit utilisé. Autrement dit, il est tout aussi probable que quelqu'un utilisant ces classes veuille une instance immuable, comme il le voudra une instance mutable.
Le principal cas d'utilisation que j'ai est une interface appelée StatSet
qui représente certains détails de combat pour un jeu (points de vie, attaque, défense). Cependant, les statistiques "effectives", ou les statistiques réelles, sont le résultat des statistiques de base, qui ne peuvent jamais être modifiées, et des statistiques entraînées, qui peuvent être augmentées. Ces deux sont liés par
/**
* 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);
}
les Stats formés sont augmentés après chaque bataille comme
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);
}
mais ils ne sont pas augmentés juste après la bataille. L'utilisation de certains objets, l'emploi de certaines tactiques, une utilisation intelligente du champ de bataille peuvent tous offrir une expérience différente.
Maintenant pour mes questions:
- Existe-t-il un nom pour diviser les interfaces par les accesseurs et les mutateurs en interfaces distinctes?
- Est-ce que les séparer de cette manière est la «bonne» approche s'ils sont également susceptibles d'être utilisés, ou existe-t-il un modèle différent et plus accepté que je devrais utiliser à la place (par exemple,
Foo foo = FooFactory.createImmutableFoo();
qui pourrait revenirDefaultFoo
ou quiDefaultMutableFoo
est caché parce qu'ilcreateImmutableFoo
revientFoo
)? - Y a-t-il des inconvénients immédiatement prévisibles à utiliser ce modèle, sauf pour compliquer la hiérarchie de l'interface?
La raison pour laquelle j'ai commencé à la concevoir de cette façon est parce que je pense que tous les implémenteurs d'une interface devraient adhérer à l'interface la plus simple possible et ne rien fournir de plus. En ajoutant des setters à l'interface, les statistiques effectives peuvent désormais être modifiées indépendamment de ses parties.
Créer une nouvelle classe pour le EffectiveStatSet
n'a pas beaucoup de sens car nous n'étendons pas la fonctionnalité en aucune façon. Nous pourrions changer la mise en œuvre, et faire EffectiveStatSet
un composite de deux différents StatSets
, mais je pense que ce n'est pas la bonne solution;
public class EffectiveStatSet implements StatSet {
public EffectiveStatSet(StatSet baseStats, StatSet trainedStats) {
// ...
}
public int getHitpoints() {
return baseStats.getHitpoints() + (trainedStats.getHitpoints() / 4);
}
}