如何在Java中为大量资金建模

如何在Java中为大量资金建模,java,design-patterns,model,modeling,Java,Design Patterns,Model,Modeling,有人能推荐一个能为大量资金建模的类库或设计模式吗 我想它应该支持: 多种货币 表示小数位数的一种机制 数学(关于某些舍入约定(cfr ie)) 与字符串表示形式之间的序列化 也就是说,19.99美元可以序列化为“USD-2000-0000000 1999” (2表示小数位数)这似乎有帮助,但我没有这方面的经验:Joda money看起来也很有希望: (不知道生产准备得如何)我会检查项目中的模块(由Jean-Marie Dautelle编写) 根据您的具体需求,Stephen Colebou

有人能推荐一个能为大量资金建模的类库或设计模式吗

我想它应该支持:

  • 多种货币
  • 表示小数位数的一种机制
  • 数学(关于某些舍入约定(cfr ie))
  • 与字符串表示形式之间的序列化
也就是说,19.99美元可以序列化为“USD-2000-0000000 1999

(2表示小数位数)

这似乎有帮助,但我没有这方面的经验:

Joda money看起来也很有希望: (不知道生产准备得如何)

我会检查项目中的模块(由Jean-Marie Dautelle编写)


根据您的具体需求,Stephen Colebourne不久前开始了(“一个比JScience更专注的项目”)。但是目前还没有完整的版本(0.5版是一年前发布的)。

除了Paul所说的,还有Martin Fowler的。

JSR 354 JavaMoney应该成为Java 9的一部分。 查看更多细节

这个JSR应该是Joda Money的替代品,但目前只有Joda Money是稳定的,并在生产中经过测试

您还可以查看以下库:

  • JSScience-使用货币单位

  • (“需要alpha用户”)


这是一个使用Martin Fowler分析模式的java money类的完整示例:

package com.console.utils.value;

import com.console.core.exceptions.UnknownCurrencyCodeException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Currency;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Assert;
import static java.math.RoundingMode.HALF_UP;

/**
 *
 * @author farouka
 */
public class Money implements Serializable {

  /**
   * Why me
   */
  private static final int[] cents = new int[]{1, 10, 100, 1000};

  private BigDecimal amount;

  private Currency currency;

  //private MathContext DEFAULT_CONTEXT = new MathContext( 2, HALF_UP );

  private MathContext DEFAULT_CONTEXT = new MathContext( 10, RoundingMode.HALF_DOWN );

  public Money(long amount, Currency currency) {
    this.currency = currency;
    this.amount = BigDecimal.valueOf(amount, currency.getDefaultFractionDigits()); 
  }

  /**
   * Creates a currency object from the long value provided assuming the long value
   * represents the base currency in the least monetary unit. For eg, new Money(500, "GBP")
   * is assumed to mean 5.00 great british pounds
   * @param amount in base monetary unit
   * @param currCode
   * @throws com.console.core.exceptions.UnknownCurrencyCodeException
   */
  public Money(long amount, String currCode) throws UnknownCurrencyCodeException {
    this( amount, Currency.getInstance(currCode) );
  }

  /**
   * Construct an IMMUTABLE money object from a double. It is assumed that 
   * the whole part of the double is the Money with the fractional part representing
   * lowest denominator of the currency. For eg, new Money (50.99, "GBP") is assumed
   * to be 50 pounds and 99 pence.
   * PS. 89.788 will be truncated to 89.78 based on the defaultcurrencydigit of the currency
   * @param amount
   * @param curr
   */
  public Money(double amount, Currency curr) {
    this.currency = curr;
    BigDecimal bd = BigDecimal.valueOf( amount );
    this.amount = bd.setScale(centFactor(), HALF_UP);
  }

  private Money() {
  }

  /**
   * Construct an IMMUTABLE money object from a double. It is assumed that 
   * the whole part of the double is the Money with the fractional part representing
   * lowest denominator of the currency. For eg, new Money (50.99, "GBP") is assumed
   * to be 50 pounds and 99 pence.
   * PS. 89.788 will be truncated to 89.78 based on the defaultcurrencydigit of the currency
   * code supplied
   * @param amount
   * @param currCode iso 4217 currency code
   * @throws com.console.core.exceptions.UnknownCurrencyCodeException
   */
  public Money(double amount, String currCode) throws UnknownCurrencyCodeException {
    this.currency = Currency.getInstance(currCode);
    BigDecimal bd = BigDecimal.valueOf( amount );
    this.amount = bd.setScale( currency.getDefaultFractionDigits(), HALF_UP);
  }

