Last active
August 17, 2023 02:47
-
-
Save seolys/a2af9400b94f777410906ffcbc286a50 to your computer and use it in GitHub Desktop.
DistributedAmountMap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.math.BigDecimal; | |
import java.math.RoundingMode; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
public abstract class DistributedAmountMap<T> { | |
protected abstract Long getId(T item); | |
protected final Map<Long, BigDecimal> getMap(final List<T> list, final BigDecimal distributeAmount) { | |
var remainingAmount = distributeAmount; | |
final var distributedAmountMap = new HashMap<Long, BigDecimal>(); | |
final var totalCount = BigDecimal.valueOf(list.size()); | |
for (final var instance : list) { | |
final var id = getId(instance); | |
// 각 항목에 할당될 금액 계산 | |
final var distributedAmount = distributeAmount.divide(totalCount, 0, RoundingMode.DOWN); | |
distributedAmountMap.put(id, distributedAmount); | |
remainingAmount = remainingAmount.subtract(distributedAmount); | |
} | |
if (!list.isEmpty()) { | |
final Long firstId = getId(list.get(0)); | |
final BigDecimal firstDistributedAmount = distributedAmountMap.get(firstId); | |
distributedAmountMap.put(firstId, firstDistributedAmount.add(remainingAmount)); | |
} | |
return distributedAmountMap; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.junit.jupiter.api.BeforeEach; | |
import org.junit.jupiter.api.Test; | |
import java.math.BigDecimal; | |
import java.util.List; | |
import java.util.Map; | |
import static org.assertj.core.api.Assertions.assertThat; | |
public class DistributedAmountMapTest { | |
private DistributedAmountMap<DummyItem> distributedAmountMap; | |
@BeforeEach | |
public void setUp() { | |
distributedAmountMap = new DistributedAmountMap<DummyItem>() { | |
@Override | |
protected Long getId(final DummyItem item) { | |
return item.getId(); | |
} | |
}; | |
} | |
@Test | |
void test1() { | |
final List<DummyItem> itemList = List.of( | |
new DummyItem(1L, new BigDecimal("100.00")), | |
new DummyItem(2L, new BigDecimal("100.00")), | |
new DummyItem(3L, new BigDecimal("100.00")) | |
); | |
final BigDecimal distributeAmount = new BigDecimal("1000"); | |
final Map<Long, BigDecimal> resultMap = distributedAmountMap.getMap(itemList, distributeAmount); | |
assertThat(resultMap) | |
.containsEntry(1L, new BigDecimal("334")) | |
.containsEntry(2L, new BigDecimal("333")) | |
.containsEntry(3L, new BigDecimal("333")) | |
.hasSize(3); | |
} | |
@Test | |
void test2() { | |
final List<DummyItem> itemList = List.of( | |
new DummyItem(1L, new BigDecimal("100.00")), | |
new DummyItem(2L, new BigDecimal("150.00")), | |
new DummyItem(3L, new BigDecimal("200.00")) | |
); | |
final BigDecimal distributeAmount = new BigDecimal("450"); | |
final Map<Long, BigDecimal> resultMap = distributedAmountMap.getMap(itemList, distributeAmount); | |
assertThat(resultMap) | |
.containsEntry(1L, new BigDecimal("150")) | |
.containsEntry(2L, new BigDecimal("150")) | |
.containsEntry(3L, new BigDecimal("150")) | |
.hasSize(3); | |
} | |
// DummyItem 클래스 정의 | |
private static class DummyItem { | |
private final Long id; | |
private final BigDecimal amount; | |
public DummyItem(final Long id, final BigDecimal amount) { | |
this.id = id; | |
this.amount = amount; | |
} | |
public Long getId() { | |
return id; | |
} | |
public BigDecimal getAmount() { | |
return amount; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.math.BigDecimal; | |
import java.math.RoundingMode; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
public abstract class RateDistributedAmountMap<T> { | |
protected final Map<Long, BigDecimal> getMap(final List<T> list, final BigDecimal distributeAmount) { | |
final var totalDistributionRateBaseAmount = getTotalDistributionRateBaseAmount(list); | |
final var rateMap = getRateMap(list, totalDistributionRateBaseAmount); | |
return getDistributeMap(list, distributeAmount, rateMap); | |
} | |
private BigDecimal getTotalDistributionRateBaseAmount(final List<T> list) { | |
return list.stream() | |
.map(this::getDistributionRateBaseAmount) | |
.reduce(BigDecimal.ZERO, BigDecimal::add); | |
} | |
protected abstract Long getId(T item); | |
protected abstract BigDecimal getDistributionRateBaseAmount(T item); | |
private Map<Long, BigDecimal> getDistributeMap( | |
final List<T> list, | |
final BigDecimal distributeAmount, | |
final HashMap<Long, BigDecimal> rateMap | |
) { | |
var remainingAmount = distributeAmount; | |
final var distributedAmountMap = new HashMap<Long, BigDecimal>(); | |
for (final var instance : list) { | |
final var id = getId(instance); | |
final var rate = rateMap.get(id); | |
final var distributedAmount = distributeAmount.multiply(rate).setScale(0, RoundingMode.DOWN); | |
distributedAmountMap.put(id, distributedAmount); | |
remainingAmount = remainingAmount.subtract(distributedAmount); | |
} | |
if (!list.isEmpty()) { | |
final Long firstId = getId(list.get(0)); | |
final BigDecimal firstDistributedAmount = distributedAmountMap.get(firstId); | |
distributedAmountMap.put(firstId, firstDistributedAmount.add(remainingAmount)); | |
} | |
return distributedAmountMap; | |
} | |
private HashMap<Long, BigDecimal> getRateMap(final List<T> list, final BigDecimal totalDistributionRateBaseAmount) { | |
final var rateMap = new HashMap<Long, BigDecimal>(); | |
for (final var instance : list) { | |
final var id = getId(instance); | |
final var amount = getDistributionRateBaseAmount(instance); | |
final BigDecimal rate = amount.divide(totalDistributionRateBaseAmount, 16, RoundingMode.HALF_UP); | |
rateMap.put(id, rate); | |
} | |
return rateMap; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.math.BigDecimal; | |
import java.math.RoundingMode; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
public interface RateDistributedAmountMapFactory<T> { | |
Long getId(T item); | |
Money getDistributionRateBaseAmount(T item); | |
default Map<Long, Money> createMap(final List<T> list, final Money totalDistributeAmount) { | |
final var totalDistributionRateBaseAmount = getTotalDistributionRateBaseAmount(list); | |
final var rateMap = getRateMap(list, totalDistributionRateBaseAmount); | |
return getDistributeMap(list, totalDistributeAmount, rateMap); | |
} | |
private Money getTotalDistributionRateBaseAmount(final List<T> list) { | |
return list.stream() | |
.map(this::getDistributionRateBaseAmount) | |
.reduce(Money.ZERO, Money::add); | |
} | |
private Map<Long, Money> getDistributeMap( | |
final List<T> list, | |
final Money distributeAmount, | |
final HashMap<Long, BigDecimal> rateMap | |
) { | |
var remainingAmount = distributeAmount; | |
final var distributedAmountMap = new HashMap<Long, Money>(); | |
for (final var instance : list) { | |
final var id = getId(instance); | |
final var rate = rateMap.get(id); | |
final var distributedAmount = distributeAmount.multiply(rate) | |
.setScale(0, RoundingMode.DOWN); | |
distributedAmountMap.put(id, distributedAmount); | |
remainingAmount = remainingAmount.subtract(distributedAmount); | |
} | |
if (!list.isEmpty()) { | |
final Long firstId = getId(list.get(0)); | |
final var firstDistributedAmount = distributedAmountMap.get(firstId); | |
distributedAmountMap.put(firstId, firstDistributedAmount.add(remainingAmount)); | |
} | |
return distributedAmountMap; | |
} | |
private HashMap<Long, BigDecimal> getRateMap( | |
final List<T> list, | |
final Money totalDistributionRateBaseAmount | |
) { | |
final var rateMap = new HashMap<Long, BigDecimal>(); | |
for (final var instance : list) { | |
final var id = getId(instance); | |
final var amount = getDistributionRateBaseAmount(instance); | |
final var rate = amount.getValue() | |
.divide(totalDistributionRateBaseAmount.getValue(), 16, RoundingMode.HALF_EVEN); | |
rateMap.put(id, rate); | |
} | |
return rateMap; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.junit.jupiter.api.BeforeEach; | |
import org.junit.jupiter.api.Test; | |
import java.math.BigDecimal; | |
import java.util.List; | |
import java.util.Map; | |
import static org.assertj.core.api.Assertions.assertThat; | |
public class RateDistributedAmountMapTest { | |
private RateDistributedAmountMap<DummyItem> rateDistributedAmountMap; | |
@BeforeEach | |
public void setUp() { | |
rateDistributedAmountMap = new RateDistributedAmountMap<DummyItem>() { | |
@Override | |
protected Long getId(final DummyItem item) { | |
return item.getId(); | |
} | |
@Override | |
protected BigDecimal getDistributionRateBaseAmount(final DummyItem item) { | |
return item.getAmount(); | |
} | |
}; | |
} | |
@Test | |
public void test1() { | |
final List<DummyItem> itemList = List.of( | |
new DummyItem(1L, new BigDecimal("100.00")), | |
new DummyItem(2L, new BigDecimal("100.00")), | |
new DummyItem(3L, new BigDecimal("100.00")) | |
); | |
final BigDecimal distributeAmount = new BigDecimal("1000"); | |
final Map<Long, BigDecimal> resultMap = rateDistributedAmountMap.getMap(itemList, distributeAmount); | |
assertThat(resultMap) | |
.containsEntry(1L, new BigDecimal("334")) | |
.containsEntry(2L, new BigDecimal("333")) | |
.containsEntry(3L, new BigDecimal("333")) | |
.hasSize(3); | |
} | |
@Test | |
public void test2() { | |
final List<DummyItem> itemList = List.of( | |
new DummyItem(1L, new BigDecimal("100.00")), | |
new DummyItem(2L, new BigDecimal("150.00")), | |
new DummyItem(3L, new BigDecimal("200.00")) | |
); | |
final BigDecimal distributeAmount = new BigDecimal("450.00"); | |
final Map<Long, BigDecimal> resultMap = rateDistributedAmountMap.getMap(itemList, distributeAmount); | |
assertThat(resultMap) | |
.containsEntry(1L, new BigDecimal("100")) | |
.containsEntry(2L, new BigDecimal("150")) | |
.containsEntry(3L, new BigDecimal("200")) | |
.hasSize(3); | |
} | |
private static class DummyItem { | |
private final Long id; | |
private final BigDecimal amount; | |
public DummyItem(final Long id, final BigDecimal amount) { | |
this.id = id; | |
this.amount = amount; | |
} | |
public Long getId() { | |
return id; | |
} | |
public BigDecimal getAmount() { | |
return amount; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static org.assertj.core.api.Assertions.assertThat; | |
import java.util.List; | |
import kr.co._29cm.claim.v2.domain.common.Money; | |
import lombok.AllArgsConstructor; | |
import lombok.Getter; | |
import org.junit.jupiter.api.BeforeEach; | |
import org.junit.jupiter.api.Test; | |
public class UnitDistributedAmountMapFactoryTest { | |
private UnitDistributedAmountMapFactory factory; | |
@BeforeEach | |
public void setup() { | |
factory = new UnitDistributedAmountMapFactory(); | |
} | |
@Test | |
public void 단일_아이템_분배_금액_0일때_맵_생성시_0으로_분배() { | |
// given | |
final List<DummyItem> itemList = List.of( | |
new DummyItem(1L, Money.of(100)) | |
); | |
final Money totalDistributeAmount = Money.of(0); | |
// when | |
final var distributedMap = factory.createMap(itemList, DummyItem::getId, | |
totalDistributeAmount); | |
// then | |
assertThat(distributedMap) | |
.containsEntry(1L, Money.ZERO) | |
.hasSize(1); | |
} | |
@Test | |
public void 단일_아이템_분배_금액_0이_아닐때_맵_생성시_단일_아이템에_분배() { | |
// given | |
final List<DummyItem> itemList = List.of( | |
new DummyItem(1L, Money.of(100)) | |
); | |
final Money totalDistributeAmount = Money.of(1000); | |
// when | |
final var distributedMap = factory.createMap(itemList, DummyItem::getId, | |
totalDistributeAmount); | |
// then | |
assertThat(distributedMap) | |
.containsEntry(1L, Money.of(1000)) | |
.hasSize(1); | |
} | |
@Test | |
public void 여러_아이템_분배_금액_균등할때_맵_생성시_균등하게_분배() { | |
// given | |
final List<DummyItem> itemList = List.of( | |
new DummyItem(1L, Money.of(100)), | |
new DummyItem(2L, Money.of(150)), | |
new DummyItem(3L, Money.of(200)) | |
); | |
final Money totalDistributeAmount = Money.of(450); | |
// when | |
final var distributedMap = factory.createMap(itemList, DummyItem::getId, | |
totalDistributeAmount); | |
// then | |
assertThat(distributedMap) | |
.containsEntry(1L, Money.of(150)) | |
.containsEntry(2L, Money.of(150)) | |
.containsEntry(3L, Money.of(150)) | |
.hasSize(3); | |
} | |
@Test | |
public void 여러_아이템_분배_금액_불균등할때_맵_생성시_비례하여_분배() { | |
// given | |
final List<DummyItem> itemList = List.of( | |
new DummyItem(1L, Money.of(100)), | |
new DummyItem(2L, Money.of(100)), | |
new DummyItem(3L, Money.of(100)) | |
); | |
final Money totalDistributeAmount = Money.of(1000); | |
// when | |
final var distributedMap = factory.createMap(itemList, DummyItem::getId, | |
totalDistributeAmount); | |
// then | |
assertThat(distributedMap) | |
.containsEntry(1L, Money.of(334)) | |
.containsEntry(2L, Money.of(333)) | |
.containsEntry(3L, Money.of(333)) | |
.hasSize(3); | |
} | |
@Getter | |
@AllArgsConstructor | |
private static class DummyItem { | |
private final Long id; | |
private final Money amount; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment