几种常用的设计模式

单例模式、工厂模式、观察者模式、策略模式、装饰器模式、适配器模式。

Java面试

几种常用的设计模式

设计模式是软件开发领域中总结出的通用解决方案,用于解决在特定环境下经常遇到的设计问题。 它们并不是具体的代码实现,而是描述了解决某类问题的抽象思想和可复用的设计模板。 在软件开发中,设计模式帮助开发人员实现代码的可读性、扩展性和复用性,同时促进团队成员之间的高效沟通。

单例模式

单例模式(Singleton Pattern)是一种创建型设计模式,目的是确保一个类在系统中只有一个实例,并提供一个全局访问点。单例模式常被用于表示全局唯一的资源或对象,例如配置管理器、日志系统、线程池等。

package com.practice.patterns;

public class Singleton {
    private static volatile Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

关键点:

  • 使用 volatile 修饰符确保多线程环境中对变量的修改对所有线程立即可见,避免指令重排序问题。
  • 私有化构造方法,禁止外部直接创建实例。
  • 通过 getInstance() 获取唯一实例,确保一个类只有一个实例,并提供全局访问点。
  • 双重检查锁定,第一次检查 instance 是否为 null,避免不必要的同步。进入同步块后再检查一次,以确保线程安全。
  • 使用 synchronized 关键字,保证同一时间只有一个线程能执行实例化代码。

工厂模式

工厂模式(Factory Pattern)是一种创建型设计模式,其目的是通过定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂模式将对象的创建逻辑与使用逻辑分离,使得代码更加灵活和可维护。

定义一个接口或抽象类 Shape,描述所有具体对象的公共行为。

package com.practice.service;

public interface Shape {
    void draw();
}

具体实现类 Circle 实现接口 Shape,提供具体行为。

package com.practice.service.impl;

import com.practice.service.Shape;

public class Circle implements Shape {
    public void draw() {
        System.out.println("画一个圆");
    }
}

具体实现类 Rectangle 实现接口 Shape,提供具体行为。

package com.practice.service.impl;

import com.practice.service.Shape;

public class Rectangle implements Shape {
    public void draw() {
        System.out.println("画一个长方形");
    }
}

ShapeFactory 是工厂类,根据参数 shapeType 动态返回具体对象。

package com.practice.patterns;

import com.practice.service.Shape;
import com.practice.service.impl.Circle;
import com.practice.service.impl.Rectangle;

public class ShapeFactory {
    public static Shape getShape(String shapeType) {
        if ("CIRCLE".equalsIgnoreCase(shapeType)) {
            return new Circle();
        } else if ("RECTANGLE".equalsIgnoreCase(shapeType)) {
            return new Rectangle();
        }
        return null;
    }
}

通过工厂获取对象,无需关心对象的具体实现细节。

package com.practice.app;

import com.practice.patterns.ShapeFactory;
import com.practice.service.Shape;

public class FactoryPatternDemo {
    public static void main(String[] args) {
        Shape circle = ShapeFactory.getShape("CIRCLE");
        circle.draw();
        // 输出:画一个圆

        Shape rectangle = ShapeFactory.getShape("RECTANGLE");
        rectangle.draw();
        // 输出:画一个长方形
    }
}

关键点:

  • 解耦了对象的创建与使用。
  • 更容易扩展,添加新类型时,只需修改工厂代码。

观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会收到通知并自动更新。

定义观察者接口,强制实现 update 方法,用于接收通知。

package com.practice.service;

public interface Observer {
    void update(String message);
}

实现观察者接口

package com.practice.service.impl;

import com.practice.service.Observer;

public class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + " received: " + message);
    }
}

Subject 类维护一个观察者列表,通过 addObserver 和 removeObserver 方法管理观察者。
调用 notifyObservers 通知所有观察者。

package com.practice.common;

import com.practice.service.Observer;

import java.util.ArrayList;
import java.util.List;

public class Subject {
    private List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

动态注册观察者。
当状态变化时,所有观察者都会收到通知。

package com.practice.app;

import com.practice.common.Subject;
import com.practice.service.Observer;
import com.practice.service.impl.ConcreteObserver;

public class ObserverPatternDemo {
    public static void main(String[] args) {
        Subject subject = new Subject();

        Observer observer1 = new ConcreteObserver("Observer1");
        Observer observer2 = new ConcreteObserver("Observer2");

        subject.addObserver(observer1);
        subject.addObserver(observer2);

        subject.notifyObservers("Event 1 occurred!");
        subject.notifyObservers("Event 2 occurred!");

        // 输出
        // Observer1 received: Event 1 occurred!
        // Observer2 received: Event 1 occurred!
        // Observer1 received: Event 2 occurred!
        // Observer2 received: Event 2 occurred!
    }
}

关键点:

  • 实现松耦合,Subject 不需要直接依赖具体的观察者实现。
  • 常用于事件驱动的系统,如 GUI 事件系统、数据模型更新、实时消息系统。

策略模式

策略模式(Strategy Pattern)是一种行为型设计模式,定义了一系列算法,将每种算法封装到独立的类中,并使它们可以互相替换。这种模式使得算法可以在不影响客户端的情况下发生变化,从而满足开闭原则。

定义策略接口,包含一个行为方法 execute。

package com.practice.service;

public interface Strategy {
    int execute(int a, int b);
}

实现策略接口,提供求和算法实现。

package com.practice.service.impl;

import com.practice.service.Strategy;

public class AdditionStrategy implements Strategy {
    public int execute(int a, int b) {
        return a + b;
    }
}

实现策略接口,提供乘法算法实现。

package com.practice.service.impl;

import com.practice.service.Strategy;

public class MultiplicationStrategy implements Strategy {
    public int execute(int a, int b) {
        return a * b;
    }
}

StrategyContext 类持有策略引用,动态调用具体策略实现。

package com.practice.common;

import com.practice.service.Strategy;

public class StrategyContext {
    private Strategy strategy;

    public StrategyContext(Strategy strategy) {
        this.strategy = strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int a, int b) {
        return strategy.execute(a, b);
    }
}

动态切换策略,无需修改已有代码。

package com.practice.app;

import com.practice.common.StrategyContext;
import com.practice.service.impl.AdditionStrategy;
import com.practice.service.impl.MultiplicationStrategy;

public class StrategyPatternDemo {
    public static void main(String[] args) {
        StrategyContext context = new StrategyContext(new AdditionStrategy());
        System.out.println("加法计算结果: " + context.executeStrategy(5, 3));
        // 输出:加法计算结果: 8

        context.setStrategy(new MultiplicationStrategy());
        System.out.println("乘法计算结果: " + context.executeStrategy(5, 3));
        // 输出:乘法计算结果: 15
    }
}

关键点:

  • 策略模式封装了算法,使得算法可以互换。
  • 客户端代码与算法解耦,便于扩展新的算法。

装饰器模式

装饰器模式(Decorator Pattern)是一种结构型设计模式,用于在不修改现有类的情况下动态地为对象添加新的功能。通过将对象嵌套在装饰器中,可以以类似于递归的方式叠加多个功能。

定义组件接口,描述核心功能。

package com.practice.service;

public interface Component {
    void operation();
}

实现组件接口,提供基本功能。

package com.practice.service.impl;

import com.practice.service.Component;

public class ConcreteComponent implements Component {
    public void operation() {
        System.out.println("基本功能");
    }
}

抽象装饰器类持有组件引用,通过组合实现扩展。

package com.practice.patterns;

import com.practice.service.Component;

public abstract class Decorator implements Component {
    protected Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    public void operation() {
        component.operation();
    }
}

具体装饰器实现附加功能A,通过调用父类方法保留原有功能。

package com.practice.common;

import com.practice.patterns.Decorator;
import com.practice.service.Component;

public class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    public void operation() {
        super.operation();
        System.out.println("添加的功能 A");
    }
}

具体装饰器实现附加功能B,通过调用父类方法保留原有功能。

package com.practice.common;

import com.practice.patterns.Decorator;
import com.practice.service.Component;

public class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }

    public void operation() {
        super.operation();
        System.out.println("添加的功能 B");
    }
}

