Refactoring Classic Design Patterns with Java 8 Lambdas

This article demonstrates how Java 8’s lambda expressions and functional interfaces can be used to refactor classic design‑pattern implementations—Strategy, Template Method, Observer, and Chain of Responsibility—into more concise, maintainable backend code, providing both traditional Java examples and their streamlined lambda equivalents.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Refactoring Classic Design Patterns with Java 8 Lambdas

Java 8 introduces lambda expressions and functional interfaces that can simplify traditional object‑oriented implementations of common design patterns.

Strategy pattern – The classic version defines an OrderService interface and two concrete strategies ( NoSqlSaveOrderStrategy and MySqlSaveOrderStrategy) injected into OrderServiceExecutor. The Java 8 refactor replaces the concrete strategy classes with lambda expressions passed directly to the executor.

public interface OrderService {
    void saveOrder(String orderNo);
}
public class MySqlSaveOrderStrategy implements OrderService {
    @Override
    public void saveOrder(String orderNo) {
        System.out.println("order:" + orderNo + " save to mysql");
    }
}
public class NoSqlSaveOrderStrategy implements OrderService {
    @Override
    public void saveOrder(String orderNo) {
        System.out.println("order:" + orderNo + " save to nosql");
    }
}
public class OrderServiceExecutor {
    private final OrderService service;
    public OrderServiceExecutor(OrderService service) {
        this.service = service;
    }
    public void save(String orderNo) {
        this.service.saveOrder(orderNo);
    }
}
public class OrderServiceTest {
    public static void main(String[] args) {
        OrderServiceExecutor executor1 = new OrderServiceExecutor(new MySqlSaveOrderStrategy());
        executor1.save("001");
        OrderServiceExecutor executor2 = new OrderServiceExecutor(new NoSqlSaveOrderStrategy());
        executor2.save("002");
    }
}
public static void main(String[] args) {
    OrderServiceExecutor executor1 = new OrderServiceExecutor((orderNo) -> System.out.println("order:" + orderNo + " save to mysql"));
    executor1.save("001");
    OrderServiceExecutor executor2 = new OrderServiceExecutor((orderNo) -> System.out.println("order:" + orderNo + " save to nosql"));
    executor2.save("002");
}

Template Method pattern – The original design defines an abstract AbstractPushTemplate with a concrete push method and abstract execute. Subclasses such as PushCouponTemplate and PushScoreTemplate implement execute. Using a lambda, a single PushTemplateLambda class accepts a Consumer<Object[]> to perform the custom logic.

public abstract class AbstractPushTemplate {
    public void push(int customerId, String shopName) {
        System.out.println("准备推送...");
        execute(customerId, shopName);
        System.out.println("推送完成
");
    }
    protected abstract void execute(int customerId, String shopName);
}
public class PushCouponTemplate extends AbstractPushTemplate {
    @Override
    protected void execute(int customerId, String shopName) {
        System.out.println("会员:" + customerId + ",你好," + shopName + "送您一张优惠券");
    }
}
public class PushScoreTemplate extends AbstractPushTemplate {
    @Override
    protected void execute(int customerId, String shopName) {
        System.out.println("会员:" + customerId + ",你好," + shopName + "送您10个积分");
    }
}
public class PushTemplateLambda {
    public void push(int customerId, String shopName, Consumer<Object[]> execute) {
        System.out.println("准备推送...");
        Object[] param = new Object[]{customerId, shopName};
        execute.accept(param);
        System.out.println("推送完成
");
    }
}
new PushTemplateLambda().push(1, "糖果店", (obj) -> System.out.println("会员:" + obj[0] + ",你好," + obj[1] + "送您一张优惠券"));
new PushTemplateLambda().push(1, "服装店", (obj) -> System.out.println("会员:" + obj[0] + ",你好," + obj[1] + "送您10个积分"));

Observer pattern – The classic version uses separate Observer and Subject interfaces with concrete implementations. Java 8 allows default methods in interfaces, enabling a single NewSubject interface that holds a list of observers and provides default registration and notification logic, while observers can be expressed as lambdas.

public interface Observer {
    void notify(String orderNo);
}
public interface Subject {
    void registerObserver(Observer o);
    void notifyAllObserver(String orderNo);
}
public class SubjectImpl implements Subject {
    private final List<Observer> list = new ArrayList<>();
    @Override
    public void registerObserver(Observer o) {
        list.add(o);
    }
    @Override
    public void notifyAllObserver(String orderNo) {
        list.forEach(c -> c.notify(orderNo));
    }
}
public class OrderObserver implements Observer {
    @Override
    public void notify(String orderNo) {
        System.out.println("订单 " + orderNo + " 状态更新为【已支付】");
    }
}
public class StockObserver implements Observer {
    @Override
    public void notify(String orderNo) {
        System.out.println("订单 " + orderNo + " 已通知库房发货!");
    }
}
static void test1() {
    Subject subject = new SubjectImpl();
    subject.registerObserver(new OrderObserver());
    subject.registerObserver(new StockObserver());
    subject.notifyAllObserver("001");
}
public interface NewSubject {
    List<Observer> list = new ArrayList<>();
    default void registerObserver(Observer o) { list.add(o); }
    default void notifyAllObserver(String orderNo) { list.forEach(c -> c.notify(orderNo)); }
}
static void test2() {
    NewSubject subject = new NewSubject() {};
    subject.registerObserver((orderNo) -> System.out.println("订单 " + orderNo + " 状态更新为【已支付】"));
    subject.registerObserver((orderNo) -> System.out.println("订单 " + orderNo + " 已通知库房发货!"));
    subject.notifyAllObserver("002");
}

Chain of Responsibility pattern – The traditional implementation defines a Processor interface, an abstract base class, and concrete processors that forward the request. With Java 8, a functional interface NewProcessor returning a Consumer<String> and method chaining via andThen achieves the same behavior in a single line.

public interface Processor {
    Processor getNextProcessor();
    void process(String param);
}
public abstract class AbstractProcessor implements Processor {
    private Processor next;
    public AbstractProcessor(Processor processor) { this.next = processor; }
    @Override public Processor getNextProcessor() { return next; }
    @Override public abstract void process(String param);
}
public class ProcessorImpl1 extends AbstractProcessor {
    public ProcessorImpl1(Processor processor) { super(processor); }
    @Override public void process(String param) {
        System.out.println("processor 1 is processing:" + param);
        if (getNextProcessor() != null) { getNextProcessor().process(param); }
    }
}
public class ProcessorImpl2 extends AbstractProcessor {
    public ProcessorImpl2(Processor next) { super(next); }
    @Override public void process(String param) {
        System.out.println("processor 2 is processing:" + param);
        if (getNextProcessor() != null) { getNextProcessor().process(param); }
    }
}
static void test1() {
    Processor p1 = new ProcessorImpl1(null);
    Processor p2 = new ProcessorImpl2(p1);
    p2.process("something happened");
}
@FunctionalInterface
public interface NewProcessor {
    Consumer<String> process(String param);
}
static void test2() {
    Consumer<String> p1 = param -> System.out.println("processor 1 is processing:" + param);
    Consumer<String> p2 = param -> System.out.println("processor 2 is processing:" + param);
    p2.andThen(p1).accept("something happened");
}

These examples show when lambda expressions improve readability and reduce boilerplate, while reminding developers to keep complex logic in dedicated classes for maintainability.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Design PatternsLambdarefactoringjava8
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.