From 1dcc63c2846e5cd850fbe3f13299a51067ae65ce Mon Sep 17 00:00:00 2001 From: Fabio Scotto di Santolo Date: Thu, 28 Nov 2019 13:37:22 +0100 Subject: [PATCH] Esempi di creazione di un DSL interno con Java con diversi pattern --- .../java/org/gym/fp/moderjava/DslTest.java | 141 ++++++++++++++++++ .../gym/fp/moderjava/dsl/GroupingBuilder.java | 29 ++++ .../fp/moderjava/dsl/LambdaOrderBuilder.java | 65 ++++++++ .../dsl/MethodChainingOrderBuilder.java | 31 ++++ .../gym/fp/moderjava/dsl/MixedBuilder.java | 68 +++++++++ .../dsl/NestedFunctionOrderBuilder.java | 46 ++++++ .../java/org/gym/fp/moderjava/dsl/Order.java | 31 ++++ .../java/org/gym/fp/moderjava/dsl/Stock.java | 25 ++++ .../gym/fp/moderjava/dsl/StockBuilder.java | 21 +++ .../java/org/gym/fp/moderjava/dsl/Tax.java | 17 +++ .../gym/fp/moderjava/dsl/TaxCalculator.java | 17 +++ .../java/org/gym/fp/moderjava/dsl/Trade.java | 50 +++++++ .../gym/fp/moderjava/dsl/TradeBuilder.java | 18 +++ .../moderjava/dsl/TradeBuilderWithStock.java | 17 +++ 14 files changed, 576 insertions(+) create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/DslTest.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/GroupingBuilder.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/LambdaOrderBuilder.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/MethodChainingOrderBuilder.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/MixedBuilder.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/NestedFunctionOrderBuilder.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/Order.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/Stock.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/StockBuilder.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/Tax.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/TaxCalculator.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/Trade.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/TradeBuilder.java create mode 100644 modern-java/src/main/java/org/gym/fp/moderjava/dsl/TradeBuilderWithStock.java diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/DslTest.java b/modern-java/src/main/java/org/gym/fp/moderjava/DslTest.java new file mode 100644 index 0000000..70c7959 --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/DslTest.java @@ -0,0 +1,141 @@ +package org.gym.fp.moderjava; + +import org.gym.fp.moderjava.dsl.*; + +import static java.lang.System.out; +import static org.gym.fp.moderjava.dsl.LambdaOrderBuilder.order; +import static org.gym.fp.moderjava.dsl.MethodChainingOrderBuilder.forCustomer; +import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.at; +import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.buy; +import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.on; +import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.order; +import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.sell; +import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.stock; + +public class DslTest { + + public static void main(String[] args) { + doUnacceptableDslTest(); + doMethodChainingPatternTest(); + doNestedFunctionsPatternTest(); + doLambdaSequencingPatternTest(); + doMixedPatternTest(); + doUsingMethodReferenceDslTest(); + } + + private static void doUnacceptableDslTest() { + Order order = new Order(); + order.setCustomer("BigBank"); + + Trade trade1 = new Trade(); + trade1.setType(Trade.Type.BUY); + + Stock stock1 = new Stock(); + stock1.setSymbol("IBM"); + stock1.setMarket("NYSE"); + + trade1.setStock(stock1); + trade1.setPrice(125.00); + trade1.setQuantity(80); + order.addTrade(trade1); + + Trade trade2 = new Trade(); + trade2.setType(Trade.Type.BUY); + + Stock stock2 = new Stock(); + stock2.setSymbol("GOOGLE"); + stock2.setMarket("NASDAQ"); + + trade2.setStock(stock2); + trade2.setPrice(375.00); + trade2.setQuantity(50); + order.addTrade(trade2); + } + + private static void doMethodChainingPatternTest() { + Order order = forCustomer("BigBank") + .buy(80) + .stock("IBM") + .on("NYSE") + .at(125.00) + .sell(50) + .stock("Google") + .on("NASDAQ") + .at(375.00) + .end(); + out.println("Order: " + order.toString()); + out.println("----------------------------------------"); + } + + private static void doNestedFunctionsPatternTest() { + Order order = order("BigBank", + buy(80, + stock("IBM", on("NYSE")), + at(125.00)), + sell(50, + stock("Google", on("NASDAQ")), + at(375.00)) + ); + out.println("Order: " + order.toString()); + out.println("----------------------------------------"); + } + + private static void doLambdaSequencingPatternTest() { + Order order = order(o -> { + o.forCustomer("BigBank"); + o.buy(t -> { + t.quantity(80); + t.price(125.00); + t.stock(s -> { + s.symbol("IBM"); + s.market("NYSE"); + }); + }); + o.sell(t -> { + t.quantity(50); + t.price(375.00); + t.stock(s -> { + s.symbol("GOOGLE"); + s.market("NASDAQ"); + }); + }); + }); + out.println("Order: " + order.toString()); + out.println("----------------------------------------"); + } + + private static void doMixedPatternTest() { + Order order = MixedBuilder.forCustomer("BigBank", + MixedBuilder.buy(t -> t.quantity(80) + .stock("IBM") + .on("NYSE") + .at(125.00)), + MixedBuilder.sell(t -> t.quantity(50) + .stock("Google") + .on("NASDAQ") + .at(375.00)) + ); + out.println("Order: " + order.toString()); + out.println("----------------------------------------"); + } + + private static void doUsingMethodReferenceDslTest() { + Order order = MixedBuilder.forCustomer("BigBank", + MixedBuilder.buy(t -> t.quantity(80) + .stock("IBM") + .on("NYSE") + .at(125.00)), + MixedBuilder.sell(t -> t.quantity(50) + .stock("Google") + .on("NASDAQ") + .at(375.00)) + ); + double value = new TaxCalculator() + .with(Tax::regional) + .with(Tax::surcharge) + .calculate(order); + out.printf("Calculate value order with taxes: %.2f\n", value); + out.println("----------------------------------------"); + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/GroupingBuilder.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/GroupingBuilder.java new file mode 100644 index 0000000..65aadbd --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/GroupingBuilder.java @@ -0,0 +1,29 @@ +package org.gym.fp.moderjava.dsl; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collector; + +import static java.util.stream.Collectors.groupingBy; + +public class GroupingBuilder { + private final Collector> collector; + + public GroupingBuilder(Collector> collector) { + this.collector = collector; + } + + public Collector> get() { + return collector; + } + + public GroupingBuilder, J> after(Function classifier) { + return new GroupingBuilder<>(groupingBy(classifier, collector)); + } + + public static GroupingBuilder, K> groupOn(Function classifier) { + return new GroupingBuilder<>(groupingBy(classifier)); + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/LambdaOrderBuilder.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/LambdaOrderBuilder.java new file mode 100644 index 0000000..c75e32e --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/LambdaOrderBuilder.java @@ -0,0 +1,65 @@ +package org.gym.fp.moderjava.dsl; + +import java.util.function.Consumer; + +public class LambdaOrderBuilder { + private Order order = new Order(); + + public static Order order(Consumer consumer) { + LambdaOrderBuilder builder = new LambdaOrderBuilder(); + consumer.accept(builder); + return builder.order; + } + + public void forCustomer(String customer) { + order.setCustomer(customer); + } + + public void buy(Consumer consumer) { + trade(consumer, Trade.Type.BUY); + } + + public void sell(Consumer consumer) { + trade(consumer, Trade.Type.SELL); + } + + private void trade(Consumer consumer, Trade.Type type) { + TradeBuilder builder = new TradeBuilder(); + builder.trade.setType(type); + consumer.accept(builder); + order.addTrade(builder.trade); + } + + public static class TradeBuilder { + private Trade trade = new Trade(); + + public void quantity(int quantity) { + trade.setQuantity(quantity); + } + + public void price(double price) { + trade.setPrice(price); + } + + public void stock(Consumer consumer) { + StockBuilder builder = new StockBuilder(); + consumer.accept(builder); + trade.setStock(builder.stock); + } + + } + + public static class StockBuilder { + private Stock stock = new Stock(); + + public void symbol(String symbol) { + stock.setSymbol(symbol); + } + + public void market(String market) { + stock.setMarket(market); + } + + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/MethodChainingOrderBuilder.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/MethodChainingOrderBuilder.java new file mode 100644 index 0000000..bf76c09 --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/MethodChainingOrderBuilder.java @@ -0,0 +1,31 @@ +package org.gym.fp.moderjava.dsl; + +public class MethodChainingOrderBuilder { + public final Order order = new Order(); + + private MethodChainingOrderBuilder(String customer) { + order.setCustomer(customer); + } + + public static MethodChainingOrderBuilder forCustomer(String customer) { + return new MethodChainingOrderBuilder(customer); + } + + public TradeBuilder buy(int quantity) { + return new TradeBuilder(this, Trade.Type.BUY, quantity); + } + + public TradeBuilder sell(int quantity) { + return new TradeBuilder(this, Trade.Type.SELL, quantity); + } + + public MethodChainingOrderBuilder addTrade(Trade trade) { + order.addTrade(trade); + return this; + } + + public Order end() { + return order; + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/MixedBuilder.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/MixedBuilder.java new file mode 100644 index 0000000..573a6f4 --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/MixedBuilder.java @@ -0,0 +1,68 @@ +package org.gym.fp.moderjava.dsl; + +import java.util.function.Consumer; +import java.util.stream.Stream; + +public class MixedBuilder { + + public static Order forCustomer(String customer, TradeBuilder... builders) { + Order order = new Order(); + order.setCustomer(customer); + Stream.of(builders).forEach(b -> order.addTrade(b.trade)); + return order; + } + + public static TradeBuilder buy(Consumer consumer) { + return buildTrade(consumer, Trade.Type.BUY); + } + + public static TradeBuilder sell(Consumer consumer) { + return buildTrade(consumer, Trade.Type.SELL); + } + + private static TradeBuilder buildTrade(Consumer consumer, Trade.Type type) { + TradeBuilder builder = new TradeBuilder(); + builder.trade.setType(type); + consumer.accept(builder); + return builder; + } + + public static class TradeBuilder { + private Trade trade = new Trade(); + + public TradeBuilder quantity(int quantity) { + trade.setQuantity(quantity); + return this; + } + + public TradeBuilder at(double price) { + trade.setPrice(price); + return this; + } + + public StockBuilder stock(String symbol) { + return new StockBuilder(this, trade, symbol); + } + + } + + public static class StockBuilder { + private final TradeBuilder builder; + private final Trade trade; + private final Stock stock = new Stock(); + + public StockBuilder(TradeBuilder tradeBuilder, Trade trade, String symbol) { + this.builder = tradeBuilder; + this.trade = trade; + this.stock.setSymbol(symbol); + } + + public TradeBuilder on(String market) { + this.stock.setMarket(market); + trade.setStock(this.stock); + return builder; + } + + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/NestedFunctionOrderBuilder.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/NestedFunctionOrderBuilder.java new file mode 100644 index 0000000..41f2231 --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/NestedFunctionOrderBuilder.java @@ -0,0 +1,46 @@ +package org.gym.fp.moderjava.dsl; + +import java.util.stream.Stream; + +public class NestedFunctionOrderBuilder { + + public static Order order(String customer, Trade... trades) { + Order order = new Order(); + order.setCustomer(customer); + Stream.of(trades).forEach(order::addTrade); + return order; + } + + public static Trade buy(int quantity, Stock stock, double price) { + return buildTrade(quantity, stock, price, Trade.Type.BUY); + } + + public static Trade sell(int quantity, Stock stock, double price) { + return buildTrade(quantity, stock, price, Trade.Type.SELL); + } + + private static Trade buildTrade(int quantity, Stock stock, double price, Trade.Type buy) { + Trade trade = new Trade(); + trade.setQuantity(quantity); + trade.setType(buy); + trade.setStock(stock); + trade.setPrice(price); + return trade; + } + + public static double at(double price) { + return price; + } + + public static Stock stock(String symbol, String market) { + Stock stock = new Stock(); + stock.setSymbol(symbol); + stock.setMarket(market); + return stock; + } + + public static String on(String market) { + return market; + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Order.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Order.java new file mode 100644 index 0000000..54960f3 --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Order.java @@ -0,0 +1,31 @@ +package org.gym.fp.moderjava.dsl; + +import lombok.ToString; + +import java.util.ArrayList; +import java.util.List; + +@ToString +public class Order { + private String customer; + private List trades = new ArrayList<>(); + + public void addTrade(Trade trade) { + trades.add(trade); + } + + public String getCustomer() { + return customer; + } + + public void setCustomer(String customer) { + this.customer = customer; + } + + public double getValue() { + return trades.stream() + .mapToDouble(Trade::getValue) + .sum(); + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Stock.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Stock.java new file mode 100644 index 0000000..4b3b31d --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Stock.java @@ -0,0 +1,25 @@ +package org.gym.fp.moderjava.dsl; + +import lombok.ToString; + +@ToString +public class Stock { + private String symbol; + private String market; + + public String getSymbol() { + return symbol; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + + public String getMarket() { + return market; + } + + public void setMarket(String market) { + this.market = market; + } +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/StockBuilder.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/StockBuilder.java new file mode 100644 index 0000000..36e885a --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/StockBuilder.java @@ -0,0 +1,21 @@ +package org.gym.fp.moderjava.dsl; + +public class StockBuilder { + private final MethodChainingOrderBuilder builder; + private final Trade trade; + private final Stock stock = new Stock(); + + public StockBuilder(MethodChainingOrderBuilder builder, + Trade trade, String symbol) { + this.builder = builder; + this.trade = trade; + this.stock.setSymbol(symbol); + } + + public TradeBuilderWithStock on(String market) { + this.stock.setMarket(market); + trade.setStock(this.stock); + return new TradeBuilderWithStock(builder, trade); + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Tax.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Tax.java new file mode 100644 index 0000000..98a798a --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Tax.java @@ -0,0 +1,17 @@ +package org.gym.fp.moderjava.dsl; + +public class Tax { + + public static double regional(double value) { + return value * 1.1; + } + + public static double general(double value) { + return value * 1.3; + } + + public static double surcharge(double value) { + return value * 1.05; + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/TaxCalculator.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/TaxCalculator.java new file mode 100644 index 0000000..5c808a2 --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/TaxCalculator.java @@ -0,0 +1,17 @@ +package org.gym.fp.moderjava.dsl; + +import java.util.function.DoubleUnaryOperator; + +public class TaxCalculator { + private DoubleUnaryOperator taxFunction = d -> d; + + public TaxCalculator with(DoubleUnaryOperator f) { + this.taxFunction = taxFunction.andThen(f); + return this; + } + + public double calculate(Order order) { + return this.taxFunction.applyAsDouble(order.getValue()); + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Trade.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Trade.java new file mode 100644 index 0000000..b4cdead --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/Trade.java @@ -0,0 +1,50 @@ +package org.gym.fp.moderjava.dsl; + +import lombok.ToString; + +@ToString +public class Trade { + public enum Type {BUY, SELL} + + private Type type; + private Stock stock; + private int quantity; + private double price; + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public Stock getStock() { + return stock; + } + + public void setStock(Stock stock) { + this.stock = stock; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + public double getValue() { + return quantity * price; + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/TradeBuilder.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/TradeBuilder.java new file mode 100644 index 0000000..8996310 --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/TradeBuilder.java @@ -0,0 +1,18 @@ +package org.gym.fp.moderjava.dsl; + +public class TradeBuilder { + private final MethodChainingOrderBuilder builder; + private final Trade trade = new Trade(); + + public TradeBuilder(MethodChainingOrderBuilder builder, + Trade.Type buy, int quantity) { + this.builder = builder; + this.trade.setType(buy); + this.trade.setQuantity(quantity); + } + + public StockBuilder stock(String symbol) { + return new StockBuilder(builder, trade, symbol); + } + +} diff --git a/modern-java/src/main/java/org/gym/fp/moderjava/dsl/TradeBuilderWithStock.java b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/TradeBuilderWithStock.java new file mode 100644 index 0000000..2651bf6 --- /dev/null +++ b/modern-java/src/main/java/org/gym/fp/moderjava/dsl/TradeBuilderWithStock.java @@ -0,0 +1,17 @@ +package org.gym.fp.moderjava.dsl; + +public class TradeBuilderWithStock { + private final MethodChainingOrderBuilder builder; + private final Trade trade; + + public TradeBuilderWithStock(MethodChainingOrderBuilder builder, Trade trade) { + this.builder = builder; + this.trade = trade; + } + + public MethodChainingOrderBuilder at(double price) { + this.trade.setPrice(price); + return builder.addTrade(trade); + } + +}