Merge pull request #6960 from mikewojtyna/master
Add examples supporting BAEL-2273 article
This commit is contained in:
commit
aecfb01a56
|
@ -0,0 +1,15 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch;
|
||||||
|
|
||||||
|
import org.joda.money.CurrencyUnit;
|
||||||
|
import org.joda.money.Money;
|
||||||
|
|
||||||
|
public class AmountBasedDiscountPolicy implements DiscountPolicy {
|
||||||
|
@Override
|
||||||
|
public double discount(Order order) {
|
||||||
|
if (order.totalCost()
|
||||||
|
.isGreaterThan(Money.of(CurrencyUnit.USD, 500.00))) {
|
||||||
|
return 0.10;
|
||||||
|
} else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch;
|
||||||
|
|
||||||
|
public interface DiscountPolicy {
|
||||||
|
double discount(Order order);
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch;
|
||||||
|
|
||||||
|
public class FlatDiscountPolicy implements DiscountPolicy {
|
||||||
|
@Override
|
||||||
|
public double discount(Order order) {
|
||||||
|
return 0.01;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch;
|
||||||
|
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.joda.money.Money;
|
||||||
|
|
||||||
|
import com.baeldung.ddd.order.OrderLine;
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.visitor.OrderVisitor;
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.visitor.Visitable;
|
||||||
|
|
||||||
|
public class Order extends com.baeldung.ddd.order.Order implements Visitable<OrderVisitor> {
|
||||||
|
public Order(List<OrderLine> orderLines) {
|
||||||
|
super(orderLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money totalCost(SpecialDiscountPolicy discountPolicy) {
|
||||||
|
return totalCost().multipliedBy(1 - applyDiscountPolicy(discountPolicy), RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double applyDiscountPolicy(SpecialDiscountPolicy discountPolicy) {
|
||||||
|
return discountPolicy.discount(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(OrderVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch;
|
||||||
|
|
||||||
|
public interface SpecialDiscountPolicy extends DiscountPolicy {
|
||||||
|
double discount(SpecialOrder order);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.baeldung.ddd.order.OrderLine;
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.visitor.OrderVisitor;
|
||||||
|
|
||||||
|
public class SpecialOrder extends Order {
|
||||||
|
|
||||||
|
private boolean eligibleForExtraDiscount;
|
||||||
|
|
||||||
|
public SpecialOrder(List<OrderLine> orderLines) {
|
||||||
|
super(orderLines);
|
||||||
|
this.eligibleForExtraDiscount = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpecialOrder(List<OrderLine> orderLines, boolean eligibleForSpecialDiscount) {
|
||||||
|
super(orderLines);
|
||||||
|
this.eligibleForExtraDiscount = eligibleForSpecialDiscount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEligibleForExtraDiscount() {
|
||||||
|
return eligibleForExtraDiscount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected double applyDiscountPolicy(SpecialDiscountPolicy discountPolicy) {
|
||||||
|
return discountPolicy.discount(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(OrderVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch.visitor;
|
||||||
|
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.Order;
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.SpecialOrder;
|
||||||
|
|
||||||
|
public class HtmlOrderViewCreator implements OrderVisitor {
|
||||||
|
|
||||||
|
private String html;
|
||||||
|
|
||||||
|
public String getHtml() {
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Order order) {
|
||||||
|
html = String.format("<p>Regular order total cost: %s</p>", order.totalCost());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(SpecialOrder order) {
|
||||||
|
html = String.format("<h1>Special Order</h1><p>total cost: %s</p>", order.totalCost());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch.visitor;
|
||||||
|
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.Order;
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.SpecialOrder;
|
||||||
|
|
||||||
|
public interface OrderVisitor {
|
||||||
|
void visit(Order order);
|
||||||
|
void visit(SpecialOrder order);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch.visitor;
|
||||||
|
|
||||||
|
public interface Visitable<V> {
|
||||||
|
void accept(V visitor);
|
||||||
|
}
|
|
@ -92,7 +92,7 @@ class JpaOrder {
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeLineItem(int line) {
|
void removeLineItem(int line) {
|
||||||
JpaOrderLine removedLine = orderLines.remove(line);
|
orderLines.remove(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCurrencyUnit(String currencyUnit) {
|
void setCurrencyUnit(String currencyUnit) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ class JpaProduct {
|
||||||
public JpaProduct(BigDecimal price, String currencyUnit) {
|
public JpaProduct(BigDecimal price, String currencyUnit) {
|
||||||
super();
|
super();
|
||||||
this.price = price;
|
this.price = price;
|
||||||
currencyUnit = currencyUnit;
|
this.currencyUnit = currencyUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.baeldung.ddd.order;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.joda.money.CurrencyUnit;
|
||||||
|
import org.joda.money.Money;
|
||||||
|
|
||||||
|
public class OrderFixtureUtils {
|
||||||
|
public static List<OrderLine> anyOrderLines() {
|
||||||
|
return Arrays.asList(new OrderLine(new Product(Money.of(CurrencyUnit.USD, 100)), 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<OrderLine> orderLineItemsWorthNDollars(int totalCost) {
|
||||||
|
return Arrays.asList(new OrderLine(new Product(Money.of(CurrencyUnit.USD, totalCost)), 1));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import org.joda.money.CurrencyUnit;
|
||||||
|
import org.joda.money.Money;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import com.baeldung.ddd.order.OrderFixtureUtils;
|
||||||
|
|
||||||
|
public class DoubleDispatchDiscountPolicyUnitTest {
|
||||||
|
// @formatter:off
|
||||||
|
@DisplayName(
|
||||||
|
"given regular order with items worth $100 total, " +
|
||||||
|
"when apply 10% discount policy, " +
|
||||||
|
"then cost after discount is $90"
|
||||||
|
)
|
||||||
|
// @formatter:on
|
||||||
|
@Test
|
||||||
|
void test() throws Exception {
|
||||||
|
// given
|
||||||
|
Order order = new Order(OrderFixtureUtils.orderLineItemsWorthNDollars(100));
|
||||||
|
SpecialDiscountPolicy discountPolicy = new SpecialDiscountPolicy() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double discount(Order order) {
|
||||||
|
return 0.10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double discount(SpecialOrder order) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
Money totalCostAfterDiscount = order.totalCost(discountPolicy);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(totalCostAfterDiscount).isEqualTo(Money.of(CurrencyUnit.USD, 90));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
@DisplayName(
|
||||||
|
"given special order eligible for extra discount with items worth $100 total, " +
|
||||||
|
"when apply 20% discount policy for extra discount orders, " +
|
||||||
|
"then cost after discount is $80"
|
||||||
|
)
|
||||||
|
// @formatter:on
|
||||||
|
@Test
|
||||||
|
void test1() throws Exception {
|
||||||
|
// given
|
||||||
|
boolean eligibleForExtraDiscount = true;
|
||||||
|
Order order = new SpecialOrder(OrderFixtureUtils.orderLineItemsWorthNDollars(100), eligibleForExtraDiscount);
|
||||||
|
SpecialDiscountPolicy discountPolicy = new SpecialDiscountPolicy() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double discount(Order order) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double discount(SpecialOrder order) {
|
||||||
|
if (order.isEligibleForExtraDiscount())
|
||||||
|
return 0.20;
|
||||||
|
return 0.10;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
Money totalCostAfterDiscount = order.totalCost(discountPolicy);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(totalCostAfterDiscount).isEqualTo(Money.of(CurrencyUnit.USD, 80.00));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.Order;
|
||||||
|
import com.baeldung.ddd.order.OrderFixtureUtils;
|
||||||
|
import com.baeldung.ddd.order.OrderLine;
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.visitor.HtmlOrderViewCreator;
|
||||||
|
|
||||||
|
public class HtmlOrderViewCreatorUnitTest {
|
||||||
|
// @formatter:off
|
||||||
|
@DisplayName(
|
||||||
|
"given collection of regular and special orders, " +
|
||||||
|
"when create HTML view using visitor for each order, " +
|
||||||
|
"then the dedicated view is created for each order"
|
||||||
|
)
|
||||||
|
// @formatter:on
|
||||||
|
@Test
|
||||||
|
void test() throws Exception {
|
||||||
|
// given
|
||||||
|
List<OrderLine> anyOrderLines = OrderFixtureUtils.anyOrderLines();
|
||||||
|
List<Order> orders = Arrays.asList(new Order(anyOrderLines), new SpecialOrder(anyOrderLines));
|
||||||
|
HtmlOrderViewCreator htmlOrderViewCreator = new HtmlOrderViewCreator();
|
||||||
|
|
||||||
|
// when
|
||||||
|
orders.get(0)
|
||||||
|
.accept(htmlOrderViewCreator);
|
||||||
|
String regularOrderHtml = htmlOrderViewCreator.getHtml();
|
||||||
|
orders.get(1)
|
||||||
|
.accept(htmlOrderViewCreator);
|
||||||
|
String specialOrderHtml = htmlOrderViewCreator.getHtml();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(regularOrderHtml).containsPattern("<p>Regular order total cost: .*</p>");
|
||||||
|
assertThat(specialOrderHtml).containsPattern("<h1>Special Order</h1><p>total cost: .*</p>");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.Order;
|
||||||
|
import com.baeldung.ddd.order.OrderFixtureUtils;
|
||||||
|
import com.baeldung.ddd.order.OrderLine;
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.SpecialDiscountPolicy;
|
||||||
|
import com.baeldung.ddd.order.doubledispatch.SpecialOrder;
|
||||||
|
|
||||||
|
public class MethodOverloadExampleUnitTest {
|
||||||
|
// @formatter:off
|
||||||
|
@DisplayName(
|
||||||
|
"given discount policy accepting special orders, " +
|
||||||
|
"when apply the policy on special order declared as regular order, " +
|
||||||
|
"then regular discount method is used"
|
||||||
|
)
|
||||||
|
// @formatter:on
|
||||||
|
@Test
|
||||||
|
void test() throws Exception {
|
||||||
|
// given
|
||||||
|
SpecialDiscountPolicy specialPolicy = new SpecialDiscountPolicy() {
|
||||||
|
@Override
|
||||||
|
public double discount(Order order) {
|
||||||
|
return 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double discount(SpecialOrder order) {
|
||||||
|
return 0.10;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Order specialOrder = new SpecialOrder(anyOrderLines());
|
||||||
|
|
||||||
|
// when
|
||||||
|
double discount = specialPolicy.discount(specialOrder);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(discount).isEqualTo(0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OrderLine> anyOrderLines() {
|
||||||
|
return OrderFixtureUtils.anyOrderLines();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.baeldung.ddd.order.doubledispatch;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import com.baeldung.ddd.order.OrderFixtureUtils;
|
||||||
|
|
||||||
|
public class SingleDispatchDiscountPolicyUnitTest {
|
||||||
|
// @formatter:off
|
||||||
|
@DisplayName(
|
||||||
|
"given two discount policies, " +
|
||||||
|
"when use these policies, " +
|
||||||
|
"then single dispatch chooses the implementation based on runtime type"
|
||||||
|
)
|
||||||
|
// @formatter:on
|
||||||
|
@Test
|
||||||
|
void test() throws Exception {
|
||||||
|
// given
|
||||||
|
DiscountPolicy flatPolicy = new FlatDiscountPolicy();
|
||||||
|
DiscountPolicy amountPolicy = new AmountBasedDiscountPolicy();
|
||||||
|
Order orderWorth501Dollars = orderWorthNDollars(501);
|
||||||
|
|
||||||
|
// when
|
||||||
|
double flatDiscount = flatPolicy.discount(orderWorth501Dollars);
|
||||||
|
double amountDiscount = amountPolicy.discount(orderWorth501Dollars);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(flatDiscount).isEqualTo(0.01);
|
||||||
|
assertThat(amountDiscount).isEqualTo(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Order orderWorthNDollars(int totalCost) {
|
||||||
|
return new Order(OrderFixtureUtils.orderLineItemsWorthNDollars(totalCost));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue