私はいくつかのコードのリファクタリングに取り組んできましたが、ウサギの穴を降りる最初の一歩を踏み出したのではないかと思います。私は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
可能性のある実装がいくつかあります(まだ実装していません)。それらの1つは
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
は、ゲームの特定の戦闘の詳細(ヒットポイント、攻撃、防御)を表すと呼ばれるインターフェースです。ただし、「有効な」統計または実際の統計は、変更できない絶対的な基本統計と、増加可能な訓練された統計の結果です。これら2つは、
/**
* 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);
}
trainedStatsは次のようなすべての戦闘後に増加します
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
、2つの異なるを合成することもできますが、StatSets
それは適切な解決策ではないと感じています。
public class EffectiveStatSet implements StatSet {
public EffectiveStatSet(StatSet baseStats, StatSet trainedStats) {
// ...
}
public int getHitpoints() {
return baseStats.getHitpoints() + (trainedStats.getHitpoints() / 4);
}
}