Skip to content

Commit 8a69946

Browse files
committed
Add examples supporting BAEL-2273 article
1 parent ef91212 commit 8a69946

16 files changed

+362
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.baeldung.ddd.order.doubledispatch;
2+
3+
import org.joda.money.CurrencyUnit;
4+
import org.joda.money.Money;
5+
6+
public class AmountBasedDiscountPolicy implements DiscountPolicy {
7+
@Override
8+
public double discount(Order order) {
9+
if (order.totalCost()
10+
.isGreaterThan(Money.of(CurrencyUnit.USD, 500.00))) {
11+
return 0.10;
12+
} else
13+
return 0;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.baeldung.ddd.order.doubledispatch;
2+
3+
public interface DiscountPolicy {
4+
double discount(Order order);
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.baeldung.ddd.order.doubledispatch;
2+
3+
public class FlatDiscountPolicy implements DiscountPolicy {
4+
@Override
5+
public double discount(Order order) {
6+
return 0.01;
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.baeldung.ddd.order.doubledispatch;
2+
3+
import java.math.RoundingMode;
4+
import java.util.List;
5+
6+
import org.joda.money.Money;
7+
8+
import com.baeldung.ddd.order.OrderLine;
9+
import com.baeldung.ddd.order.doubledispatch.visitor.OrderVisitor;
10+
import com.baeldung.ddd.order.doubledispatch.visitor.Visitable;
11+
12+
public class Order extends com.baeldung.ddd.order.Order implements Visitable<OrderVisitor> {
13+
public Order(List<OrderLine> orderLines) {
14+
super(orderLines);
15+
}
16+
17+
public Money totalCost(SpecialDiscountPolicy discountPolicy) {
18+
return totalCost().multipliedBy(1 - applyDiscountPolicy(discountPolicy), RoundingMode.HALF_UP);
19+
}
20+
21+
protected double applyDiscountPolicy(SpecialDiscountPolicy discountPolicy) {
22+
return discountPolicy.discount(this);
23+
}
24+
25+
@Override
26+
public void accept(OrderVisitor visitor) {
27+
visitor.visit(this);
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.baeldung.ddd.order.doubledispatch;
2+
3+
public interface SpecialDiscountPolicy extends DiscountPolicy {
4+
double discount(SpecialOrder order);
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.baeldung.ddd.order.doubledispatch;
2+
3+
import java.util.List;
4+
5+
import com.baeldung.ddd.order.OrderLine;
6+
import com.baeldung.ddd.order.doubledispatch.visitor.OrderVisitor;
7+
8+
public class SpecialOrder extends Order {
9+
10+
private boolean eligibleForExtraDiscount;
11+
12+
public SpecialOrder(List<OrderLine> orderLines) {
13+
super(orderLines);
14+
this.eligibleForExtraDiscount = false;
15+
}
16+
17+
public SpecialOrder(List<OrderLine> orderLines, boolean eligibleForSpecialDiscount) {
18+
super(orderLines);
19+
this.eligibleForExtraDiscount = eligibleForSpecialDiscount;
20+
}
21+
22+
public boolean isEligibleForExtraDiscount() {
23+
return eligibleForExtraDiscount;
24+
}
25+
26+
@Override
27+
protected double applyDiscountPolicy(SpecialDiscountPolicy discountPolicy) {
28+
return discountPolicy.discount(this);
29+
}
30+
31+
@Override
32+
public void accept(OrderVisitor visitor) {
33+
visitor.visit(this);
34+
}
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.baeldung.ddd.order.doubledispatch.visitor;
2+
3+
import com.baeldung.ddd.order.doubledispatch.Order;
4+
import com.baeldung.ddd.order.doubledispatch.SpecialOrder;
5+
6+
public class HtmlOrderViewCreator implements OrderVisitor {
7+
8+
private String html;
9+
10+
public String getHtml() {
11+
return html;
12+
}
13+
14+
@Override
15+
public void visit(Order order) {
16+
html = String.format("<p>Regular order total cost: %s</p>", order.totalCost());
17+
}
18+
19+
@Override
20+
public void visit(SpecialOrder order) {
21+
html = String.format("<h1>Special Order</h1><p>total cost: %s</p>", order.totalCost());
22+
}
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.baeldung.ddd.order.doubledispatch.visitor;
2+
3+
import com.baeldung.ddd.order.doubledispatch.Order;
4+
import com.baeldung.ddd.order.doubledispatch.SpecialOrder;
5+
6+
public interface OrderVisitor {
7+
void visit(Order order);
8+
void visit(SpecialOrder order);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.baeldung.ddd.order.doubledispatch.visitor;
2+
3+
public interface Visitable<V> {
4+
void accept(V visitor);
5+
}

Diff for: ddd/src/main/java/com/baeldung/ddd/order/jpa/JpaOrder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ BigDecimal getTotalCost() {
9292
}
9393

9494
void removeLineItem(int line) {
95-
JpaOrderLine removedLine = orderLines.remove(line);
95+
orderLines.remove(line);
9696
}
9797

9898
void setCurrencyUnit(String currencyUnit) {

Diff for: ddd/src/main/java/com/baeldung/ddd/order/jpa/JpaProduct.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public JpaProduct() {
1515
public JpaProduct(BigDecimal price, String currencyUnit) {
1616
super();
1717
this.price = price;
18-
currencyUnit = currencyUnit;
18+
this.currencyUnit = currencyUnit;
1919
}
2020

2121
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.baeldung.ddd.order;
2+
3+
import java.util.Arrays;
4+
import java.util.List;
5+
6+
import org.joda.money.CurrencyUnit;
7+
import org.joda.money.Money;
8+
9+
public class OrderFixtureUtils {
10+
public static List<OrderLine> anyOrderLines() {
11+
return Arrays.asList(new OrderLine(new Product(Money.of(CurrencyUnit.USD, 100)), 1));
12+
}
13+
14+
public static List<OrderLine> orderLineItemsWorthNDollars(int totalCost) {
15+
return Arrays.asList(new OrderLine(new Product(Money.of(CurrencyUnit.USD, totalCost)), 1));
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.baeldung.ddd.order.doubledispatch;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.joda.money.CurrencyUnit;
6+
import org.joda.money.Money;
7+
import org.junit.jupiter.api.DisplayName;
8+
import org.junit.jupiter.api.Test;
9+
10+
import com.baeldung.ddd.order.OrderFixtureUtils;
11+
12+
public class DoubleDispatchDiscountPolicyUnitTest {
13+
// @formatter:off
14+
@DisplayName(
15+
"given regular order with items worth $100 total, " +
16+
"when apply 10% discount policy, " +
17+
"then cost after discount is $90"
18+
)
19+
// @formatter:on
20+
@Test
21+
void test() throws Exception {
22+
// given
23+
Order order = new Order(OrderFixtureUtils.orderLineItemsWorthNDollars(100));
24+
SpecialDiscountPolicy discountPolicy = new SpecialDiscountPolicy() {
25+
26+
@Override
27+
public double discount(Order order) {
28+
return 0.10;
29+
}
30+
31+
@Override
32+
public double discount(SpecialOrder order) {
33+
return 0;
34+
}
35+
};
36+
37+
// when
38+
Money totalCostAfterDiscount = order.totalCost(discountPolicy);
39+
40+
// then
41+
assertThat(totalCostAfterDiscount).isEqualTo(Money.of(CurrencyUnit.USD, 90));
42+
}
43+
44+
// @formatter:off
45+
@DisplayName(
46+
"given special order eligible for extra discount with items worth $100 total, " +
47+
"when apply 20% discount policy for extra discount orders, " +
48+
"then cost after discount is $80"
49+
)
50+
// @formatter:on
51+
@Test
52+
void test1() throws Exception {
53+
// given
54+
boolean eligibleForExtraDiscount = true;
55+
Order order = new SpecialOrder(OrderFixtureUtils.orderLineItemsWorthNDollars(100), eligibleForExtraDiscount);
56+
SpecialDiscountPolicy discountPolicy = new SpecialDiscountPolicy() {
57+
58+
@Override
59+
public double discount(Order order) {
60+
return 0;
61+
}
62+
63+
@Override
64+
public double discount(SpecialOrder order) {
65+
if (order.isEligibleForExtraDiscount())
66+
return 0.20;
67+
return 0.10;
68+
}
69+
};
70+
71+
// when
72+
Money totalCostAfterDiscount = order.totalCost(discountPolicy);
73+
74+
// then
75+
assertThat(totalCostAfterDiscount).isEqualTo(Money.of(CurrencyUnit.USD, 80.00));
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.baeldung.ddd.order.doubledispatch;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.util.Arrays;
6+
import java.util.List;
7+
8+
import org.junit.jupiter.api.DisplayName;
9+
import org.junit.jupiter.api.Test;
10+
11+
import com.baeldung.ddd.order.doubledispatch.Order;
12+
import com.baeldung.ddd.order.OrderFixtureUtils;
13+
import com.baeldung.ddd.order.OrderLine;
14+
import com.baeldung.ddd.order.doubledispatch.visitor.HtmlOrderViewCreator;
15+
16+
public class HtmlOrderViewCreatorUnitTest {
17+
// @formatter:off
18+
@DisplayName(
19+
"given collection of regular and special orders, " +
20+
"when create HTML view using visitor for each order, " +
21+
"then the dedicated view is created for each order"
22+
)
23+
// @formatter:on
24+
@Test
25+
void test() throws Exception {
26+
// given
27+
List<OrderLine> anyOrderLines = OrderFixtureUtils.anyOrderLines();
28+
List<Order> orders = Arrays.asList(new Order(anyOrderLines), new SpecialOrder(anyOrderLines));
29+
HtmlOrderViewCreator htmlOrderViewCreator = new HtmlOrderViewCreator();
30+
31+
// when
32+
orders.get(0)
33+
.accept(htmlOrderViewCreator);
34+
String regularOrderHtml = htmlOrderViewCreator.getHtml();
35+
orders.get(1)
36+
.accept(htmlOrderViewCreator);
37+
String specialOrderHtml = htmlOrderViewCreator.getHtml();
38+
39+
// then
40+
assertThat(regularOrderHtml).containsPattern("<p>Regular order total cost: .*</p>");
41+
assertThat(specialOrderHtml).containsPattern("<h1>Special Order</h1><p>total cost: .*</p>");
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.baeldung.ddd.order.doubledispatch;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.util.List;
6+
7+
import org.junit.jupiter.api.DisplayName;
8+
import org.junit.jupiter.api.Test;
9+
10+
import com.baeldung.ddd.order.doubledispatch.Order;
11+
import com.baeldung.ddd.order.OrderFixtureUtils;
12+
import com.baeldung.ddd.order.OrderLine;
13+
import com.baeldung.ddd.order.doubledispatch.SpecialDiscountPolicy;
14+
import com.baeldung.ddd.order.doubledispatch.SpecialOrder;
15+
16+
public class MethodOverloadExampleUnitTest {
17+
// @formatter:off
18+
@DisplayName(
19+
"given discount policy accepting special orders, " +
20+
"when apply the policy on special order declared as regular order, " +
21+
"then regular discount method is used"
22+
)
23+
// @formatter:on
24+
@Test
25+
void test() throws Exception {
26+
// given
27+
SpecialDiscountPolicy specialPolicy = new SpecialDiscountPolicy() {
28+
@Override
29+
public double discount(Order order) {
30+
return 0.01;
31+
}
32+
33+
@Override
34+
public double discount(SpecialOrder order) {
35+
return 0.10;
36+
}
37+
};
38+
Order specialOrder = new SpecialOrder(anyOrderLines());
39+
40+
// when
41+
double discount = specialPolicy.discount(specialOrder);
42+
43+
// then
44+
assertThat(discount).isEqualTo(0.01);
45+
}
46+
47+
private List<OrderLine> anyOrderLines() {
48+
return OrderFixtureUtils.anyOrderLines();
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.baeldung.ddd.order.doubledispatch;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.junit.jupiter.api.DisplayName;
6+
import org.junit.jupiter.api.Test;
7+
8+
import com.baeldung.ddd.order.OrderFixtureUtils;
9+
10+
public class SingleDispatchDiscountPolicyUnitTest {
11+
// @formatter:off
12+
@DisplayName(
13+
"given two discount policies, " +
14+
"when use these policies, " +
15+
"then single dispatch chooses the implementation based on runtime type"
16+
)
17+
// @formatter:on
18+
@Test
19+
void test() throws Exception {
20+
// given
21+
DiscountPolicy flatPolicy = new FlatDiscountPolicy();
22+
DiscountPolicy amountPolicy = new AmountBasedDiscountPolicy();
23+
Order orderWorth501Dollars = orderWorthNDollars(501);
24+
25+
// when
26+
double flatDiscount = flatPolicy.discount(orderWorth501Dollars);
27+
double amountDiscount = amountPolicy.discount(orderWorth501Dollars);
28+
29+
// then
30+
assertThat(flatDiscount).isEqualTo(0.01);
31+
assertThat(amountDiscount).isEqualTo(0.1);
32+
}
33+
34+
private Order orderWorthNDollars(int totalCost) {
35+
return new Order(OrderFixtureUtils.orderLineItemsWorthNDollars(totalCost));
36+
}
37+
}

0 commit comments

Comments
 (0)