自由组合装饰器,动态添加功能。

package com.practice.app;

import com.practice.common.ConcreteDecoratorA;
import com.practice.common.ConcreteDecoratorB;
import com.practice.service.Component;
import com.practice.service.impl.ConcreteComponent;

public class DecoratorPatternDemo {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Component decoratorA = new ConcreteDecoratorA(component);
        Component decoratorB = new ConcreteDecoratorB(decoratorA);

        decoratorB.operation();
        // 输出
        // 基本功能
        // 添加的功能 A
        // 添加的功能 B
    }
}

关键点:

  • 动态为对象添加职责,而不是通过继承。
  • 提高了系统的灵活性和可扩展性。

适配器模式

适配器模式(Adapter Pattern)是一种结构型设计模式,用于将一个接口转换成客户端期望的另一个接口。适配器模式让原本不兼容的类可以协同工作。

目标接口:定义客户端期望使用的接口,假设新需求是播放 MP4

package com.practice.service;

public interface MediaPlayer {
    void request();
}

被适配者:已有的类,只支持播放 mp3

package com.practice.common;

public class Mp3Player {
    public void playMp3(String fileName) {
        System.out.println("播放 mp3 文件: " + fileName);
    }
}

适配器:将 MediaPlayer 转换为 Mp3Player 的接口

package com.practice.service.impl;

import com.practice.common.Mp3Player;
import com.practice.service.MediaPlayer;

public class MediaAdapter implements MediaPlayer {
    private Mp3Player mp3Player;

    public MediaAdapter() {
        this.mp3Player = new Mp3Player();
    }

    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")) {
            mp3Player.playMp3(fileName);
        } else {
            System.out.println("无效的音频类型: " + audioType);
        }
    }
}

客户端类:使用 MediaPlayer 播放音频

package com.practice.service.impl;

import com.practice.service.MediaPlayer;

public class AudioPlayer implements MediaPlayer {
    private MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("立即播放 mp3 文件: " + fileName);
        } else if (audioType.equalsIgnoreCase("mp4")) {
            mediaAdapter = new MediaAdapter();
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("不支持的音频类型: " + audioType);
        }
    }
}

测试代码

package com.practice.app;

import com.practice.service.impl.AudioPlayer;

public class AdapterPatternDemo {
    public static void main(String[] args) {
        AudioPlayer audioPlayer = new AudioPlayer();

        audioPlayer.play("mp3", "音乐.mp3");
        audioPlayer.play("mp4", "视频.mp4");
        audioPlayer.play("wav", "音频.wav");
        // 输出
        // 立即播放 mp3 文件: 音乐.mp3
        // 无效的音频类型: mp4
        // 不支持的音频类型: wav
    }
}

关键点:

  • 兼容性。
  • 解耦。
  • 扩展性。

总结与选择指南

在实际开发中,选择合适的设计模式可以事半功倍。以下是常用模式的快速选择指南:

设计模式 核心思想 适用场景 举例
单例模式 (Singleton) 确保唯一 全局只需要一个实例,如配置、日志、连接池 Runtime, Spring Bean (默认)
工厂模式 (Factory) 解耦创建 对象创建复杂,或需要根据条件创建不同对象 BeanFactory, Calendar.getInstance()
观察者模式 (Observer) 事件驱动 一个对象变化需通知多个对象,解耦触发与响应 Vue 响应式, EventBus, 消息队列
策略模式 (Strategy) 算法互换 同一任务有多种算法,需要在运行时动态切换 支付方式(阿里/微信), 排序算法
装饰器模式 (Decorator) 动态增强 不修改原类代码,动态添加功能,避免类爆炸 IO流 (BufferedInputStream), 咖啡加糖
适配器模式 (Adapter) 接口转换 接口不兼容,需要复用旧代码或协同工作 Arrays.asList(), 电源适配器

快速判断口诀

  • 全局唯一 找单例
  • 对象复杂 找工厂
  • 多方通知 找观察
  • 算法切换 找策略
  • 功能增强 找装饰
  • 接口兼容 找适配