Не делайте этого!
Как и сейчас, ваш calculateTax
метод подобен контейнеру для четырех реальных calculateTax
методов,по одному для каждой из 3 стран и по одному для недействительного случая.Любой другой метод, который вы делаете в этом направлении, будет таким.Следуя этой схеме, вы получите множество переключателей (проверяющих один и тот же набор наблюдений) во многих методах, где каждый случай содержит специфику наблюдения.Но это точно полиморфизм, гораздо лучше!
Подобные паттерны являются очень убедительным свидетельством того, что вы не пользуетесь ориентацией объекта, и исключаете любые другие причины для этого, вам определенно следует это сделать.В конце концов, это Java, и это все, что нужно.
Создайте интерфейс наподобие TaxPolicy
:
interface TaxPolicy {
BigDecimal calculateTaxFor(BigDecimal saleAmount);
}
Создайте класс, который его реализует:
class NationalSalesTaxPolicy implements TaxPolicy {
String countryName;
BigDecimal salesTaxRate;
// Insert constructor, getters, setters, etc. here
BigDecimal calculateTaxFor(BigDecimal saleAmount) {
return saleAmount.multiply(salesTaxRate);
}
}
Затем создайте объекты этого класса, по одному на страну, которую вы хотите поддерживать.Мы можем заключить этот список в новый класс NationalSalesTaxCalculator
, который будет нашим универсальным средством расчета налога с продаж для любой страны:
class NationalSalesTaxCalculator {
static Map<String, NationalSalesTaxPolicy> SUPPORTED_COUNTRIES = Stream.of(
new NationalSalesTaxPolicy("POLAND", "0.23"),
new NationalSalesTaxPolicy("AUSTRIA", "0.20"),
new NationalSalesTaxPolicy("CYPRUS", "0.19")
).collect(Collectors.toMap(NationalSalesTaxPolicy::getCountryName, c -> c));
BigDecimal calculateTaxFor(String countryName, BigDecimal saleAmount) {
NationalSalesTaxPolicy country = SUPPORTED_COUNTRIES.get(countryName);
if (country == null) throw new UnsupportedOperationException("Country not supported");
return country.calculateTaxFor(saleAmount);
}
}
И мы можем использовать его следующим образом:
NationalSalesTaxCalculator calculator = new NationalSalesTaxCalculator();
BigDecimal salesTax = calculator.calculateTaxFor("AUSTRIA", new BigDecimal("100"));
System.out.println(salesTax);
Некоторые ключевые преимущества, на которые следует обратить внимание:
- Если вы добавляете новую страну, которую хотите поддерживать, вам просто нужно создать новый объект.Все методы, которые могут нуждаться в этом объекте, автоматически «делают правильные вещи», без необходимости вручную находить их все, чтобы добавить новые операторы if.
- У вас есть возможность адаптировать функциональность по мере необходимости.Например, где я живу (Онтарио, Канада), налог с продаж не взимается за продукты.Таким образом, я мог бы создать свой собственный подкласс
NationalSalesTaxPolicy
, который имеет более тонкую логику. Есть даже больше возможностей для улучшения.Обратите внимание, что NationalSalesTaxCalculator.calculateTaxFor()
содержит некоторый код, относящийся к обработке неподдерживаемой страны.Если мы добавим новые операции к этому классу, каждому методу потребуются одинаковые проверки на нуль и выдача ошибок.
Вместо этого это может быть реорганизовано в использование шаблона нулевого объекта .Вы реализуете UnsuppoertedTaxPolicy
, который является классом, который реализует все методы интерфейса, генерируя исключения.Вот так:
class UnsuppoertedTaxPolicy implements TaxPolicy {
public BigDecimal calculateTaxFor(BigDecimal saleAmount) {
throw new UnsupportedOperationException("Country not supported");
}
}
Затем вы можете сделать
TaxPolicy countryTaxPolicy = Optional
.ofNullable(SUPPORTED_COUNTRIES.get(countryName))
.orElse(UNSUPPORTED_COUNTRY);
return countryTaxPolicy.calculateTaxFor(saleAmount);
Это «централизует» все ваши исключения в одном месте, что облегчает их поиск (таким образом, легче устанавливать точки останова на), легче редактировать (в случае, если вы когда-либо захотите перенести типы исключений или изменить сообщение), и он удаляет остальную часть кода, так что ему нужно только беспокоиться о счастливом случае.
Вот рабочая демонстрация: https://repl.it/@alexandermomchilov/Polymorphism-over-ifswitch