Skip to content

Instantly share code, notes, and snippets.

@forax
Created August 20, 2020 21:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save forax/80da75b7e31c1cacaf029df928ab78e7 to your computer and use it in GitHub Desktop.
Save forax/80da75b7e31c1cacaf029df928ab78e7 to your computer and use it in GitHub Desktop.
Henry refacting: sealed interface + records
/*
* Copyright 2019-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pro.tremblay.core;
import javax.annotation.Nonnull;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
import static pro.tremblay.core.Percentage.pct;
public record Amount(BigDecimal value) implements Numeric<Amount> {
private static final Amount ZERO = new Amount(BigDecimal.ZERO);
public static Amount zero() {
return ZERO;
}
public static Amount amnt(double value) {
return new Amount(BigDecimal.valueOf(value));
}
public static Amount amnt(long value) {
return new Amount(BigDecimal.valueOf(value));
}
public static Amount amnt(@Nonnull BigDecimal value) {
return new Amount(Objects.requireNonNull(value));
}
public static Amount amnt(@Nonnull String value) {
return new Amount(new BigDecimal(Objects.requireNonNull(value)));
}
public Amount {
// To simplify, we consider everything is in the same currency so all amounts have a precision of 2
value = value.setScale(2, RoundingMode.HALF_UP);
}
@Override
public String toString() {
return value.toPlainString() + "$";
}
@Override
public Amount fromValue(@Nonnull BigDecimal newValue) {
return new Amount(newValue);
}
public Amount multiply(Quantity quantity) {
return new Amount(value.multiply(quantity.value()));
}
public Percentage asRatioOf(Amount initialValue) {
return pct(value.subtract(initialValue.value)
.divide(initialValue.value, 10, RoundingMode.HALF_UP)
.multiply(Percentage.hundred().value()));
}
}
/*
* Copyright 2019-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pro.tremblay.core;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.ThreadSafe;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
@ThreadSafe
public sealed interface Numeric<T extends Numeric<T>>
permits Amount, Percentage, Quantity {
BigDecimal value();
T fromValue(@Nonnull BigDecimal newValue);
default T add(@Nonnull T numeric) {
return fromValue(value().add(numeric.value()));
}
default T substract(@Nonnull T numeric) {
return fromValue(value().subtract(numeric.value()));
}
default boolean isZero() {
return value().signum() == 0;
}
default T negate() {
return fromValue(value().negate());
}
/**
* Scale this numerator to another denominator. E.g. if this is "3" on "4" ("from" param)
* and we want to scale to "8" ("to" param), we expect 3 x 8 / 4 = 6 as a result.
*
* @param from denominator from which to start
* @param to denominator to go to
* @return the numerator scaling to another denominator
*/
default T scale(int from, int to) {
return fromValue(value().multiply(BigDecimal.valueOf(to))
.divide(BigDecimal.valueOf(from), 2, RoundingMode.HALF_UP));
}
}
/*
* Copyright 2019-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pro.tremblay.core;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.ThreadSafe;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
@ThreadSafe
public record Percentage(BigDecimal value) implements Numeric<Percentage> {
private static final Percentage HUNDRED = pct("100");
private static final Percentage ZERO = pct("0");
public static Percentage hundred() {
return HUNDRED;
}
public static Percentage zero() {
return ZERO;
}
public static Percentage pct(long value) {
return new Percentage(BigDecimal.valueOf(value));
}
public static Percentage pct(@Nonnull String value) {
return new Percentage(new BigDecimal(Objects.requireNonNull(value)));
}
public static Percentage pct(@Nonnull BigDecimal value) {
return new Percentage(Objects.requireNonNull(value));
}
public Percentage {
value = value.setScale(2, RoundingMode.HALF_UP);
}
@Override
public String toString() {
return value.toPlainString() + "%";
}
@Override
public Percentage fromValue(@Nonnull BigDecimal newValue) {
return new Percentage(newValue);
}
}
/*
* Copyright 2019-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pro.tremblay.core;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.ThreadSafe;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
@ThreadSafe
public record Quantity(BigDecimal value) implements Numeric<Quantity> {
private static final Quantity ZERO = new Quantity(BigDecimal.ZERO);
private static final Quantity TEN = qty(10);
public static Quantity zero() {
return ZERO;
}
public static Quantity ten() {
return TEN;
}
public static Quantity qty(long value) {
return new Quantity(BigDecimal.valueOf(value));
}
public static Quantity qty(@Nonnull BigDecimal value) {
return new Quantity(Objects.requireNonNull(value));
}
public Quantity {
// To simplify, we consider everything is in the same currency so all amounts have a precision of 2
value = value.setScale(0, RoundingMode.HALF_UP);
}
@Override
public String toString() {
return value.toPlainString();
}
@Override
public Quantity fromValue(@Nonnull BigDecimal newValue) {
return new Quantity(newValue);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment