Skip to content
0

设计模式 ​

软件设计原则 ​

1. 开闭原则(Open-Closed Principle) ​

对扩展开放、对修改关闭。

软件实体应当对扩展开放,对修改关闭。也就是说,当应用的需求改变时,在不修改软件实体的源代码前提下,可以通过扩展模块的行为来满足新的需求。

在 Java 中通常使用接口和抽象类来定义规范,从抽象派生的实现类来进行扩展。当需求变化时,只需要重新派生一个实现类来扩展即可,而无需修改原有代码。

2. 里氏代换原则(Liskov Substitution Principle) ​

子类可以扩展父类的功能,但不能改变父类原有的功能。

在 Java 中,里氏代换原则要求:子类可以新增方法来实现新功能,但尽量不要重写父类的方法(尤其是非抽象方法)。如果必须重写,也要保证子类的行为与父类一致,不会破坏原有逻辑。

3. 依赖倒置原则(Dependency Inversion Principle) ​

高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。

该原则的核心是面向接口编程,通过引入抽象层来降低模块间的耦合度。

java
// 抽象接口
interface Phone {
    void call();
}

// 低层模块
class ApplePhone implements Phone {
    public void call() {
        System.out.println("使用 iPhone 打电话");
    }
}

class HuaweiPhone implements Phone {
    public void call() {
        System.out.println("使用华为手机打电话");
    }
}

// 高层模块
class Person {
    private Phone phone;
    
    // 依赖注入:通过构造函数传入抽象接口
    public Person(Phone phone) {
        this.phone = phone;
    }
    
    public void usePhone() {
        phone.call();
    }
}

public class Main {
    public static void main(String[] args) {
        // 可以轻松替换不同的手机,而无需修改 Person 类
        Person person1 = new Person(new ApplePhone());
        person1.usePhone();
        
        Person person2 = new Person(new HuaweiPhone());
        person2.usePhone();
    }
}

4. 接口隔离原则(Interface Segregation Principle) ​

客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。

应该将臃肿的大接口拆分为更细粒度的接口,让实现类只关心自己需要的方法。

java
// 将大接口拆分为多个小接口
interface Printer {
    void print();
}

interface Scanner {
    void scan();
}

// 多功能一体机实现多个接口
class MultiFunctionMachine implements Printer, Scanner {
    public void print() {
        System.out.println("打印文件");
    }
    
    public void scan() {
        System.out.println("扫描文件");
    }
}

// 普通打印机只需实现 Printer 接口
class SimplePrinter implements Printer {
    public void print() {
        System.out.println("打印文件");
    }
}

5. 迪米特法则(Law of Demeter) ​

只和你的直接朋友交谈,不跟陌生人说话。

如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

6. 合成复用原则(Composite Reuse Principle) ​

尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

继承是强耦合关系,父类的任何改变都可能影响子类。而组合/聚合是弱耦合关系,更加灵活。

java
class A {
    public void doSomething() {
        System.out.println("A 的方法");
    }
}

// 继承复用:强耦合,A 的改变会影响 B
class B extends A {
}

// 组合复用:弱耦合,更灵活
class C {
    private A a = new A();
    
    public void doSomething() {
        a.doSomething(); // 委托给 A
    }
}

创建型模式 ​

创建型模式关注如何创建对象,其核心思想是将对象的创建与使用分离,降低系统的耦合度。

1. 单例模式(Singleton) ​

定义:确保一个类只有一个实例,并提供一个全局访问点。

适用场景:线程池、缓存、日志对象、对话框、打印机驱动等全局唯一的资源。

饿汉式:类加载时即创建实例 ​

静态成员变量方式

java
class Singleton {
    // 1. 私有构造方法,防止外部实例化
    private Singleton() {}
    
    // 2. 类加载时即创建实例
    private static final Singleton instance = new Singleton();
    
    // 3. 提供公共访问方式
    public static Singleton getInstance() {
        return instance;
    }
}

静态代码块方式

java
class Singleton {
    private Singleton() {}
    private static final Singleton instance;
    
    static {
        instance = new Singleton();
    }

    public static Singleton getInstance() {
        return instance;
    }
}

优点:线程安全,实现简单,无锁开销。
缺点:类加载即初始化,如果实例从未使用则造成资源浪费。