  /**
   * Constructs an IMMUTABLE money from a BigDecimal. the BigDecimal provided is only scaled
   * to used the default digits in currency object represented by the sting parameter
   * @param bigDecimal
   * @param currCode ISO 4217 cuurency code
   * @throws com.console.core.exceptions.UnknownCurrencyCodeException
   */
  public Money(BigDecimal bigDecimal, String currCode ) throws UnknownCurrencyCodeException {    
    this.currency = Currency.getInstance(currCode);
    this.amount = bigDecimal.setScale( currency.getDefaultFractionDigits(), HALF_UP);
  }

  /**
   * Constructs an IMMUTABLE money from a BigDecimal. the BigDecimal provided is only scaled
   * to used the default digits in currency object represented by the sting parameter
   * @param multiply
   * @param currency
   */
  public Money(BigDecimal bigDecimal, Currency currency) {
    this.currency = currency;   
    this.amount = bigDecimal.setScale( currency.getDefaultFractionDigits(), HALF_UP);
  }

//  public  boolean assertSameCurrencyAs(Money arg) {
//    return  this.currency.getCurrencyCode().equals(arg.currency.getCurrencyCode());
//  }
//  
  public boolean assertSameCurrencyAs(Money money) throws IncompatibleCurrencyException{
  if ( this.currency == null ) {
   throw new IncompatibleCurrencyException( "currency.invalid" );
  }
  if ( money == null ) {
   throw new IncompatibleCurrencyException( "currency.invalid" );
  }
    Assert.assertEquals("money math mismatch", currency, money.currency);
    return true;
  }

  private int centFactor() {
    return cents[ getCurrency().getDefaultFractionDigits() ];
  }

  public BigDecimal amount() {
    return amount;
  }

  public long amountAsLong(){
    return amount.unscaledValue().longValue();
  }

