我已经开始了一个信贷管理的新项目,现在我不得不处理货币兑换。 (例如欧元->美元),因此我进行了一些头脑风暴,并得到了以下提示:
现在,我正在考虑性能。我相信我的设计包含一些冗余:如果我有欧元兑美元,我就必须有美元兑欧元。此外,对于大量条目(假设100k),我的存储(pair
现在,输入代码:
转化率类别:
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);
}
}
ConversionRates接口:
package currency;
import javafx.util.Pair;
import java.util.ArrayList;
public interface IConversionRate {
double find(ICurrency euro, ICurrency newCurrency);
void add(Pair<Pair<String,String>,Double> newRate);
void update(ArrayList< Pair<Pair<String,String>,Double> > newRates);
void remove(Pair<Pair<String,String>,Double> redundantRate);
}
货币界面:
package currency;
public interface ICurrency {
double convertTo(double amount, ICurrency newCurrency);
double convert(double oldCurrencyAmount, double conversionRateToNewCurrency);
String getId();
}
货币实现(由于欧元完全相同,因此我只列出欧元):
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;
}
}
我也想听听您的一般设计。
答案 0 :(得分:1)
实际上,肯特·贝克(Kent Beck)在其有关TDD的书中致力于解决这一问题。
我还没有读完它,但是我记得在某个时候他有Money
抽象类,Sum
(或类似的东西)来存钱和Bank
。银行负责货币汇率。
我不确定创建类似Dollar
和Euro
的类是否是一种好方法。仅仅坚持使用Money
并将货币保留在实例变量(KISS)中可能会更加容易。我想我希望一种类别(Bank
,ExchangeRate
或最适合您的任何一种)完全处理货币,并使Money
实例不可变。而且所有的计算都是由Sum
类进行的(或其他任何事情,关键是要使逻辑脱离金钱本身)。
无论如何,也许您应该看看贝克的书。我很确定他的设计很好。
答案 1 :(得分:1)
我认为您应该更简单一些。我看不到使用货币接口的原因,特别是在这种情况下,因为我看不到您的实现将如何需要不同的提供程序。每个Currency
都用一个符号和该货币的一组汇率表示。
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);
}
ExchangeRate
是所交换货币的汇率,因此您不必每次都重复该货币的1.0。不要忘记@Override
hashCode
和equals
,以便Set
逻辑将正常工作。
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);
请注意BigDecimal
的用法。由于浮点数和双精度数的准确性失败,因此对于货币计算始终必须使用这种或类似的方法。您可以转换为Long
或Integer
并自己跟踪规模,但是您将做与BigDecimal
类相同的事情。