23种设计模式详解及在JDK和Spring种的应用

23种设计模式详解及在JDK和Spring种的应用, 包括创建型、结构型和行为型设计模式。并提供了实际在JDK或者Spring中的应用。

目录

  1. 设计模式的分类
    1.1 创建型模式
    1.2 结构型模式
    1.3 行为型模式
  2. 创建型设计模式
    2.1 单例模式
    2.2 工厂模式
    2.3 抽象工厂模式
    2.4 建造者模式
    2.5 原型模式
  3. 结构型设计模式
    3.1 适配器模式
    3.2 桥接模式
    3.3 组合模式
    3.4 装饰模式
    3.5 外观模式
    3.6 享元模式
    3.7 代理模式
  4. 行为型设计模式
    4.1 责任链模式
    4.2 命令模式
    4.3 解释器模式
    4.4 迭代器模式
    4.5 中介者模式
    4.6 备忘录模式
    4.7 观察者模式
    4.8 状态模式
    4.9 策略模式
    4.10 模板方法模式
    4.11 访问者模式
  5. 总结

1. 设计模式的分类

设计模式通常分为三大类:

  1. 创建型模式:解决对象创建问题。
    代表模式:单例模式、工厂模式、建造者模式等。

  2. 结构型模式:解决对象或类的组合问题。
    代表模式:适配器模式、代理模式、桥接模式等。

  3. 行为型模式:关注对象之间的交互以及职责分配。
    代表模式:观察者模式、策略模式、模板方法模式等。


2. 创建型设计模式

2.1 单例模式

场景:需要确保一个类只有一个实例,比如配置管理类或线程池。

优点

  • 提供对唯一实例的全局访问点。
  • 控制实例数量,节约系统资源。

缺点

  • 难以进行单元测试。
  • 可能导致过多的职责集中。

示例:数据库连接池。

实际应用

  • JDKjava.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 工厂模式

场景:对象创建逻辑复杂或依赖多种配置时。

优点

  • 解耦对象的创建和使用。
  • 易于扩展。

缺点

  • 增加了系统的复杂性。
  • 可能导致类的个数增加。

示例:日志记录器创建时选择文件、数据库或控制台输出。

实际应用

  • JDKjava.util.CalendargetInstance方法。

扩展说明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库为不同操作系统提供按钮和窗口组件。

实际应用

  • JDKjavax.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文档。

实际应用

  • JDKStringBuilder类。

扩展说明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 原型模式

场景:需要大量创建相同或相似的对象,且对象初始化成本较高时。

优点

  • 提高了对象创建的性能。
  • 简化了对象的创建过程。

缺点

  • 必须实现克隆方法。
  • 克隆过程可能复杂。

示例:克隆大数据中的记录进行快速数据加载。

实际应用

  • JDKjava.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 适配器模式

场景:需要将一个接口转换成客户端期望的另一个接口时。

优点

  • 提高了类的复用性。
  • 提供了现有类与其他类协作的可能性。

缺点

  • 增加了系统的复杂性。

示例:将旧版支付系统适配到新的电商平台。

实际应用

  • JDKjava.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 外观模式

场景:需要为复杂子系统提供一个简化的接口时。

优点

  • 减少了系统的依赖性。
  • 提高了子系统的灵活性和可维护性。

缺点

  • 增加了额外的封装层。
  • 隐藏了子系统的具体实现。

示例:为多模块的电商系统提供统一订单处理接口。

实际应用

  • JDKjavax.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 享元模式

场景:需要大量创建相同或相似的小对象,且需要节省内存时。

优点

  • 减少了内存消耗。
  • 提高了性能。

缺点

  • 增加了系统的复杂性。
  • 需要维护外部状态和内部状态的区分。

示例:文本编辑器中的字符对象共享。

实际应用

  • JDKjava.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 代理模式

场景:需要为对象提供额外功能(如控制访问)时。

优点

  • 提供了对目标对象的控制。
  • 可以对目标对象进行功能扩展。

缺点

  • 增加了系统的复杂性。
  • 可能造成请求处理速度变慢。

示例:远程代理实现延迟加载。

实际应用

  • JDKjava.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 责任链模式

场景:需要多个对象按顺序处理请求时。

优点

  • 降低了对象之间的耦合度。
  • 提高了请求的灵活性。

缺点

  • 可能导致请求未被处理。
  • 调试较困难。

示例:审批流程中的多级审核。

实际应用

  • JDKjava.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 命令模式

场景:需要将请求封装为对象,以便延迟执行或记录日志时。

优点

  • 解耦了调用者和接收者。
  • 易于实现撤销和重做。

缺点

  • 可能导致类的数量增多。
  • 系统更复杂。

示例:操作系统中的撤销功能。

实际应用

  • JDKjava.lang.Runnable接口。

扩展说明Runnable封装了一段可执行的代码,体现了命令模式的思想。
代码实例

public class CommandExample {
    public static void main(String[] args) {
        Runnable command = () -> System.out.println("执行命令");
        command.run();
    }
}

4.3 解释器模式

场景:需要为特定语言定义语法解析时。

优点

  • 易于扩展新的语法。
  • 语法的表示清晰直观。

缺点

  • 适用性较窄。
  • 可能导致复杂的语法树。

示例:正则表达式解析器。

实际应用

  • JDKjava.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 迭代器模式

场景:需要顺序访问集合中的元素而不暴露其内部表示时。

优点

  • 提供了一种一致的方法来遍历集合。
  • 支持不同类型的集合。

缺点

  • 增加了系统的复杂性。
  • 对不同集合的实现可能较繁琐。

示例:遍历社交媒体用户的好友列表。

实际应用

  • JDKjava.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 中介者模式

场景:需要减少对象之间的直接依赖时。

优点

  • 降低了对象之间的耦合。
  • 改进了对象之间的通信。

缺点

  • 增加了中介者的复杂性。
  • 可能导致中介者过于庞大。

示例:聊天应用中的消息路由器。

实际应用

  • JDKjava.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 备忘录模式

场景:需要保存对象的状态以便后续恢复时。

优点

  • 提供了一种恢复对象状态的机制。
  • 实现了信息的封装。

缺点

  • 可能占用较多内存。
  • 增加了实现的复杂性。

示例:文本编辑器中的撤销功能。

实际应用

  • JDKjava.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 观察者模式

场景:对象间存在一对多依赖关系时。

优点

  • 实现了对象之间的松耦合。
  • 易于动态添加观察者。

缺点

  • 可能导致性能问题。
  • 可能出现通知循环。

示例:新闻发布系统中,用户订阅了新闻更新。

实际应用

  • JDKjava.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 状态模式

场景:对象的行为随状态改变而改变时。

优点

  • 避免了大量的条件语句。
  • 提高了代码的可维护性。

缺点

  • 增加了系统的复杂性。
  • 可能导致状态类的数量增多。

示例:在线订单状态从创建、支付到发货的转换。

实际应用

  • JDKjavax.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 策略模式

场景:需要定义一系列算法,并在运行时选择其中之一时。

优点

  • 提高了算法的灵活性。
  • 避免了使用多重条件语句。

缺点

  • 可能导致类的数量增多。
  • 客户端必须了解不同策略之间的区别。

示例:支付系统中不同支付方式的选择。

实际应用

  • JDKjava.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 模板方法模式

场景:定义算法框架,但将具体实现步骤延迟到子类时。

优点

  • 提高了代码复用性。
  • 提供了一个代码规范。

缺点

  • 对每个不同实现都需要创建子类。
  • 模板方法的修改可能会影响子类。

示例:处理文件的标准步骤(打开、读取、处理、关闭)。

实际应用

  • JDKjava.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 访问者模式

场景:需要对对象结构中的元素执行操作,而不改变其类定义时。

优点

  • 增加新的操作变得简单。
  • 将操作与对象结构分离。

缺点

  • 增加了系统的复杂性。
  • 可能导致对象结构难以扩展。

示例:编译器中对语法树节点的操作。

实际应用

  • JDKjavax.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. 总结

设计模式为我们提供了成熟的解决方案,帮助解决常见的设计问题,提高代码的可复用性、可读性和可维护性。在实际开发中,选择合适的设计模式可以显著提升系统的灵活性和扩展性。

更多阅读

C语言设计模式

Java设计模式

Golang设计模式

Rust设计模式