懒汉式:首次使用时创建实例 ​

线程安全版(同步方法)

java
class Singleton {
    private Singleton() {}
    private static Singleton instance;

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

优点:延迟加载。
缺点:每次获取实例都加锁,性能较差。

双重检查锁定(DCL)—— 推荐

java
class Singleton {
    private Singleton() {}
    
    // volatile 禁止指令重排序,确保多线程安全
    private static volatile Singleton instance;

    public static Singleton getInstance() {
        if (instance == null) {                    // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) {            // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

优点:延迟加载,线程安全,性能高(仅第一次初始化时加锁)。

静态内部类方式 —— 最优雅

java
class Singleton {
    private Singleton() {}
    
    // JVM 保证类加载的线程安全,且内部类延迟加载
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

优点:利用 JVM 类加载机制保证线程安全,实现延迟加载,代码简洁。

枚举方式 —— 最简洁(Effective Java 推荐)

java
public enum Singleton {
    INSTANCE;
    
    public void doSomething() {
        System.out.println("单例方法");
    }
}

// 使用
Singleton.INSTANCE.doSomething();

优点:天生线程安全,防止反射和序列化破坏单例。
缺点:无法延迟加载,且无法继承其他类。

破坏单例的方式及防御 ​

1. 反射攻击

java
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance2 = constructor.newInstance(); // 创建新实例!

防御方案:在构造函数中增加判断

java
class Singleton {
    private static boolean created = false;
    
    private Singleton() {
        if (created) {
            throw new RuntimeException("单例对象已存在,禁止反射创建");
        }
        created = true;
    }
}

2. 序列化攻击

java
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
oos.writeObject(instance1);

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton.ser"));
Singleton instance2 = (Singleton) ois.readObject(); // 可能是新实例!

防御方案:添加 readResolve() 方法

java
class Singleton implements Serializable {
    // ... 其他代码
    
    // 反序列化时直接返回单例实例
    protected Object readResolve() {
        return getInstance();
    }
}

2. 工厂模式 ​

定义:定义一个创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。

简单工厂模式(静态工厂) ​

简单工厂不是 GoF 23 种设计模式之一,但它是工厂模式的基础。它根据传入的参数类型,创建并返回对应的产品实例。

java
// 产品接口
interface Coffee {
    void make();
}

class Americano implements Coffee {
    public void make() {
        System.out.println("制作美式咖啡");
    }
}

class Latte implements Coffee {
    public void make() {
        System.out.println("制作拿铁咖啡");
    }
}

// 简单工厂
class SimpleCoffeeFactory {
    public static Coffee createCoffee(String type) {
        if ("americano".equals(type)) {
            return new Americano();
        } else if ("latte".equals(type)) {
            return new Latte();
        }
        throw new IllegalArgumentException("未知咖啡类型");
    }
}

// 使用
Coffee coffee = SimpleCoffeeFactory.createCoffee("latte");
coffee.make();

缺点:增加新产品时需要修改工厂类,违反开闭原则。

工厂方法模式 ​

将产品创建逻辑下沉到各个具体工厂子类中,每个产品对应一个工厂。

java
// 抽象产品
abstract class Document {
    public abstract void open();
}

// 具体产品
class WordDocument extends Document {
    public void open() {
        System.out.println("打开 Word 文档");
    }
}

class PdfDocument extends Document {
    public void open() {
        System.out.println("打开 PDF 文档");
    }
}

// 抽象工厂
interface DocumentFactory {
    Document createDocument();
}

// 具体工厂
class WordFactory implements DocumentFactory {
    public Document createDocument() {
        return new WordDocument();
    }
}

class PdfFactory implements DocumentFactory {
    public Document createDocument() {
        return new PdfDocument();
    }
}

// 使用
DocumentFactory factory = new WordFactory();
Document doc = factory.createDocument();
doc.open();

抽象工厂模式 ​

工厂方法模式考虑的是一类产品的生产,而抽象工厂可以生产多个类别等级的产品族。

java
// 产品族:按钮
interface Button {
    void click();
}

class WindowsButton implements Button {
    public void click() {
        System.out.println("Windows 风格按钮");
    }
}

class MacButton implements Button {
    public void click() {
        System.out.println("Mac 风格按钮");
    }
}

// 产品族:文本框
interface TextField {
    void input();
}

class WindowsTextField implements TextField {
    public void input() {
        System.out.println("Windows 风格文本框");
    }
}

class MacTextField implements TextField {
    public void input() {
        System.out.println("Mac 风格文本框");
    }
}

// 抽象工厂:UI 组件工厂
interface GUIFactory {
    Button createButton();
    TextField createTextField();
}

// 具体工厂
class WindowsFactory implements GUIFactory {
    public Button createButton() {
        return new WindowsButton();
    }
    public TextField createTextField() {
        return new WindowsTextField();
    }
}

class MacFactory implements GUIFactory {
    public Button createButton() {
        return new MacButton();
    }
    public TextField createTextField() {
        return new MacTextField();
    }
}

在 JDK 中的应用 ​

Collection.iterator() 就是工厂方法模式的典型应用:

java
List<String> list = new ArrayList<>();
Iterator<String> iterator = list.iterator(); // 工厂方法

Set<String> set = new HashSet<>();
Iterator<String> setIterator = set.iterator(); // 返回不同的迭代器实现

3. 原型模式(Prototype) ​

定义:以一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。

原型模式的克隆分为浅克隆和深克隆。

浅克隆 ​

只复制对象本身和基本数据类型字段,引用类型字段仍然指向原对象的内存地址。

java
public class Resume implements Cloneable {
    private String name;
    private int age;
    private List<String> skills; // 引用类型
    
    @Override
    protected Resume clone() throws CloneNotSupportedException {
        return (Resume) super.clone();
    }
    
    // getters and setters...
}

// 使用
Resume resume1 = new Resume("张三", 25, Arrays.asList("Java", "Python"));
Resume resume2 = resume1.clone();

// 浅克隆的问题:引用类型共享同一个对象
resume2.getSkills().add("Go");
System.out.println(resume1.getSkills()); // [Java, Python, Go] —— 原对象也被修改了!

深克隆 ​

不仅复制对象本身,还会递归复制所有引用类型字段指向的对象,产生完全独立的副本。

方式一:递归实现 Cloneable

java
public class Resume implements Cloneable {
    private String name;
    private int age;
    private List<String> skills;
    private Address address; // 嵌套引用类型
    
    @Override
    protected Resume clone() throws CloneNotSupportedException {
        Resume clone = (Resume) super.clone();
        // 手动克隆引用类型
        clone.skills = new ArrayList<>(this.skills);
        clone.address = this.address.clone();
        return clone;
    }
}

class Address implements Cloneable {
    private String city;
    
    @Override
    protected Address clone() throws CloneNotSupportedException {
        return (Address) super.clone();
    }
}

方式二:序列化实现(推荐)

java
import java.io.*;

public class Resume implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    private List<String> skills;
    private Address address;
    
    // 深克隆:通过序列化实现
    public Resume deepClone() {
        try {
            // 序列化
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            
            // 反序列化
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            return (Resume) ois.readObject();
        } catch (Exception e) {
            throw new RuntimeException("深克隆失败", e);
        }
    }
}

注意:序列化方式要求所有引用类型都实现 Serializable 接口。


结构型模式 ​

结构型模式关注如何将类或对象组合成更大的结构。

4. 代理模式(Proxy) ​

定义:为其他对象提供一种代理以控制对这个对象的访问。

java
// 主题接口
interface Image {
    void display();
}

// 真实主题
class RealImage implements Image {
    private String filename;
    
    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk(); // 模拟耗时加载
    }
    
    private void loadFromDisk() {
        System.out.println("加载图片: " + filename);
    }
    
    public void display() {
        System.out.println("显示图片: " + filename);
    }
}

// 代理
class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;
    
    public ProxyImage(String filename) {
        this.filename = filename;
    }
    
    public void display() {
        // 延迟加载:真正需要显示时才创建真实对象
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

// 使用
Image image = new ProxyImage("photo.jpg");
image.display(); // 此时才真正加载图片

代理模式的类型:

  • 静态代理:手动编写代理类(如上例)
  • 动态代理:运行时生成代理(JDK 动态代理、cglib)
  • 远程代理:为远程对象提供本地代表
  • 保护代理:控制对象的访问权限

5. 适配器模式(Adapter) ​

定义:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

java
// 目标接口
interface MediaPlayer {
    void play(String filename);
}

// 被适配者(已有类,不能修改)
class AdvancedMediaPlayer {
    public void playMp4(String filename) {
        System.out.println("播放 MP4: " + filename);
    }
    public void playVlc(String filename) {
        System.out.println("播放 VLC: " + filename);
    }
}

// 适配器
class MediaAdapter implements MediaPlayer {
    private AdvancedMediaPlayer advancedPlayer;
    
    public MediaAdapter() {
        this.advancedPlayer = new AdvancedMediaPlayer();
    }
    
    public void play(String filename) {
        if (filename.endsWith(".mp4")) {
            advancedPlayer.playMp4(filename);
        } else if (filename.endsWith(".vlc")) {
            advancedPlayer.playVlc(filename);
        }
    }
}

6. 装饰器模式(Decorator) ​

定义:动态地给一个对象添加一些额外的职责,比生成子类更为灵活。

java
// 组件接口
interface Coffee {
    double cost();
    String description();
}

// 具体组件
class SimpleCoffee implements Coffee {
    public double cost() {
        return 10;
    }
    public String description() {
        return "简单咖啡";
    }
}

// 抽象装饰器
abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }
    
    public double cost() {
        return decoratedCoffee.cost();
    }
    
    public String description() {
        return decoratedCoffee.description();
    }
}

// 具体装饰器
class Milk extends CoffeeDecorator {
    public Milk(Coffee coffee) {
        super(coffee);
    }
    
