Last active
June 3, 2025 10:36
-
-
Save fResult/79ddd12a0db136f104697c589ae6b66e to your computer and use it in GitHub Desktop.
Example for the article https://fresult.medium.com/31014f433b49 and POC Strategy Design Pattern
This file contains hidden or 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.util.ArrayList; | |
| import java.util.List; | |
| import java.util.function.Function; | |
| class PricingDiscountApp { | |
| public static void main(String[] args) { | |
| final var customer1 = new Customer("1", "John Doe", CustomerType.VIP); | |
| final var customer2 = new Customer("2", "Jane Doe", CustomerType.REGULAR); | |
| final var customer3 = new Customer("3", "Business Corp", CustomerType.BUSINESS); | |
| final var item1 = new LineItem("item1", 2, 50.0); | |
| final var item2 = new LineItem("item2", 3, 30.0); | |
| final var item3 = new LineItem("item3", 25, 100.0); | |
| final var order1 = Order.of("order1", List.of(item1, item2), customer1); | |
| final var order2 = Order.of("order2", List.of(item2), customer2); | |
| final var order3 = Order.of("order3", List.of(item1, item2, item3), customer3); | |
| final var standard = new StandardPricingStrategy(); | |
| final var vipDiscount = new VipDiscountPricingStrategy(10.0); | |
| final var businessDiscount = new BusinessDiscountPricingStrategy(25.0); | |
| final var seasonal = new SeasonalPricingStrategy(Season.SUMMER); | |
| final var standardCalculator = PricingCalculator.createPriceCalculator(standard); | |
| final var vipDiscountCalculator = PricingCalculator.createPriceCalculator(vipDiscount); | |
| final var businessDiscountCalculator = PricingCalculator.createPriceCalculator(businessDiscount); | |
| final var seasonalCalculator = PricingCalculator.createPriceCalculator(seasonal); | |
| final var composedPricingCalculator = standardCalculator | |
| .andThen(vipDiscountCalculator) | |
| .andThen(businessDiscountCalculator) | |
| .andThen(seasonalCalculator); | |
| final var orders = List.of(order1, order2, order3); | |
| orders.stream().map(composedPricingCalculator).forEach(o -> { | |
| System.out.println("Order ID: " + o.id()); | |
| System.out.println("Customer: " + o.customer().name()); | |
| System.out.println("Base Price: " + o.basePrice()); | |
| System.out.println("Final Price: " + o.basePrice() * (o.customer().isVIP() ? 0.9 : 1.0)); // Example of final price calculation | |
| System.out.println("-----------------------------"); | |
| }); | |
| } | |
| } | |
| class PricingCalculator { | |
| public static Function<Order, Order> createPriceCalculator(PricingStrategy strategy) { | |
| return order -> { | |
| if (!strategy.isApplicableTo(order)) { | |
| System.out.println("Strategy [" + strategy.name() + "] not applicable to order: " + order.id()); | |
| return order; | |
| } | |
| return switch (strategy) { | |
| case StandardPricingStrategy(double baseRate) -> | |
| order.withPrice(order.basePrice() * baseRate); | |
| case VipDiscountPricingStrategy(double discountPercentage) -> | |
| applyDiscount(order, discountPercentage); | |
| case BusinessDiscountPricingStrategy(double discountPercentage) -> | |
| applyDiscount(order, discountPercentage); | |
| case SeasonalPricingStrategy seasonal -> | |
| order.withPrice(order.basePrice() * seasonal.multiplier()); | |
| }; | |
| }; | |
| } | |
| private static Order applyDiscount(Order order, double discountPercentage) { | |
| System.out.println("Applied discount: " + discountPercentage + "%"); | |
| final var discountPrice = order.basePrice() * (discountPercentage / 100); | |
| return order.withPrice(order.basePrice() - discountPrice); | |
| } | |
| } | |
| sealed interface PricingStrategy permits | |
| VipDiscountPricingStrategy, | |
| BusinessDiscountPricingStrategy, | |
| SeasonalPricingStrategy, | |
| StandardPricingStrategy { | |
| String name(); | |
| boolean isApplicableTo(Order order); | |
| } | |
| /** | |
| * @param baseRate <code>1.0</code> is the default rate, meaning no change to the base price. | |
| */ | |
| record StandardPricingStrategy(double baseRate) implements PricingStrategy { | |
| public String name() {return "Standard";} | |
| public StandardPricingStrategy() { | |
| this(1.0); | |
| } | |
| @Override | |
| public boolean isApplicableTo(Order order) { | |
| return true; | |
| } | |
| } | |
| /** | |
| * @param discountPercentage | |
| * The percentage discount to apply to the base price.<br> | |
| * <code>10.0</code> means a 10% discount. | |
| */ | |
| record VipDiscountPricingStrategy(double discountPercentage) implements PricingStrategy { | |
| public String name() {return "VIP Discount";} | |
| public VipDiscountPricingStrategy() { | |
| this(10.0); | |
| } | |
| @Override | |
| public boolean isApplicableTo(Order order) { | |
| return order.customer().isVIP(); | |
| } | |
| } | |
| /** | |
| * @param discountPercentage | |
| * The percentage discount to apply to the base price.<br> | |
| * <code>25.0</code> means a 25% discount. | |
| */ | |
| record BusinessDiscountPricingStrategy(double discountPercentage) implements PricingStrategy { | |
| public String name() {return "Business Discount";} | |
| public BusinessDiscountPricingStrategy() { | |
| this(25.0); | |
| } | |
| @Override | |
| public boolean isApplicableTo(Order order) { | |
| return order.customer().isBusiness(); | |
| } | |
| } | |
| record SeasonalPricingStrategy(Season season, double multiplier) implements PricingStrategy { | |
| public String name() {return "Seasonal [" + season + "]";} | |
| public SeasonalPricingStrategy(Season season) { | |
| this(season, switch (season) { | |
| case SUMMER -> 0.9; | |
| case WINTER -> 1.2; | |
| case SPRING, FALL -> 1.0; | |
| }); | |
| } | |
| @Override | |
| public boolean isApplicableTo(Order order) { | |
| return true; | |
| } | |
| } | |
| enum Season { | |
| WINTER, SPRING, SUMMER, FALL | |
| } | |
| record Order(String id, List<LineItem> items, double basePrice, Customer customer) { | |
| Order { | |
| if (id == null || items == null || customer == null) | |
| throw new IllegalArgumentException("Order id, items, and customer cannot be null"); | |
| if (items.isEmpty()) | |
| throw new IllegalArgumentException("Order must have at least one item"); | |
| items = List.copyOf(items); | |
| } | |
| public static Order of(String id, List<LineItem> items, Customer customer) { | |
| final var calculatedPrice = recalculateBasePrice(items); | |
| return new Order(id, items, calculatedPrice, customer); | |
| } | |
| public Order addItem(LineItem item) { | |
| if (item == null) throw new IllegalArgumentException("LineItem cannot be null"); | |
| final var newItems = new ArrayList<>(items) {{ add(item); }}; | |
| return new Order(id, newItems, basePrice + item.totalPrice(), customer); | |
| } | |
| @Override | |
| public List<LineItem> items() { | |
| return List.copyOf(items); | |
| } | |
| public Order withPrice(double newPrice) { | |
| return new Order(id, items, newPrice, customer); | |
| } | |
| private static double recalculateBasePrice(List<LineItem> items) { | |
| return items.stream() | |
| .mapToDouble(LineItem::totalPrice) | |
| .sum(); | |
| } | |
| } | |
| record LineItem(String productId, int quantity, double pricePerUnit) { | |
| public double totalPrice() { | |
| return quantity * pricePerUnit; | |
| } | |
| } | |
| record Customer(String id, String name, CustomerType type) { | |
| public boolean isVIP() { | |
| return type == CustomerType.VIP; | |
| } | |
| public boolean isRegular() { | |
| return type == CustomerType.REGULAR; | |
| } | |
| public boolean isBusiness() { | |
| return type == CustomerType.BUSINESS; | |
| } | |
| } | |
| enum CustomerType { | |
| REGULAR, VIP, BUSINESS | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment