23种设计模式详解及在JDK和Spring种的应用
23种设计模式详解及在JDK和Spring种的应用, 包括创建型、结构型和行为型设计模式。并提供了实际在JDK或者Spring中的应用。
目录
- 设计模式的分类
1.1 创建型模式
1.2 结构型模式
1.3 行为型模式 - 创建型设计模式
2.1 单例模式
2.2 工厂模式
2.3 抽象工厂模式
2.4 建造者模式
2.5 原型模式 - 结构型设计模式
3.1 适配器模式
3.2 桥接模式
3.3 组合模式
3.4 装饰模式
3.5 外观模式
3.6 享元模式
3.7 代理模式 - 行为型设计模式
4.1 责任链模式
4.2 命令模式
4.3 解释器模式
4.4 迭代器模式
4.5 中介者模式
4.6 备忘录模式
4.7 观察者模式
4.8 状态模式
4.9 策略模式
4.10 模板方法模式
4.11 访问者模式 - 总结
1. 设计模式的分类
设计模式通常分为三大类:
-
创建型模式:解决对象创建问题。
代表模式:单例模式、工厂模式、建造者模式等。 -
结构型模式:解决对象或类的组合问题。
代表模式:适配器模式、代理模式、桥接模式等。 -
行为型模式:关注对象之间的交互以及职责分配。
代表模式:观察者模式、策略模式、模板方法模式等。
2. 创建型设计模式
2.1 单例模式
场景:需要确保一个类只有一个实例,比如配置管理类或线程池。
优点:
- 提供对唯一实例的全局访问点。
- 控制实例数量,节约系统资源。
缺点:
- 难以进行单元测试。
- 可能导致过多的职责集中。
示例:数据库连接池。
实际应用:
- JDK:
java.lang.Runtime类。
扩展说明:Runtime类通过私有构造函数确保只能通过getRuntime方法获取唯一实例,从而实现单例模式。
代码实例:
public class SingletonExample {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
System.out.println(runtime);
Runtime anotherRuntime = Runtime.getRuntime();
System.out.println(anotherRuntime);
// 输出结果相同,说明两次获取的是同一个实例
}
}
2.2 工厂模式
场景:对象创建逻辑复杂或依赖多种配置时。
优点:
- 解耦对象的创建和使用。
- 易于扩展。
缺点:
- 增加了系统的复杂性。
- 可能导致类的个数增加。
示例:日志记录器创建时选择文件、数据库或控制台输出。
实际应用:
- JDK:
java.util.Calendar的getInstance方法。
扩展说明:Calendar类通过getInstance方法根据区域和时间等参数动态创建实例。
代码实例:
import java.util.Calendar;
public class FactoryExample {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println("当前时间: " + calendar.getTime());
}
}
2.3 抽象工厂模式
场景:需要跨平台或多个产品族创建对象时。
优点:
- 分离了具体类的生成。
- 易于交换产品系列。
缺点:
- 增加了系统的抽象性和复杂性。
示例:UI库为不同操作系统提供按钮和窗口组件。
实际应用:
- JDK:
javax.xml.parsers.DocumentBuilderFactory。
扩展说明:DocumentBuilderFactory通过抽象工厂模式提供了跨平台解析XML文档的能力。
代码实例:
import javax.xml.parsers.DocumentBuilderFactory;
public class AbstractFactoryExample {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
System.out.println("工厂实现类: " + factory.getClass().getName());
}
}
2.4 建造者模式
场景:需要创建复杂对象,且需要按步骤构造时。
优点:
- 允许更精细地控制对象的创建过程。
- 将对象的创建与表示分离。
缺点:
- 增加了代码的复杂性。
- 需要更多的类和构造器。
示例:生成一份复杂的PDF文档。
实际应用:
- JDK:
StringBuilder类。
扩展说明:StringBuilder通过建造者模式逐步构造字符串,提供了更高效的字符串拼接方式。
代码实例:
public class BuilderExample {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder();
builder.append("Hello, ");
builder.append("world!");
System.out.println(builder.toString());
}
}
2.5 原型模式
场景:需要大量创建相同或相似的对象,且对象初始化成本较高时。
优点:
- 提高了对象创建的性能。
- 简化了对象的创建过程。
缺点:
- 必须实现克隆方法。
- 克隆过程可能复杂。
示例:克隆大数据中的记录进行快速数据加载。
实际应用:
- JDK:
java.lang.Object类的clone方法。
扩展说明:Object类的clone方法为创建对象的浅拷贝提供了基础支持。
代码实例:
class Prototype implements Cloneable {
int value;
public Prototype(int value) {
this.value = value;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class PrototypeExample {
public static void main(String[] args) throws CloneNotSupportedException {
Prototype original = new Prototype(42);
Prototype cloned = (Prototype) original.clone();
System.out.println("原始值: " + original.value);
System.out.println("克隆值: " + cloned.value);
}
}
3. 结构型设计模式
3.1 适配器模式
场景:需要将一个接口转换成客户端期望的另一个接口时。
优点:
- 提高了类的复用性。
- 提供了现有类与其他类协作的可能性。
缺点:
- 增加了系统的复杂性。
示例:将旧版支付系统适配到新的电商平台。
实际应用:
- JDK:
java.util.Arrays#asList方法。
扩展说明:asList方法将数组适配为List接口。
代码实例:
import java.util.Arrays;
import java.util.List;
public class AdapterExample {
public static void main(String[] args) {
String[] array = {"A", "B", "C"};
List<String> list = Arrays.asList(array);
System.out.println(list);
}
}
3.5 外观模式
场景:需要为复杂子系统提供一个简化的接口时。
优点:
- 减少了系统的依赖性。
- 提高了子系统的灵活性和可维护性。
缺点:
- 增加了额外的封装层。
- 隐藏了子系统的具体实现。
示例:为多模块的电商系统提供统一订单处理接口。
实际应用:
- JDK:
javax.faces.context.FacesContext类。
扩展说明:FacesContext为开发者提供了一个简化的接口以访问底层JSF(JavaServer Faces)组件。
代码实例:
import javax.faces.context.FacesContext;
public class FacadeExample {
public static void main(String[] args) {
FacesContext context = FacesContext.getCurrentInstance();
if (context != null) {
System.out.println("外观模式正在工作: " + context);
} else {
System.out.println("无法获取FacesContext实例。");
}
}
}
3.6 享元模式
场景:需要大量创建相同或相似的小对象,且需要节省内存时。
优点:
- 减少了内存消耗。
- 提高了性能。
缺点:
- 增加了系统的复杂性。
- 需要维护外部状态和内部状态的区分。
示例:文本编辑器中的字符对象共享。
实际应用:
- JDK:
java.lang.String中的字符串常量池。
扩展说明:字符串常量池通过享元模式避免了重复创建相同字符串对象,节省内存。
代码实例:
public class FlyweightExample {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
System.out.println("s1 == s2: " + (s1 == s2)); // true
}
}
3.7 代理模式
场景:需要为对象提供额外功能(如控制访问)时。
优点:
- 提供了对目标对象的控制。
- 可以对目标对象进行功能扩展。
缺点:
- 增加了系统的复杂性。
- 可能造成请求处理速度变慢。
示例:远程代理实现延迟加载。
实际应用:
- JDK:
java.lang.reflect.Proxy动态代理。
扩展说明:Proxy类通过动态代理在运行时创建代理对象,以增强或控制对目标对象的访问。
代码实例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Service {
void execute();
}
class RealService implements Service {
public void execute() {
System.out.println("执行真实服务。");
}
}
class ProxyHandler implements InvocationHandler {
private Object target;
public ProxyHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理: 调用前");
Object result = method.invoke(target, args);
System.out.println("代理: 调用后");
return result;
}
}
public class ProxyExample {
public static void main(String[] args) {
Service realService = new RealService();
Service proxyInstance = (Service) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new ProxyHandler(realService)
);
proxyInstance.execute();
}
}
4.1 责任链模式
场景:需要多个对象按顺序处理请求时。
优点:
- 降低了对象之间的耦合度。
- 提高了请求的灵活性。
缺点:
- 可能导致请求未被处理。
- 调试较困难。
示例:审批流程中的多级审核。
实际应用:
- JDK:
java.util.logging.Logger中的过滤器链。
扩展说明:Logger通过责任链模式,支持日志消息的多级处理。
代码实例:
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ChainOfResponsibilityExample {
public static void main(String[] args) {
Logger logger = Logger.getLogger("MyLogger");
ConsoleHandler handler = new ConsoleHandler();
logger.addHandler(handler);
logger.setLevel(Level.ALL);
handler.setLevel(Level.ALL);
logger.severe("严重错误");
logger.info("普通信息");
}
}
4.2 命令模式
场景:需要将请求封装为对象,以便延迟执行或记录日志时。
优点:
- 解耦了调用者和接收者。
- 易于实现撤销和重做。
缺点:
- 可能导致类的数量增多。
- 系统更复杂。
示例:操作系统中的撤销功能。
实际应用:
- JDK:
java.lang.Runnable接口。
扩展说明:Runnable封装了一段可执行的代码,体现了命令模式的思想。
代码实例:
public class CommandExample {
public static void main(String[] args) {
Runnable command = () -> System.out.println("执行命令");
command.run();
}
}
4.3 解释器模式
场景:需要为特定语言定义语法解析时。
优点:
- 易于扩展新的语法。
- 语法的表示清晰直观。
缺点:
- 适用性较窄。
- 可能导致复杂的语法树。
示例:正则表达式解析器。
实际应用:
- JDK:
java.util.regex.Pattern。
扩展说明:Pattern类用于定义和解析正则表达式,是解释器模式的典型应用。
代码实例:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class InterpreterExample {
public static void main(String[] args) {
String regex = "\\\\d+";
String text = "12345 is a number";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println("找到的数字: " + matcher.group());
}
}
}
4.4 迭代器模式
场景:需要顺序访问集合中的元素而不暴露其内部表示时。
优点:
- 提供了一种一致的方法来遍历集合。
- 支持不同类型的集合。
缺点:
- 增加了系统的复杂性。
- 对不同集合的实现可能较繁琐。
示例:遍历社交媒体用户的好友列表。
实际应用:
- JDK:
java.util.Iterator接口。
扩展说明:Iterator接口提供了一种标准方式来遍历集合元素。
代码实例:
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorExample {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
4.5 中介者模式
场景:需要减少对象之间的直接依赖时。
优点:
- 降低了对象之间的耦合。
- 改进了对象之间的通信。
缺点:
- 增加了中介者的复杂性。
- 可能导致中介者过于庞大。
示例:聊天应用中的消息路由器。
实际应用:
- JDK:
java.util.Timer类。
扩展说明:Timer通过中介者模式协调多个定时任务的执行。
代码实例:
import java.util.Timer;
import java.util.TimerTask;
public class MediatorExample {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
System.out.println("执行定时任务。");
}
}, 1000, 2000);
}
}
4.6 备忘录模式
场景:需要保存对象的状态以便后续恢复时。
优点:
- 提供了一种恢复对象状态的机制。
- 实现了信息的封装。
缺点:
- 可能占用较多内存。
- 增加了实现的复杂性。
示例:文本编辑器中的撤销功能。
实际应用:
- JDK:
java.util.Date类。
扩展说明:Date对象通过序列化方式保存和恢复日期信息,体现了备忘录模式。
代码实例:
import java.util.Date;
public class MementoExample {
public static void main(String[] args) {
Date now = new Date();
System.out.println("当前日期: " + now);
long timestamp = now.getTime();
Date restored = new Date(timestamp);
System.out.println("恢复日期: " + restored);
}
}
4.7 观察者模式
场景:对象间存在一对多依赖关系时。
优点:
- 实现了对象之间的松耦合。
- 易于动态添加观察者。
缺点:
- 可能导致性能问题。
- 可能出现通知循环。
示例:新闻发布系统中,用户订阅了新闻更新。
实际应用:
- JDK:
java.util.Observable类和Observer接口。
扩展说明:Observable允许对象将变化通知给所有订阅者(观察者)。
代码实例:
import java.util.Observable;
import java.util.Observer;
class NewsFeed extends Observable {
void publishNews(String news) {
setChanged();
notifyObservers(news);
}
}
class Subscriber implements Observer {
private String name;
Subscriber(String name) {
this.name = name;
}
public void update(Observable o, Object arg) {
System.out.println(name + " 收到新闻: " + arg);
}
}
public class ObserverExample {
public static void main(String[] args) {
NewsFeed feed = new NewsFeed();
Subscriber sub1 = new Subscriber("用户A");
Subscriber sub2 = new Subscriber("用户B");
feed.addObserver(sub1);
feed.addObserver(sub2);
feed.publishNews("今天的头条新闻!");
}
}
4.8 状态模式
场景:对象的行为随状态改变而改变时。
优点:
- 避免了大量的条件语句。
- 提高了代码的可维护性。
缺点:
- 增加了系统的复杂性。
- 可能导致状态类的数量增多。
示例:在线订单状态从创建、支付到发货的转换。
实际应用:
- JDK:
javax.faces.lifecycle.Lifecycle类。
扩展说明:Lifecycle类通过状态模式管理JSF应用的生命周期。
代码实例:
public class StateExample {
interface State {
void handle();
}
static class StartState implements State {
public void handle() {
System.out.println("进入开始状态。");
}
}
static class EndState implements State {
public void handle() {
System.out.println("进入结束状态。");
}
}
static class Context {
private State state;
void setState(State state) {
this.state = state;
}
void request() {
state.handle();
}
}
public static void main(String[] args) {
Context context = new Context();
context.setState(new StartState());
context.request();
context.setState(new EndState());
context.request();
}
}
4.9 策略模式
场景:需要定义一系列算法,并在运行时选择其中之一时。
优点:
- 提高了算法的灵活性。
- 避免了使用多重条件语句。
缺点:
- 可能导致类的数量增多。
- 客户端必须了解不同策略之间的区别。
示例:支付系统中不同支付方式的选择。
实际应用:
- JDK:
java.util.Comparator接口。
扩展说明:Comparator允许以不同策略比较对象。
代码实例:
import java.util.Arrays;
import java.util.Comparator;
public class StrategyExample {
public static void main(String[] args) {
Integer[] numbers = {5, 2, 9, 1};
Arrays.sort(numbers, Comparator.naturalOrder());
System.out.println("升序排序: " + Arrays.toString(numbers));
Arrays.sort(numbers, Comparator.reverseOrder());
System.out.println("降序排序: " + Arrays.toString(numbers));
}
}
4.10 模板方法模式
场景:定义算法框架,但将具体实现步骤延迟到子类时。
优点:
- 提高了代码复用性。
- 提供了一个代码规范。
缺点:
- 对每个不同实现都需要创建子类。
- 模板方法的修改可能会影响子类。
示例:处理文件的标准步骤(打开、读取、处理、关闭)。
实际应用:
- JDK:
java.util.AbstractList。
扩展说明:AbstractList通过模板方法模式定义了操作列表的基本框架。
代码实例:
import java.util.AbstractList;
import java.util.Arrays;
class CustomList extends AbstractList<String> {
private String[] elements = {"A", "B", "C"};
public String get(int index) {
return elements[index];
}
public int size() {
return elements.length;
}
}
public class TemplateMethodExample {
public static void main(String[] args) {
CustomList list = new CustomList();
System.out.println(list);
System.out.println("元素: " + list.get(1));
}
}
4.11 访问者模式
场景:需要对对象结构中的元素执行操作,而不改变其类定义时。
优点:
- 增加新的操作变得简单。
- 将操作与对象结构分离。
缺点:
- 增加了系统的复杂性。
- 可能导致对象结构难以扩展。
示例:编译器中对语法树节点的操作。
实际应用:
- JDK:
javax.lang.model.element.ElementVisitor接口。
扩展说明:ElementVisitor通过访问者模式定义了处理Java程序元素的操作。
代码实例:
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementVisitor;
class CustomVisitor implements ElementVisitor<String, Void> {
public String visit(Element e, Void p) {
return "访问元素: " + e.getSimpleName();
}
public String visitUnknown(Element e, Void p) {
return "访问未知元素: " + e.getSimpleName();
}
// 省略其他方法的默认实现
}
public class VisitorExample {
public static void main(String[] args) {
System.out.println("此模式需在Java编译器插件中实际使用。");
}
}
5. 总结
设计模式为我们提供了成熟的解决方案,帮助解决常见的设计问题,提高代码的可复用性、可读性和可维护性。在实际开发中,选择合适的设计模式可以显著提升系统的灵活性和扩展性。