Java 存储货币汇率的设计
我已经开始了一个新的信用管理项目,我已经到了必须处理货币兑换的地步。(例如欧元->美元)所以我做了一些头脑风暴,得出了以下结论:Java 存储货币汇率的设计,java,oop,Java,Oop,我已经开始了一个新的信用管理项目,我已经到了必须处理货币兑换的地步。(例如欧元->美元)所以我做了一些头脑风暴,得出了以下结论: 货币是抽象的,每一种新货币都是一个接口的实现 构建了一个rate类,该类存储pair数组列表(我想到的是类似于euro,dollar,1.14->欧元对美元的汇率是1.14),并具有一些功能,如:货币汇率存储,正确汇率的查找器(它是一个函数,获取当前货币和新货币作为参数,并在列表上执行搜索,返回适当的货币)和一个实用函数,用于确定货币对的索引 现在,我在考虑性能。
- 货币是抽象的,每一种新货币都是一个接口的实现
- 构建了一个rate类,该类存储pair
数组列表(我想到的是类似于euro,dollar,1.14->欧元对美元的汇率是1.14),并具有一些功能,如:货币汇率存储,正确汇率的查找器(它是一个函数,获取当前货币和新货币作为参数,并在列表上执行搜索,返回适当的货币)和一个实用函数,用于确定货币对的索引
package currency;
import javafx.util.Pair;
import loggers.ILogger;
import java.util.ArrayList;
public class ConversionRates implements IConversionRate {
private ArrayList<Pair<Pair<String, String>, Double>> conversionRates;
private ILogger log;
public ConversionRates(ArrayList<Pair<Pair<String, String>, Double>> conversionRates, ILogger log) {
this.conversionRates = conversionRates;
this.log = log;
}
@Override
public double find(ICurrency firstCurrency, ICurrency secondCurrency) {
log.add("Performing rate identification");
String first = firstCurrency.getId();
String second = secondCurrency.getId();
int index = searchPairs(first, second);
Pair<Pair<String, String>, Double> selectedPair = conversionRates.get(index);
double rate = selectedPair.getValue();
return rate;
}
private int searchPairs(String first, String second) {
Pair<String, String> pairContainingRate = new Pair<>(first, second);
for (int index = 0; index < conversionRates.size(); index++) {
if (conversionRates.get(index).getKey().equals(pairContainingRate)) {
log.add("Successfully found target " + first + "/" + second);
return index;
}
}
log.add("Failed to find target " + first + "/" + second);
return -1;
}
@Override
public void add(Pair<Pair<String, String>, Double> newRate) {
log.add("Added new rate " + newRate);
conversionRates.add(newRate);
}
@Override
public void update(ArrayList<Pair<Pair<String, String>, Double>> newRates) {
log.add("Updated rates");
conversionRates.clear();
conversionRates.addAll(newRates);
}
@Override
public void remove(Pair<Pair<String, String>, Double> redundantRate) {
log.add("Removed rate " + redundantRate);
conversionRates.remove(redundantRate);
}
}
货币实施(我将只列出欧元,因为欧元完全相同):
我也想听听你对总体设计的看法。事实上,肯特·贝克(Kent Beck)在他关于TDD的书中就此事进行了研究 我没有读到最后,但我记得在某个时候,他有
货币
抽象类,总和
(或类似的东西)用于存储货币和银行
。银行负责货币汇率
我不确定创建像Dollar
和Euro
这样的类是否是一个好方法。也许坚持使用Money
并将货币保持在实例变量(KISS)中更容易。我想我希望货币完全由一个类处理(Bank
,ExchangeRate
或任何最适合你的东西)我会让Money
实例不可变。所有的计算都是由Sum
类进行的(或其他,重点是将逻辑从Money本身移开)
无论如何,也许你应该看看贝克的书。我很确定他设计得很好。我认为你应该从更简单的角度开始。我不明白为什么要使用货币接口,尤其是在这种情况下,因为我不明白你的实现将需要不同的提供者。每个
货币都用一个符号和该货币的一组汇率
public class Currency {
private final String symbol;
private final Set<ExchangeRate> rates = new HashSet<>();
public Currency(String symbol) {
this.symbol = symbol;
}
public BigDecimal convert(Currency currency, BigDecimal amount) {
return findExchangeRate(currency).getRate().multiply(amount).setScale(2, RoundingMode.HALF_DOWN);
}
public String getSymbol() {
return symbol;
}
public ExchangeRate findExchangeRate(Currency currency) {
for(ExchangeRate rate: rates) {
if ( rate.getCurrency().equals(currency)) {
return rate;
}
}
throw new IllegalArgumentException("Currency not found: " + currency);
}
public void setExchangeRate(ExchangeRate rate) {
if ( rates.contains(rate) ) rates.remove(rate);
rates.add(rate);
}
public boolean removeExchangeRate(ExchangeRate rate) {
return rates.remove(rate);
}
使用它非常简单
Currency usd = new Currency("USD");
Currency eur = new Currency("EUR");
usd.setExchangeRate(new ExchangeRate(eur, new BigDecimal("0.87540")));
eur.setExchangeRate(new ExchangeRate(usd, new BigDecimal("1.14233")));
BigDecimal myMoney = new BigDecimal("1000.00");
myMoney = usd.convert(eur, myMoney);
System.out.println("My Euros: " + myMoney);
myMoney = eur.convert(usd, myMoney);
System.out.println("My Dollars: " + myMoney);
请注意BigDecimal
的用法。由于浮点和双精度的错误,这类或类似的操作对于货币计算总是必需的。您可以转换为Long
或Integer
并自己跟踪比例,但您将执行与BigDecimal
类相同的操作 在考虑性能之前,您应该先考虑可读性。我不知道ArrayList可能包含什么。定义类、枚举,它们的名称清楚地显示它们所代表的内容。请注意,Java和Guava都不包括Pair类,因为它鼓励开发人员编写与您所拥有的代码类似的不可读代码e那里。第一对表示货币,双精度表示从第一个到第二个的实际汇率。然后定义并使用一个包含三个字段的CurrencyConversion类:CurrencySourceCurrency、CurrencyTargetCurrency、double conversionRate。它可读性更强。它可以包含实际计算转换的方法。它可以ontain是一种通过交换两种货币并反转汇率来创建反向货币转换的方法。它可以在构建时验证汇率的有效性。它可以有一个toString()方法,该方法返回对象的可读描述,等等。听起来不错。因此,我应该创建该类并将其注入每个货币对象并执行转换。但是,我应该如何处理汇率存储,或者如何正确执行汇率检测?提前和对于这些错误,我深表歉意,我仍然是新手。这要视情况而定。这是一个真正的应用程序,其中存储实际上意味着持久存储吗?如果是,请使用数据库。如果这是一个玩具项目,其中所有内容都在内存中,您可能应该使用一个映射,其中CurrencyPair包含源货币和目标货币,是不可变的,并定义适当的equals()和hashCode()方法。这将允许O(1)查阅。这并不是问题的答案。若要评论或要求作者澄清,请在其帖子下方留下评论。-@DavidMaze>我也想听听您对总体设计的看法。我非常感谢您的解释。我将尝试您建议的方法,看看这是否适合我的情况。这这不是一个真正的项目,这只是我正在做的一个个人项目。你的解决方案太棒了。我从来没有想过我可以这样做,谢谢你,先生!还有一个问题,让每种货币都有自己的汇率是一个好主意吗?把它们分开,这样我就可以更容易地更新它们,不是更好吗?不知道你从哪里开始以数据为基础,但如果您对其进行排序,则应具有多组货币。您将始终必须先找到一种货币,然后再找到另一种货币。如果您将所有货币放入一个集合,则基本上具有成对的汇率。如果这取决于您的真实世界模型。例如,一家银行可能支持某一组货币,而另一家银行可能支持另一组货币ght支持另一个。因此,您可以创建一个银行类,并在其中包含一组货币。@k-nicholas嗯。我已成功构建了自己的解决方案,将其用作模板。我的问题是:对于Set logi
package currency;
public class Euro implements ICurrency {
private String id;
private IConversionRate conversionRateFinder;
public Euro(String id, IConversionRate conversionRateBuilder) {
this.id = id;
this.conversionRateFinder = conversionRateBuilder;
}
@Override
public double convertTo(double amount, ICurrency newCurrency) {
double currentConversionRate = conversionRateFinder.find(this, newCurrency);
double newAmount = newCurrency.convert(amount,currentConversionRate);
return newAmount;
}
@Override
public double convert(double oldCurrencyAmount, double currentConversionRate) {
return oldCurrencyAmount*currentConversionRate;
}
public String getId() {
return id;
}
}
public class Currency {
private final String symbol;
private final Set<ExchangeRate> rates = new HashSet<>();
public Currency(String symbol) {
this.symbol = symbol;
}
public BigDecimal convert(Currency currency, BigDecimal amount) {
return findExchangeRate(currency).getRate().multiply(amount).setScale(2, RoundingMode.HALF_DOWN);
}
public String getSymbol() {
return symbol;
}
public ExchangeRate findExchangeRate(Currency currency) {
for(ExchangeRate rate: rates) {
if ( rate.getCurrency().equals(currency)) {
return rate;
}
}
throw new IllegalArgumentException("Currency not found: " + currency);
}
public void setExchangeRate(ExchangeRate rate) {
if ( rates.contains(rate) ) rates.remove(rate);
rates.add(rate);
}
public boolean removeExchangeRate(ExchangeRate rate) {
return rates.remove(rate);
}
public class ExchangeRate {
private final Currency currency;
private final BigDecimal rate;
public ExchangeRate(Currency currency, BigDecimal rate) {
this.currency = currency;
this.rate = rate;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((currency == null) ? 0 : currency.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ExchangeRate other = (ExchangeRate) obj;
if (currency == null) {
if (other.currency != null)
return false;
} else if (!currency.equals(other.currency))
return false;
return true;
}
Currency usd = new Currency("USD");
Currency eur = new Currency("EUR");
usd.setExchangeRate(new ExchangeRate(eur, new BigDecimal("0.87540")));
eur.setExchangeRate(new ExchangeRate(usd, new BigDecimal("1.14233")));
BigDecimal myMoney = new BigDecimal("1000.00");
myMoney = usd.convert(eur, myMoney);
System.out.println("My Euros: " + myMoney);
myMoney = eur.convert(usd, myMoney);
System.out.println("My Dollars: " + myMoney);