Я работал над рефакторингом некоторого кода, и я думаю, что, возможно, сделал первый шаг по кроличьей норе. Я пишу пример на 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);
}
}