用于存储货币汇率的设计

时间:2018-08-12 13:18:26

标签: java oop

我已经开始了一个信贷管理的新项目,现在我不得不处理货币兑换。 (例如欧元->美元),因此我进行了一些头脑风暴,并得到了以下提示:

  • 货币是抽象的,每种新货币都是接口的实现
  • 构建了一个汇率类,该汇率类存储对的数组列表(我想到的是欧元,美元,1.14->欧元对美元的汇率是1.14)并具有以下功能:货币汇率存储,用于正确汇率的查找器(该函数获取当前货币和新货币作为参数,并在列表中执行搜索,返回适当的货币),以及确定工具索引的实用程序函数对。

现在,我正在考虑性能。我相信我的设计包含一些冗余:如果我有欧元兑美元,我就必须有美元兑欧元。此外,对于大量条目(假设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;
    }
}

我也想听听您的一般设计。

2 个答案:

答案 0 :(得分:1)

实际上,肯特·贝克(Kent Beck)在其有关TDD的书中致力于解决这一问题。

我还没有读完它,但是我记得在某个时候他有Money抽象类,Sum(或类似的东西)来存钱和Bank。银行负责货币汇率。

我不确定创建类似DollarEuro的类是否是一种好方法。仅仅坚持使用Money并将货币保留在实例变量(KISS)中可能会更加容易。我想我希望一种类别(BankExchangeRate或最适合您的任何一种)完全处理货币,并使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 hashCodeequals,以便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的用法。由于浮点数和双精度数的准确性失败,因此对于货币计算始终必须使用这种或类似的方法。您可以转换为LongInteger并自己跟踪规模,但是您将做与BigDecimal类相同的事情。