    public double cost() {
        return super.cost() + 2;
    }
    
    public String description() {
        return super.description() + ", 牛奶";
    }
}

class Sugar extends CoffeeDecorator {
    public Sugar(Coffee coffee) {
        super(coffee);
    }
    
    public double cost() {
        return super.cost() + 1;
    }
    
    public String description() {
        return super.description() + ", 糖";
    }
}

// 使用:动态组合各种配料
Coffee coffee = new SimpleCoffee();
coffee = new Milk(coffee);   // 加牛奶
coffee = new Sugar(coffee);  // 加糖

System.out.println(coffee.description()); // 简单咖啡, 牛奶, 糖
System.out.println(coffee.cost());        // 13.0

7. 外观模式(Facade) ​

定义:为子系统中的一组接口提供一个一致的入口,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

java
// 子系统类
class CPU {
    public void start() { System.out.println("CPU 启动"); }
}

class Memory {
    public void load() { System.out.println("内存加载"); }
}

class HardDrive {
    public void read() { System.out.println("硬盘读取"); }
}

// 外观类
class ComputerFacade {
    private CPU cpu;
    private Memory memory;
    private HardDrive hardDrive;
    
    public ComputerFacade() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.hardDrive = new HardDrive();
    }
    
    // 一键启动
    public void start() {
        cpu.start();
        memory.load();
        hardDrive.read();
        System.out.println("电脑启动完成");
    }
}

// 使用
ComputerFacade computer = new ComputerFacade();
computer.start(); // 简化了复杂的子系统调用

行为型模式 ​

行为型模式关注对象之间的通信和责任分配。

8. 策略模式(Strategy) ​

定义:定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。

java
// 策略接口
interface PaymentStrategy {
    void pay(double amount);
}

// 具体策略
class AlipayStrategy implements PaymentStrategy {
    public void pay(double amount) {
        System.out.println("使用支付宝支付 " + amount + " 元");
    }
}

class WeChatPayStrategy implements PaymentStrategy {
    public void pay(double amount) {
        System.out.println("使用微信支付 " + amount + " 元");
    }
}

class CreditCardStrategy implements PaymentStrategy {
    public void pay(double amount) {
        System.out.println("使用信用卡支付 " + amount + " 元");
    }
}

// 上下文
class ShoppingCart {
    private PaymentStrategy paymentStrategy;
    
    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }
    
