Чтобы округлить до 5 центов, вы можете:
private static final BigDecimal TWENTY = new BigDecimal(20);
public BigDecimal roundUp5(BigDecimal value)
{
// The final setScale(2) is just to add the ".00" when you display
return value.multiply(TWENTY).setScale(0, BigDecimal.ROUND_CEILING).divide(TWENTY).setScale(2);
}
// You don't need this one as you're using BigDecimal; it's just for completeness!
public double roundUp5(double value)
{
return Math.ceil(value * 20) / 20;
}
С радостью изложить свои мысли в виде обзора кода.
Во-первых, почему раздельные convertOrders
и calculate
методы? Создание Map
в convertOrders
, а затем итерация по нему и повторная вставка в calculate
является грязным и запутанным.
Гораздо лучше было бы иметь convertOrders
call calculate
как часть его итерация при чтении каждой строки и, следовательно, правильное создание каждой записи Map
.
У вас есть несколько повторяющихся выражений, которые я бы преобразовал в методы для удобочитаемости и перспективы будущих бизнес-изменений, поэтому:
public boolean isBasic(String itemName) {
String name = itemName.toLowerCase();
return !( name.contains("chocolate") || name.contains("book") || name.contains("pill") );
}
public boolean isImport(String itemName) {
String name = itemName.toLowerCase();
return name.contains("import");
}
В ваших if
выражениях в calculate
в настоящее время выполняются как:
if (isBasic(k) && isImport(k)) {
// Basic and Import
} else {
if (isBasic(k)) {
}
if (isImport(k)) {
}
}
Этот поток немного сбивает с толку, и было бы неудобно проводить рефакторинг, если в будущем вам понадобится также обрабатывать неосновные c и не импортированные элементы.
Существует некоторый общий код между всеми этими if
блоками, поэтому я бы переместил его после if
- что оставило бы оператор log и установка коэффициента умножения единственные строки, оставшиеся в блоках if
.
Пока мы говорим о коэффициентах умножения, у вас есть те, которые установлены на значения меньше чем 1 (например, 0,15) ... бит, то вы добавляете v
к результату! Было бы намного лучше, чтобы коэффициенты были> 1, и поэтому избавились бы от add(v)
.
Кстати, также предпочтительно инициализировать BigDecimal
в String
s, а не в буквальное значение float
или double
, так как они могут получить ошибки округления (например, 0,15, поскольку число с плавающей запятой может фактически быть 0,1499999999), тогда как BigDecimal
, созданное из String
, является точным.
Предполагая, что вы счастливы покинуть регистрация окончательной суммы до конца, что приводит нас к чему-то вроде:
static final BigDecimal BASICSALES = new BigDecimal("1.10");
static final BigDecimal IMPORTSALES = new BigDecimal("1.05");
static final BigDecimal BASICANDIMPORTSALES = new BigDecimal("1.15");
for(String orderLine : order) {
String[] splitOrder = orderLine.split("at\\ ");
String name = splitOrder[0];
BigDecimal value = new BigDecimal(splitOrder[1]));
BigDecimal taxRate;
if(isBasic(name)) {
if (isImport(name)) {
taxRate = BASICANDIMPORTSALES;
System.out.println("Both " + k);
} else {
taxRate = BASICSALES;
System.out.println("Basic " + k);
}
} else {
if (isImport(name)) {
taxRate = IMPORTSALES;
System.out.println("Import " + k);
} else {
taxRate = BigDecimal.ONE;
System.out.println("Neither " + k);
}
}
BigDecimal amountWithTax = amount.multiply(taxRate).round(new MathContext(4));
System.out.println(name + " tax: $" + amountWithTax);
newOrder.put(name, amountWithTax);
}