Skip to content

Instantly share code, notes, and snippets.

@fResult
Last active June 3, 2025 10:36
Show Gist options
  • Select an option

  • Save fResult/79ddd12a0db136f104697c589ae6b66e to your computer and use it in GitHub Desktop.

Select an option

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
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