    public void checkout(double amount) {
        paymentStrategy.pay(amount);
    }
}

// 使用
ShoppingCart cart = new ShoppingCart();
cart.setPaymentStrategy(new AlipayStrategy());
cart.checkout(100);

9. 观察者模式(Observer) ​

定义:定义对象之间的一对多依赖关系,当一个对象状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

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

// 主题接口
interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

// 观察者接口
interface Observer {
    void update(String message);
}

// 具体主题
class NewsPublisher implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String latestNews;
    
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
    
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
    
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(latestNews);
        }
    }
    
    public void publishNews(String news) {
        this.latestNews = news;
        notifyObservers();
    }
}

// 具体观察者
class User implements Observer {
    private String name;
    
    public User(String name) {
        this.name = name;
    }
    
    public void update(String message) {
        System.out.println(name + " 收到新闻: " + message);
    }
}

// 使用
NewsPublisher publisher = new NewsPublisher();
publisher.registerObserver(new User("张三"));
publisher.registerObserver(new User("李四"));

publisher.publishNews("设计模式更新啦!");
// 输出:
// 张三 收到新闻: 设计模式更新啦!
// 李四 收到新闻: 设计模式更新啦!

JDK 内置支持:java.util.Observer(已废弃)和 java.util.Observable,或使用更现代的 PropertyChangeSupport。

