Ich habe daran gearbeitet, Code zu überarbeiten, und ich glaube, ich habe den ersten Schritt im Kaninchenbau getan. Ich schreibe das Beispiel in Java, aber ich nehme an, es könnte agnostisch sein.
Ich habe eine Schnittstelle Foo
definiert als
public interface Foo {
int getX();
int getY();
int getZ();
}
Und eine Implementierung als
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;
}
Ich habe auch eine Schnittstelle MutableFoo
, die passende Mutatoren bietet
/**
* 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);
}
Es gibt einige Implementierungen davon MutableFoo
(ich habe sie noch nicht implementiert). Einer von ihnen ist
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;
}
Der Grund, warum ich diese aufgeteilt habe, ist, dass es für jeden gleich wahrscheinlich ist, dass sie verwendet werden. Das heißt, es ist ebenso wahrscheinlich, dass jemand, der diese Klassen verwendet, eine unveränderliche Instanz wünscht, wie es ist, dass er eine veränderbare Instanz will.
Der primäre Anwendungsfall, den ich habe, ist eine Schnittstelle namens StatSet
, die bestimmte Kampfdetails für ein Spiel darstellt (Trefferpunkte, Angriff, Verteidigung). Die "effektiven" Statistiken oder die tatsächlichen Statistiken sind jedoch ein Ergebnis der Basisstatistiken, die niemals geändert werden können, und der trainierten Statistiken, die erhöht werden können. Diese beiden sind verwandt mit
/**
* 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);
}
Die trainierten Statistiken werden nach jedem Kampf wie erhöht
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);
}
aber sie werden nicht gleich nach dem Kampf erhöht. Wenn Sie bestimmte Gegenstände verwenden, bestimmte Taktiken anwenden und das Schlachtfeld geschickt einsetzen, können Sie unterschiedliche Erfahrungen sammeln.
Nun zu meinen Fragen:
- Gibt es einen Namen für die Aufteilung von Schnittstellen durch Accessoren und Mutatoren in separate Schnittstellen?
- Ist Splitting sie auf diese Weise der ‚richtige‘ Ansatz , wenn sie gleich wahrscheinlich sind verwendet werden, oder gibt es ein anderes, akzeptierten Muster , dass ich stattdessen verwenden sollte (z. B. ,
Foo foo = FooFactory.createImmutableFoo();
die zurückkehren könnteDefaultFoo
oderDefaultMutableFoo
aber weil verstecktcreateImmutableFoo
kehrtFoo
)? - Gibt es unmittelbar vorhersehbare Nachteile bei der Verwendung dieses Musters, abgesehen von der Komplikation der Schnittstellenhierarchie?
Der Grund, warum ich angefangen habe, es so zu entwerfen, ist, dass ich der Meinung bin, dass alle Implementierer einer Schnittstelle die einfachste mögliche Schnittstelle einhalten und nichts mehr bereitstellen sollten. Durch Hinzufügen von Setzern zur Benutzeroberfläche können jetzt die effektiven Statistiken unabhängig von ihren Teilen geändert werden.
Das Erstellen einer neuen Klasse für das EffectiveStatSet
macht wenig Sinn, da wir die Funktionalität in keiner Weise erweitern. Wir könnten die Implementierung ändern und EffectiveStatSet
eine Mischung aus zwei verschiedenen erstellen StatSets
, aber ich denke, das ist nicht die richtige Lösung.
public class EffectiveStatSet implements StatSet {
public EffectiveStatSet(StatSet baseStats, StatSet trainedStats) {
// ...
}
public int getHitpoints() {
return baseStats.getHitpoints() + (trainedStats.getHitpoints() / 4);
}
}