Smells create pattern pressure
Repeated conditionals, tangled construction, and bloated coordination often hint at a better structure.
One of the healthiest ways to arrive at patterns is to refactor toward them. A Java system grows, change pressure reveals repeated variation or awkward responsibilities, and the design moves toward a clearer pattern because the need is now obvious. This produces stronger judgment than applying a pattern by name from day one.
Order-management systems evolve unevenly. Discounts may remain simple for months, then suddenly multiply. Notifications may start as email only, then expand to SMS, webhooks, and in-app messages. Refactoring toward patterns lets the design grow when real variability or complexity appears.
Refactoring toward patterns sits naturally between OOAD principles and the GoF catalog. It teaches you to see why a strategy, factory, observer, or state structure might emerge from the current pain in the code.
In Java, this often means first isolating responsibilities, then extracting stable contracts, then moving conditional logic or object creation into more coherent forms. The pattern becomes the result of good pressure management, not the starting slogan.
This approach usually produces simpler systems because abstractions appear only where change has already justified them.
Repeated conditionals, tangled construction, and bloated coordination often hint at a better structure.
The path matters: improve design incrementally without betting the whole system on a rewrite.
A pattern is most useful when you can point to the concrete pressure it relieved.
package org.javaomnibus.ecommerce.process;
public interface DiscountPolicy {
Money apply(Order order, Money subtotal);
}
public final class LoyaltyDiscountPolicy implements DiscountPolicy {
@Override
public Money apply(Order order, Money subtotal) {
return subtotal.multiply(95).divide(100);
}
}
public final class CheckoutPricingService {
private final DiscountPolicy discountPolicy;
public CheckoutPricingService(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
public Money finalTotal(Order order, Money subtotal) {
return discountPolicy.apply(order, subtotal);
}
}
If you wait too long, the refactoring cost increases. If you abstract too early, the design gains indirection without value. The skill is in spotting the moment variation becomes persistent.