我在开发一款小游戏时,昨晚给自己编了一个问题。前几天,我确信自己应该将我的一些业余时间用于高效工作,所以我决定开始研究基于文本的RPG,而不是游戏。那么,我来到游戏中代表货币的问题。现在,因为这只是为了好玩,我想稍微挑战自己。而不是仅仅将货币表示为单一价值(例如:称为&#34的单一类型的硬币;金币和#34;不是游戏中唯一的硬币。)
我决定做的是创造4种类型的硬币 - 便士,铜,denar和oren。所有4个硬币都具有重量,体积,材料和名称等值。此外,硬币具有汇率,这决定了它们的相对价值。这样做的目的是允许不同的用法,否则将是一种无聊的旧货币。我的问题是我不确定如何实现它。
昨晚我到达的是4班(Pence,Copper,Denar,Oren),他们扩展了一个抽象级的硬币。硬币包含许多受保护的静态元素,例如所有4个子类的DENSITY,VOLUME,NAME,EXCHANGE。
子类的构造函数如下所示:
public Coppers() {
super();
super.metal = COPPER_METAL;
super.name = COPPER;
super.setVolume();
super.setDensity();
super.setWeight();
}
超类中的方法如下所示:
protected void setDensity() {
switch( getMetal()) {
case "copper":
this.density = DENSITY_COPPER;
break;
case "silver":
this.density = DENSITY_SILVER;
break;
case "gold":
this.density = DENSITY_GOLD;
break;
default:
this.density = DENSITY_COPPER;
break;
};
}
这似乎非常......错了。我不确定最佳做法是什么。我问我的朋友们使用静态类来保存这些值,并收到混合响应。但这些类的POINT很重要。想象一下,玩家类有一个名为Purse的对象,它跟踪不同类型硬币的数量。通过他们的钱包,玩家可以在银行兑换硬币,购买商品和出售硬币。持有一套所有实例化硬币是没有任何意义的,对吧?我只需要信息和方法。那么实现静态类是否有意义?当他们共享这么多属性时,我如何才能让所有4个硬币最佳?
答案 0 :(得分:1)
在这种情况下,您可以使用Enum
。你枚举了你需要的常量,通过它们的构造函数给它们一个类型。
现在我们有他们的类型,我们可以将它与您在代码中处理的字符串进行比较,如果没有类型匹配,我们默认将其设置为Density.COPPER
。
Density density;
protected void setDensity (String metal) {
for (Density d : Density.values()) {
if (metal.equals(d.getType())) {
this.density = d;
return;
}
}
this.density = Density.COPPER;
}
enum Density {
COPPER("copper"),
SILVER("silver"),
GOLD("gold");
String type;
Density(String s) {
type = s;
}
public String getType() {
return type;
}
}
答案 1 :(得分:1)
让我们在这里倒退。
想象一下,玩家类有一个名为Purse的对象,它跟踪不同类型硬币的数量。通过他们的钱包,玩家可以在银行兑换硬币,购买商品,并出售硬币货物。
这意味着什么:
public class Purse {
private final List<Coin> coins = new ArrayList<>();
}
这告诉我这里的枚举不够(足够)。此上下文中的枚举描述了多个状态;您正在寻找的是实际对象,它们可以保存您需要进行某些计算的值。
如果我们打算坚持这种货币,我认为某些中心对象没有任何问题来描述它。
在我看来,使用抽象类可能没问题,但你错过了一个关键组件:一个工厂来创建你想要的硬币类型。你也想完全减少硬币的责任 - 硬币知道它的价值是好的,但它不应该关心它相对于其他硬币的价值;这是某种交换对象的责任,它打算根据你给定硬币的价值产生一些硬币。
所以让我们编写抽象类的构造函数。如果我们想要创建一个通用硬币,我们需要知道它的体积,密度和重量。这个名字是根据它的类名提供的,所以你真的不必担心这个;你可以在以后提取它。
如果您想要某种硬币层次结构,可以使用Comparable
;说明那里的排序,而不是通过枚举。
public abstract class Coin implements Comparable<Coin> {
protected final int volume;
protected final int density;
protected final int weight;
public Coin(int volume, int density, int weight) {
this.volume = volume;
this.density = density;
this.weight = weight;
}
public int getVolume() {
return volume;
}
public int getDensity() {
return density;
}
public int getWeight() {
return weight;
}
}
这描述了准系统Coin
类型。
例如,我们也在这里描述Copper
类型。该代码假设相同类型的硬币具有可比性,否则它会自行降级(铜位于列表的底部)。
观察一些事情:
compareTo
(因为我们必须),我们让那个驱动器成为硬币订购的主要方式。public class Copper extends Coin {
public Copper(final int volume, final int density, final int weight) {
super(volume, density, weight);
}
@Override
public int compareTo(final Coin otherCoin) {
if(otherCoin instanceof Copper) {
return (volume - getVolume()) + (density - getDensity()) + (weight - getWeight());
}
// assume Coppers are worth the least
return Integer.MIN_VALUE;
}
}
其他货币留给读者练习。
我想要涵盖的最后一件事是适用于所有货币的某种形式的发电机。这是一些反射魔法可以真正帮助调用你关心的构造函数。
我还将此返回Optional<T extends Coin>
,以便在生成失败的情况下出于某种原因,您可以选择使用而不是null
public class CoinFactory {
private CoinFactory() {
}
public static <T extends Coin> Optional<T> generateCoin(int weight, int volume, int density, Class<T> clazz) {
Optional<T> coin = Optional.empty();
try {
coin = Optional.of(clazz.getDeclaredConstructor(int.class, int.class, int.class)
.newInstance(weight, volume, density));
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
return coin;
}
}
您可以使用此main
方法对其进行抽烟测试:
public static void main(String[] args) {
final Optional<Copper> x = CoinFactory.generateCoin(10, 20, 30, Copper.class);
if(x.isPresent()) {
System.out.println(x.get());
}
}
答案 2 :(得分:0)
在Map
中为密度设置私有Coin
,密钥为“铜”,“金”和“银”,值为DENSITY_
常量。 setDensity()
应该只运行:
this.density = densityMap.get(getMetal());
或者更好的是,废弃setDensity()
,而只是getDensity()
,返回densityMap.get(getMetal())
。
答案 3 :(得分:0)
由于四种硬币的行为没有区别,一个类就足够了。我建议这个:
public enum Coin {
PENCE, COPPER, DENAR, OREN;
private static final String[] METAL = { "copper", "copper", "silver", "gold" };
private static final int[] VALUE = { 1, 12, 60, 360 };
public String getMetal() {
return METAL[ordinal()];
}
public int getValue() {
return VALUE[ordinal()];
}
}