Я працював над рефакторингом якогось коду, і, думаю, я зробив перший крок у кролячій норі. Я пишу приклад на Java, але, мабуть, це може бути агностиком.
У мене інтерфейс Foo
визначений як
public interface Foo {
int getX();
int getY();
int getZ();
}
І реалізація як
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;
}
У мене також є інтерфейс, MutableFoo
який забезпечує відповідні мутатори
/**
* 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
які я ще не реалізував. Один з них є
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;
}
Причина, по якій я їх розділив, полягає в тому, що однаково ймовірність використання кожного. Це означає, що однаково ймовірно, що хтось, хто використовує ці класи, захоче незмінний екземпляр, так як саме він захоче змінний.
Основний випадок використання, який у мене є, називається інтерфейсом, StatSet
який представляє певні бойові деталі для гри (точки удару, атака, захист). Однак "ефективні" статистичні дані, або фактичні статистичні дані, є результатом базової статистики, яку ніколи не можна змінити, і тренованої статистики, яку можна збільшити. Ці два пов'язані між собою
/**
* 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);
}
Навчені Стани збільшуються після кожного подібного бою
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);
}
але вони не збільшуються відразу після бою. Використання певних предметів, використання певної тактики, розумне використання поля бою - все це може дати різний досвід.
Тепер для моїх запитань:
- Чи існує назва поділу інтерфейсів за допомогою аксесуарів та мутаторів на окремі інтерфейси?
- Чи розділення їх таким чином «правильний» підхід, якщо вони однаковою мірою будуть використані, чи є інша, більш прийнята модель, яку я повинен використовувати замість цього (наприклад,
Foo foo = FooFactory.createImmutableFoo();
яка може повернутисяDefaultFoo
абоDefaultMutableFoo
прихована черезcreateImmutableFoo
поверненняFoo
)? - Чи є негайно передбачувані мінуси використання цього шаблону, окрім ускладнення ієрархії інтерфейсу?
Причина, що я почав розробляти його таким чином, полягає в тому, що я маю на увазі, що всі реалізатори інтерфейсу повинні дотримуватися найпростішого можливого інтерфейсу і більше нічого не надавати. Додавши в інтерфейс сетери, тепер ефективну статистику можна змінювати незалежно від її частин.
Створення нового класу для EffectiveStatSet
не має особливого сенсу, оскільки ми жодним чином не розширюємо функціональність. Ми могли б змінити реалізацію та скласти EffectiveStatSet
композицію з двох різних StatSets
, але я вважаю, що це не правильне рішення;
public class EffectiveStatSet implements StatSet {
public EffectiveStatSet(StatSet baseStats, StatSet trainedStats) {
// ...
}
public int getHitpoints() {
return baseStats.getHitpoints() + (trainedStats.getHitpoints() / 4);
}
}