10. 模板方法模式(Template Method) ​

定义:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

java
// 抽象类定义模板
abstract class DataImporter {
    // 模板方法:定义算法骨架
    public final void importData() {
        readData();      // 步骤1
        validateData();  // 步骤2(钩子,可选)
        processData();   // 步骤3
        saveData();      // 步骤4
    }
    
    protected abstract void readData();
    protected abstract void processData();
    protected abstract void saveData();
    
    // 钩子方法:子类可选择性重写
    protected void validateData() {
        System.out.println("执行默认数据验证");
    }
}

// 具体实现
class ExcelImporter extends DataImporter {
    protected void readData() {
        System.out.println("读取 Excel 文件");
    }
    protected void processData() {
        System.out.println("处理 Excel 数据");
    }
    protected void saveData() {
        System.out.println("保存到数据库");
    }
}

class CsvImporter extends DataImporter {
    protected void readData() {
        System.out.println("读取 CSV 文件");
    }
    protected void processData() {
        System.out.println("处理 CSV 数据");
    }
    protected void saveData() {
        System.out.println("保存到数据库");
    }
}

// 使用
DataImporter importer = new ExcelImporter();
importer.importData();

11. 迭代器模式(Iterator) ​

定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

java
// 自定义聚合类
class BookShelf implements Iterable<Book> {
    private Book[] books;
    private int last = 0;
    
    public BookShelf(int maxsize) {
        this.books = new Book[maxsize];
    }
    
    public void appendBook(Book book) {
        this.books[last] = book;
        last++;
    }
    
    public Book getBookAt(int index) {
        return books[index];
    }
    
    public int getLength() {
        return last;
    }
    
    // 返回迭代器
    public Iterator<Book> iterator() {
        return new BookShelfIterator(this);
    }
}

// 自定义迭代器
class BookShelfIterator implements Iterator<Book> {
    private BookShelf bookShelf;
    private int index;
    
    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
        this.index = 0;
    }
    
    public boolean hasNext() {
        return index < bookShelf.getLength();
    }
    
    public Book next() {
        Book book = bookShelf.getBookAt(index);
        index++;
        return book;
    }
}

class Book {
    private String name;
    public Book(String name) { this.name = name; }
    public String getName() { return name; }
}

// 使用
BookShelf shelf = new BookShelf(4);
shelf.appendBook(new Book("设计模式"));
shelf.appendBook(new Book("算法导论"));

for (Book book : shelf) {
    System.out.println(book.getName());
}

设计模式速查表 ​

类型模式核心思想
创建型单例模式全局唯一实例
工厂方法子类决定实例化哪个类
抽象工厂创建相关的产品族
原型模式复制已有对象
建造者模式分步骤构建复杂对象
结构型代理模式控制对象访问
适配器模式接口转换兼容
装饰器模式动态添加职责
外观模式简化子系统接口
桥接模式抽象与实现分离
组合模式树形结构统一处理
享元模式共享细粒度对象
行为型策略模式封装 interchangeable 算法
观察者模式一对多依赖通知
模板方法算法骨架,延迟实现
命令模式请求封装为对象
职责链沿链传递请求
状态模式状态驱动行为
迭代器模式顺序访问聚合元素
中介者模式封装对象交互
备忘录模式保存和恢复状态
访问者模式分离算法与结构
解释器模式定义文法解释器
最近更新