Master 9 Essential Design Patterns for Cleaner Java Code
This article explains nine widely used design patterns—Singleton, Builder, Factory, Strategy, Template Method, Chain of Responsibility, Proxy, Adapter, and Observer—detailing their concepts, typical use‑cases, and concrete Java code examples drawn from popular open‑source projects such as Spring, Dubbo, MyBatis and Guava.
Ever struggled with unreadable legacy code full of long methods, tangled if‑else branches, and no one to ask for clarification? This guide shows how to refactor such code using nine common design patterns, providing clear explanations and real‑world source code snippets from open‑source projects.
Singleton Pattern
The Singleton pattern ensures a class has only one instance within a process (or within a Spring container). Two popular implementations are the eager static constant and the lazy double‑checked locking.
1. Static Constant (eager)
Used frequently in Spring, e.g.,
AnnotationBeanNameGeneratorfor bean name generation.
2. Double‑Checked Locking (lazy)
Common in interviews and open‑source code. The implementation uses a volatile instance and synchronizes only on the first creation.
<code>public class Singleton {
private volatile static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
</code>The outer null check reduces synchronization overhead, while the inner check prevents multiple creations. The
volatilekeyword avoids instruction reordering that could expose a partially constructed object.
Double‑Checked Locking in Dubbo
Dubbo’s SPI mechanism also employs this pattern to guarantee a single instance.
Builder Pattern
The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations. It is especially useful when an object has many parameters.
<code>PersonDTO personDTO = PersonDTO.builder()
.name("三友的java日记")
.age(18)
.sex(1)
.phone("188****9527")
.build();
</code>Spring’s
BeanDefinitionBuilder, Guava’s
CacheBuilder, and JDK’s
StringBuilderare real‑world examples.
Factory Pattern
Factory patterns encapsulate object creation. Three variants are covered:
Simple Factory
Factory Method
Abstract Factory
Simple Factory
<code>public class SimpleAnimalFactory {
public Animal createAnimal(String animalType) {
if ("cat".equals(animalType)) {
Cat cat = new Cat();
// complex initialization
return cat;
} else if ("dog".equals(animalType)) {
Dog dog = new Dog();
// complex initialization
return dog;
} else {
throw new RuntimeException("animalType=" + animalType + " cannot be created");
}
}
}
</code>Factory Method
Introduces an
AnimalFactoryinterface so each concrete animal has its own factory, adhering to the Open/Closed Principle.
<code>public interface AnimalFactory {
Animal createAnimal();
}
public class CatFactory implements AnimalFactory {
@Override
public Animal createAnimal() {
Cat cat = new Cat();
// complex initialization
return cat;
}
}
public class DogFactory implements AnimalFactory {
@Override
public Animal createAnimal() {
Dog dog = new Dog();
// complex initialization
return dog;
}
}
</code>Abstract Factory
Extends the idea to create families of related products, e.g., animals and their food.
<code>public interface AnimalFactory {
Animal createAnimal();
Food createFood();
}
</code>MyBatis’s
SqlSessionFactoryand Spring’s
BeanFactoryare practical applications.
Strategy Pattern
Encapsulates interchangeable algorithms. The article shows a messaging example where different notification channels (SMS, App, Email) are implemented as separate strategies.
<code>public interface MessageNotifier {
boolean support(int notifyType);
void notify(User user, String content);
}
@Component
public class SMSMessageNotifier implements MessageNotifier {
@Override
public boolean support(int notifyType) { return notifyType == 0; }
@Override
public void notify(User user, String content) { /* send SMS */ }
}
public class AppMessageNotifier implements MessageNotifier {
@Override
public boolean support(int notifyType) { return notifyType == 1; }
@Override
public void notify(User user, String content) { /* send App push */ }
}
// usage
@Resource
private List<MessageNotifier> messageNotifiers;
public void notifyMessage(User user, String content, int type) {
for (MessageNotifier n : messageNotifiers) {
if (n.support(type)) { n.notify(user, content); break; }
}
}
</code>Spring MVC’s
HandlerMethodArgumentResolverand
HandlerMethodReturnValueHandlerare concrete strategy implementations.
Template Method Pattern
Defines the skeleton of an algorithm in a base class while allowing subclasses to override specific steps. Examples include
HashMap’s
afterNodeInsertion, Spring’s
ApplicationContext.refresh, and MyBatis’s
BaseExecutorworkflow.
Chain of Responsibility Pattern
Links handlers in a chain so a request traverses until a handler processes it. The article provides a leave‑approval example and shows its use in Spring MVC interceptors, Sentinel slots, and MyBatis executor chaining.
Proxy Pattern
Provides a surrogate object that controls access to the real subject. Examples include a logging proxy for a service, Spring AOP, MyBatis’s
CachingExecutorproxying a
SimpleExecutor, and adapter usage for logging frameworks.
Adapter Pattern
Converts one interface to another so incompatible classes can work together. The tutorial shows a USB‑Type‑C to Micro‑USB adapter and explains MyBatis’s logging abstraction that adapts to various logging frameworks.
Observer Pattern
Establishes a one‑to‑many dependency so that when the subject changes, all observers are notified. Spring’s event mechanism and typical fire‑alarm examples illustrate the pattern.
Conclusion
The nine patterns—Singleton, Builder, Factory, Strategy, Template Method, Chain of Responsibility, Proxy, Adapter, and Observer—are not only prevalent in open‑source frameworks but also valuable tools for everyday software development.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.