  public Currency getCurrency() {
    return currency;
  }
//    common currencies
  public static Money dollars(double amount) {
    Money result = null;
    try {
      result = new Money(amount, "USD");
    } catch (UnknownCurrencyCodeException ex) {
      Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }

  public static Money dollars(long amount) {
    Money result = null;
    try {
      result = new Money(amount, "USD");
    } catch (UnknownCurrencyCodeException ex) {
      Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }

  public static Money pounds(double amount) {
    Money result = null;
    try {
      result = new Money(amount, "GBP");
    } catch (UnknownCurrencyCodeException ex) {
      Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }

  public static Money pounds(long amount) {
    Money result = null;
    try {
      result = new Money(amount, "GBP");
    } catch (UnknownCurrencyCodeException ex) {
      Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }

  public static Money pounds(BigDecimal amount) {
    Money result = null;
    try {
      result = new Money(amount, "GBP");
    } catch (UnknownCurrencyCodeException ex) {
      Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }


  @Override
  public int hashCode() {
    int hash = (int) ( amount.hashCode() ^ (amount.hashCode() >>> 32) );
    return hash;
  }

  @Override
  public boolean equals(Object other) {
    return (other instanceof Money && equals((Money) other));
  }

  public boolean equals(Money other) {
    return ( currency.equals(other.currency) && (amount.equals(other.amount)) );
  }

  public Money add(Money other) throws Exception{
    assertSameCurrencyAs( other );
    return newMoney(amount.add(other.amount, DEFAULT_CONTEXT));
  }

  private int compareTo(Money money) throws Exception {
    assertSameCurrencyAs( money );
    return amount.compareTo( money.amount ); 
  }

  public Money multiply(BigDecimal amount) {
    return new Money( this.amount().multiply(amount, DEFAULT_CONTEXT), currency);
  }

  public Money multiply( BigDecimal amount, RoundingMode roundingMode ) {
    MathContext ct = new MathContext( currency.getDefaultFractionDigits(), roundingMode );
    return new Money( amount().multiply(amount, ct), currency);
  }

  private Money newMoney(BigDecimal amount) {
    return new Money( amount, this.currency );
  }

  public Money multiply(double amount) {
    return multiply( new BigDecimal( amount ) );
  }

  public Money subtract(Money other) throws Exception {
    assertSameCurrencyAs(other);
    return newMoney( amount.subtract(other.amount, DEFAULT_CONTEXT) );
  }

  public int compareTo(Object other) throws Exception {
    return compareTo((Money) other);
  }

  public boolean greaterThan(Money other)throws Exception {
    return (compareTo(other) > 0);
  }

//  public Money[] allocate(int n){
//    Money lowResult = newMoney( amount.unscaledValue().longValue()/n );
//    Money highResult = newMoney(lowResult.amount + 1);
//    Money[] results = new Money[n];  
//    int remainder = (int) amount % n;
//    
//    for(int i = 0; i < remainder; i++)results[i] = highResult;
//    for(int i = 0; i < n; i++) results[i] = lowResult;
//    
//    return results;
//  }
//  
//  public Money[]allocate(long[] ratios){
//    long total = 0;
//    for (int i = 0; i < ratios.length; i++) {
//      total += ratios[i];
//    }
//    long remainder = amount;
//    Money[] results = new Money[ratios.length];
//    for (int i = 0; i < results.length; i++) {
//      results[i] = newMoney(amount * ratios[i]/total);
//      remainder -= results[i].amount;
//    }
//    for (int i = 0; i < remainder; i++) {
//      results[i].amount++;
//    }
//    return results;
//
//  }

  public Money divideByNumber( double divisor){
     BigDecimal div = BigDecimal.valueOf( divisor );
     BigDecimal ans = this.amount.divide(div, DEFAULT_CONTEXT);
     return new Money(ans, this.currency);
  }

  public int getQuotient( Money divisor ){
    BigDecimal ans = this.amount.divide(divisor.amount, RoundingMode.DOWN);
    return ans.intValue();
  }

  /**
   * divides toe moneys and return the quotient and Remainder this method has been customised,
   * for my money transfer needs...sorry
   * @param divisor
   * @return
   */
  public int[] getQuotientandRemainder(Money divisor){
    int[] ans = new int[2];
    BigDecimal[] bdArr = this.amount.divideAndRemainder(divisor.amount, DEFAULT_CONTEXT);
    BigDecimal quo = bdArr[0];
    BigDecimal rem = bdArr[1];
    ans[0] = quo.intValue();
    if( rem.compareTo(BigDecimal.ZERO) == 0 ){
      ans[1] =0;
    }else{
      ans[1] = 1;
    }
    return ans;
  }

 public String toFormattedString() {
  NumberFormat nf = NumberFormat.getCurrencyInstance();
  nf.setCurrency( currency );
  nf.setGroupingUsed( true );
  nf.setMaximumFractionDigits( currency.getDefaultFractionDigits() );
  return nf.format( this.amount.doubleValue() );
 }

 /**
  * Returns the ISO-4217 currency code of the currency
  * attached to this money.
  * 
  * @return The ISO-4217 currency code.
  */
 public String getCurrencyCode() {
  return currency.getCurrencyCode();
 }

  @Override
  public String toString() {
      return amount.toString();
  }

 /**
  * Returns the precision for this money. The precision is the total number
  * of digits that the value can represent. This includes the integer part.
  * So, 18 would be able to represent:
  * 

* 1234567890.12345678
  * 

* 1234567890123456.78
  * 

* 123456789012345678
  * 

* 0.123456789012345678
  * 
  * @return The precision. 
  */ 
 public int precision() {
  return amount.precision();
 }

 /**
  * Returns the 'scale' for this money. The scale is the number of 
  * digits that are moved to the fractional part, assuming that all
  * digits are represented by a single integer value. For example:
  * 

* If: 123456789012345678 has scaling 2, it would be :
  * 

* 1234567890123456.78
  * 
  * @return The scale value. 
  */
 public int scale() {
  return amount.scale();
 }

 /**
  * Returns the sign for the money (negative or positive).
  * -1 if negative, 0 if 0.00 (zero), 1 if positive.
  * 
  * @return The sign of the money. 
  */ 
 public int signum() {
  return amount.signum();
 }
}


And here is the UnknownCurrencyCodeException class
package com.console.lib.utils;

/**
 * An exception which is raised when an unrecognised currency code is passed to the
 * Currency class.
 *
 * @author Farouk Alhassan
 * @see Currency
 */
public class UnknownCurrencyCodeException extends Exception {

    // Reason for exception
    private String reason = null;

    /**
     * Create a new unknown currency code exception.
     *
     * @param reason for the exception
     */
    public UnknownCurrencyCodeException(String reason) {
        this.reason = reason;
    }

    /**
     * Return the reason this exception was raised.
     *
     * @return the reason why the string isn't a valid currency code
     */
    public String getReason() {
        return reason;
    }

    /**
     * Convert the exception to a string
     *
     * @return string version of the exception
     */
    public String toString() {
 return getReason();
    }
}
package com.console.utils.value;
导入com.console.core.exceptions.UnknownCurrencyCodeException;
导入java.io.Serializable;
导入java.math.BigDecimal;
导入java.math.MathContext;
导入java.math.RoundingMode;
导入java.text.NumberFormat;
导入java.util.Currency;
导入java.util.logging.Level;
导入java.util.logging.Logger;
导入org.junit.Assert;
导入静态java.math.RoundingMode.HALF_UP;
/**
*
*@作者法鲁卡
*/
公共类货币实现可序列化{
/**
*为什么是我
*/
私有静态最终整数[]分=新整数[]{1,10,100,1000};
私人金额;
私人货币;
//private MathContext DEFAULT_CONTEXT=新MathContext(2,一半以上);
private MathContext DEFAULT_CONTEXT=新MathContext(10,RoundingMode.HALF_DOWN);
公共资金(长期金额、货币){
这个。货币=货币;
this.amount=BigDecimal.valueOf(amount,currency.getDefaultFractionDigits());
}
/**
*根据提供的long值(假定为long值)创建货币对象
*表示以最小货币单位表示的基础货币。例如,新货币(500,“英镑”)
*假设是指5.00英镑
*@param以基本货币单位表示的金额
*@param currCode
*@throws com.console.core.exceptions.UnknownCurrencyCodeException
*/
公共资金(长金额,字符串currCode)抛出UnknownCurrencyCodeException{
这(金额、货币.getInstance(currCode));
}
/**
*从double构造一个不可变的money对象
*双精度的整个部分是货币,小数部分代表
*货币的最低分母。例如,假设新货币(50.99英镑)
*50磅99便士。
*根据货币的DefaultCurrencyDigital,PS.89.788将被截断为89.78
*@param金额
*@param curr
*/
公共资金(双倍金额,货币){
该货币=货币;
BigDecimal bd=BigDecimal.valueOf(金额);
this.amount=bd.setScale(centFactor(),向上减半);
}
私人资金{
}
/**
*从double构造一个不可变的money对象
*双精度的整个部分是货币,小数部分代表
*货币的最低分母。例如,假设新货币(50.99英镑)
*50磅99便士。
*根据货币的DefaultCurrencyDigital,PS.89.788将被截断为89.78
*提供的代码
*@param金额
*@param currCode iso 4217货币代码
*@throws com.console.core.exceptions.UnknownCurrencyCodeException
*/
公共资金(双倍金额,字符串currCode)引发UnknownCurrencyCodeException{
this.currency=currency.getInstance(currCode);
BigDecimal bd=BigDecimal.valueOf(金额);
this.amount=bd.setScale(currency.getDefaultFractionDigits(),上半个);
}
/**
*从BigDecimal构造不可变货币。提供的BigDecimal仅按比例缩放
*使用sting参数表示的货币对象中的默认数字
*@param bigDecimal
*@param currCode ISO 4217 CUURENCE代码
*@throws com.console.core.exceptions.UnknownCurrencyCodeException
*/
公共货币(BigDecimal BigDecimal,字符串currCode)抛出UnknownCurrencyCodeException{
this.currency=currency.getInstance(currCode);
this.amount=bigDecimal.setScale(currency.getDefaultFractionDigits(),向上减半);
}
/**
*从BigDecimal构造不可变货币。提供的BigDecimal仅按比例缩放
*使用sting参数表示的货币对象中的默认数字
*@param乘法
*@param货币
*/
公共货币(BigDecimal BigDecimal,货币){
这个。货币=货币;
this.amount=bigDecimal.setScale(currency.getDefaultFractionDigits(),向上减半);
}
//公共布尔资产samecurrencyas(货币参数){
//返回此.currency.getCurrencyCode().equals(arg.currency.getCurrencyCode());
//  }
//  
公共布尔资产Samecurrencyas(Money Money)抛出不兼容的CurrencyException{
如果(this.currency==null){
抛出新的不兼容CurrencyException(“currency.invalid”);
}
if(money==null){
抛出新的不兼容CurrencyException(“currency.invalid”);
}
Assert.assertEquals(“货币数学不匹配”,货币,货币,货币);
返回true;