一文搞懂常用设计模式原理(附Java实现)

设计模式是设计原则的“落地形态”,是前人总结的、解决特定场景问题的通用代码结构。掌握常用设计模式,能让你用成熟的方案解决开发中的高频问题,写出更优雅、易维护的代码。

本文聚焦23种设计模式中最常用的10种,按“创建型、结构型、行为型”三大类划分,每类讲解核心原理、适用场景、Java实现代码,全程贴合实际开发,新手也能快速上手。

一、设计模式总览:分类与核心目标

设计模式按“解决的问题类型”分为三类,核心目标都是解耦、复用、扩展

类型 核心目标 常用模式
创建型 封装对象的创建逻辑 单例、工厂方法、抽象工厂、建造者、原型
结构型 优化类/对象的组合结构 代理、装饰器、适配器、桥接、组合、享元
行为型 优化对象间的交互/职责分配 观察者、策略、模板方法、迭代器、命令

二、高频设计模式详解(原理+Java实现)

(一)创建型模式:搞定对象创建

1. 单例模式(Singleton)

  • 原理:保证一个类在程序中只有一个实例,并提供全局访问点。
  • 适用场景:配置管理、连接池、日志对象等(避免重复创建消耗资源)。
  • 核心实现:私有构造器 + 静态方法/静态变量控制实例创建。
  • Java实现(懒汉式-线程安全版,开发常用)
/**
 * 懒汉式单例:按需创建,加锁保证线程安全
 * 优点:节省内存,线程安全;缺点:加锁有轻微性能损耗
 */
public class Singleton {
    // volatile保证多线程下可见性,防止指令重排
    private static volatile Singleton instance;
    
    // 私有构造器:禁止外部new
    private Singleton() {}
    
    // 双重检查锁:减少锁竞争,提升性能
    public static Singleton getInstance() {
        if (instance == null) { // 第一层检查:避免每次加锁
            synchronized (Singleton.class) { // 加锁保证线程安全
                if (instance == null) { // 第二层检查:防止多线程同时进入第一层
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
    
    // 测试方法
    public void doSomething() {
        System.out.println("单例对象执行操作");
    }
}

// 调用示例
class SingletonTest {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2); // true,只有一个实例
        s1.doSomething();
    }
}

2. 工厂方法模式(Factory Method)

  • 原理:定义创建对象的接口,让子类决定创建哪种对象,将对象创建延迟到子类。
  • 适用场景:产品类型较多,且需要扩展新类型(符合开闭原则)。
  • 核心角色:抽象工厂(创建接口)、具体工厂(实现创建逻辑)、抽象产品、具体产品。
  • Java实现(支付场景)
// 1. 抽象产品:支付接口
public interface Pay {
    void pay(double amount);
}

// 2. 具体产品:支付宝支付
public class Alipay implements Pay {
    @Override
    public void pay(double amount) {
        System.out.println("支付宝支付:" + amount + "元");
    }
}

// 3. 具体产品:微信支付
public class WechatPay implements Pay {
    @Override
    public void pay(double amount) {
        System.out.println("微信支付:" + amount + "元");
    }
}

// 4. 抽象工厂:支付工厂接口
public interface PayFactory {
    Pay createPay();
}

// 5. 具体工厂:支付宝工厂
public class AlipayFactory implements PayFactory {
    @Override
    public Pay createPay() {
        return new Alipay();
    }
}

// 6. 具体工厂:微信支付工厂
public class WechatPayFactory implements PayFactory {
    @Override
    public Pay createPay() {
        return new WechatPay();
    }
}

// 调用示例
class FactoryMethodTest {
    public static void main(String[] args) {
        // 创建支付宝支付
        PayFactory alipayFactory = new AlipayFactory();
        Pay alipay = alipayFactory.createPay();
        alipay.pay(100);
        
        // 创建微信支付(新增类型无需修改原有代码,符合开闭原则)
        PayFactory wechatFactory = new WechatPayFactory();
        Pay wechat = wechatFactory.createPay();
        wechat.pay(200);
    }
}

3. 建造者模式(Builder)

  • 原理:将复杂对象的构建与表示分离,分步构建对象,且构建过程可复用。
  • 适用场景:对象属性多、可选参数多(如订单、用户、汽车对象)。
  • Java实现(用户对象构建)
/**
 * 复杂对象:用户(含必填+可选属性)
 */
public class User {
    // 必填属性
    private final String username;
    private final String password;
    // 可选属性
    private final String phone;
    private final String email;
    private final int age;

    // 私有构造器:仅Builder能调用
    private User(Builder builder) {
        this.username = builder.username;
        this.password = builder.password;
        this.phone = builder.phone;
        this.email = builder.email;
        this.age = builder.age;
    }

    // 静态内部建造者类
    public static class Builder {
        // 必填属性(无默认值)
        private final String username;
        private final String password;
        // 可选属性(默认值)
        private String phone = "";
        private String email = "";
        private int age = 0;

        // Builder构造器:强制传入必填属性
        public Builder(String username, String password) {
            this.username = username;
            this.password = password;
        }

        // 链式设置可选属性
        public Builder phone(String phone) {
            this.phone = phone;
            return this;
        }

        public Builder email(String email) {
            this.email = email;
            return this;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        // 构建最终对象
        public User build() {
            return new User(this);
        }
    }

    // 重写toString方便测试
    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", phone='" + phone + '\'' +
                ", email='" + email + '\'' +
                ", age=" + age +
                '}';
    }
}

// 调用示例
class BuilderTest {
    public static void main(String[] args) {
        // 分步构建用户:必填+部分可选
        User user = new User.Builder("zhangsan", "123456")
                .phone("13800138000")
                .age(25)
                .build();
        
        System.out.println(user);
        // 输出:User{username='zhangsan', phone='13800138000', email='', age=25}
    }
}

(二)结构型模式:优化对象组合

1. 代理模式(Proxy)

  • 原理:为目标对象提供一个代理对象,控制对目标对象的访问,可在访问前后增加额外逻辑。
  • 适用场景:权限控制、日志记录、缓存、远程调用(如RPC)。
  • 核心分类:静态代理(编译期确定)、动态代理(运行时生成)。
  • Java实现(静态代理-日志增强)
// 1. 抽象主题:业务接口
public interface OrderService {
    void createOrder(String orderId);
}

// 2. 真实主题:目标业务类
public class OrderServiceImpl implements OrderService {
    @Override
    public void createOrder(String orderId) {
        System.out.println("创建订单:" + orderId);
    }
}

// 3. 代理类:增强目标对象逻辑
public class OrderServiceProxy implements OrderService {
    // 持有目标对象
    private final OrderService target;

    public OrderServiceProxy(OrderService target) {
        this.target = target;
    }

    @Override
    public void createOrder(String orderId) {
        // 前置增强:日志记录
        System.out.println("【日志】创建订单开始,订单ID:" + orderId);
        // 调用目标对象方法
        target.createOrder(orderId);
        // 后置增强:日志记录
        System.out.println("【日志】创建订单结束,订单ID:" + orderId);
    }
}

// 调用示例
class ProxyTest {
    public static void main(String[] args) {
        // 目标对象
        OrderService target = new OrderServiceImpl();
        // 代理对象
        OrderService proxy = new OrderServiceProxy(target);
        // 通过代理调用方法(自动增强)
        proxy.createOrder("001");
    }
}

2. 装饰器模式(Decorator)

  • 原理:动态给对象添加额外职责,不改变原有类结构,比继承更灵活。
  • 适用场景:给对象动态增加功能,且功能可组合(如IO流、咖啡加配料)。
  • 核心区别(与代理):装饰器关注增强对象功能,代理关注控制对象访问
  • Java实现(咖啡加配料)
// 1. 抽象组件:咖啡
public interface Coffee {
    double cost(); // 计算价格
    String getName(); // 获取名称
}

// 2. 具体组件:基础咖啡(美式咖啡)
public class Americano implements Coffee {
    @Override
    public double cost() {
        return 10; // 基础价格10元
    }

    @Override
    public String getName() {
        return "美式咖啡";
    }
}

// 3. 抽象装饰器:配料
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee; // 持有被装饰的咖啡对象

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
}

// 4. 具体装饰器:牛奶
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return coffee.cost() + 2; // 基础价格+牛奶2元
    }

    @Override
    public String getName() {
        return coffee.getName() + "+牛奶";
    }
}

// 5. 具体装饰器:糖
public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return coffee.cost() + 1; // 基础价格+糖1元
    }

    @Override
    public String getName() {
        return coffee.getName() + "+糖";
    }
}

// 调用示例
class DecoratorTest {
    public static void main(String[] args) {
        // 基础咖啡
        Coffee coffee = new Americano();
        System.out.println(coffee.getName() + ":" + coffee.cost() + "元"); // 美式咖啡:10.0元
        
        // 加牛奶
        coffee = new MilkDecorator(coffee);
        System.out.println(coffee.getName() + ":" + coffee.cost() + "元"); // 美式咖啡+牛奶:12.0元
        
        // 再加糖(组合增强)
        coffee = new SugarDecorator(coffee);
        System.out.println(coffee.getName() + ":" + coffee.cost() + "元"); // 美式咖啡+牛奶+糖:13.0元
    }
}

3. 适配器模式(Adapter)

  • 原理:将一个类的接口转换成客户端期望的另一个接口,解决接口不兼容问题
  • 适用场景:集成第三方组件、复用已有类但接口不匹配(如老系统对接新接口)。
  • 核心角色:目标接口(客户端期望)、适配者(原有接口)、适配器(转换接口)。
  • Java实现(老支付接口适配新接口)
// 1. 目标接口:新系统期望的支付接口
public interface NewPayService {
    void unifiedPay(double amount); // 统一支付方法
}

// 2. 适配者:老系统的支付接口(无法修改)
public class OldAlipayService {
    // 老接口方法名/参数与新接口不一致
    public void alipayPay(double money) {
        System.out.println("老支付宝接口支付:" + money + "元");
    }
}

// 3. 适配器:将老接口适配为新接口
public class PayAdapter implements NewPayService {
    // 持有老接口对象
    private OldAlipayService oldAlipayService;

    public PayAdapter(OldAlipayService oldAlipayService) {
        this.oldAlipayService = oldAlipayService;
    }

    @Override
    public void unifiedPay(double amount) {
        // 适配转换:调用老接口
        oldAlipayService.alipayPay(amount);
    }
}

// 调用示例
class AdapterTest {
    public static void main(String[] args) {
        // 老接口对象
        OldAlipayService oldService = new OldAlipayService();
        // 适配为新接口
        NewPayService newService = new PayAdapter(oldService);
        // 客户端按新接口调用(无需关心老接口细节)
        newService.unifiedPay(100);
    }
}

(三)行为型模式:优化对象交互

1. 观察者模式(Observer)

  • 原理:定义对象间的一对多依赖,当一个对象(主题)状态变化时,所有依赖它的对象(观察者)自动收到通知并更新
  • 适用场景:消息通知、事件监听、发布订阅(如订单状态变更通知物流、库存)。
  • Java实现(订单状态通知)
import java.util.ArrayList;
import java.util.List;

// 1. 抽象主题:订单主题
public interface OrderSubject {
    // 注册观察者
    void registerObserver(OrderObserver observer);
    // 移除观察者
    void removeObserver(OrderObserver observer);
    // 通知所有观察者
    void notifyObservers(String orderId, String status);
}

// 2. 具体主题:订单服务(被观察对象)
public class OrderService implements OrderSubject {
    // 观察者列表
    private List<OrderObserver> observers = new ArrayList<>();

    @Override
    public void registerObserver(OrderObserver observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(OrderObserver observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers(String orderId, String status) {
        // 遍历通知所有观察者
        for (OrderObserver observer : observers) {
            observer.update(orderId, status);
        }
    }

    // 订单状态变更方法
    public void updateOrderStatus(String orderId, String status) {
        System.out.println("订单" + orderId + "状态变更为:" + status);
        // 通知观察者
        notifyObservers(orderId, status);
    }
}

// 3. 抽象观察者
public interface OrderObserver {
    void update(String orderId, String status);
}

// 4. 具体观察者1:物流系统
public class LogisticsObserver implements OrderObserver {
    @Override
    public void update(String orderId, String status) {
        if ("已支付".equals(status)) {
            System.out.println("物流系统:订单" + orderId + "已支付,开始备货发货");
        }
    }
}

// 5. 具体观察者2:库存系统
public class StockObserver implements OrderObserver {
    @Override
    public void update(String orderId, String status) {
        if ("已支付".equals(status)) {
            System.out.println("库存系统:订单" + orderId + "已支付,扣减库存");
        }
    }
}

// 调用示例
class ObserverTest {
    public static void main(String[] args) {
        // 创建主题(订单服务)
        OrderService orderService = new OrderService();
        
        // 注册观察者
        orderService.registerObserver(new LogisticsObserver());
        orderService.registerObserver(new StockObserver());
        
        // 变更订单状态(自动通知观察者)
        orderService.updateOrderStatus("001", "已支付");
    }
}

2. 策略模式(Strategy)

  • 原理:定义一系列算法,将每个算法封装成独立类,算法可互相替换,且算法变化不影响使用算法的客户端。
  • 适用场景:多种算法/规则可选(如排序算法、支付方式、优惠策略)。
  • Java实现(订单优惠策略)
// 1. 抽象策略:优惠接口
public interface DiscountStrategy {
    double calculate(double amount); // 计算优惠后金额
}

// 2. 具体策略1:满减优惠(满100减20)
public class FullReduceStrategy implements DiscountStrategy {
    @Override
    public double calculate(double amount) {
        if (amount >= 100) {
            return amount - 20;
        }
        return amount;
    }
}

// 3. 具体策略2:折扣优惠(9折)
public class DiscountStrategyImpl implements DiscountStrategy {
    @Override
    public double calculate(double amount) {
        return amount * 0.9;
    }
}

// 4. 上下文:订单(持有策略,调用策略)
public class OrderContext {
    private DiscountStrategy strategy;

    // 动态设置策略
    public void setStrategy(DiscountStrategy strategy) {
        this.strategy = strategy;
    }

    // 计算最终金额
    public double calculateFinalAmount(double amount) {
        return strategy.calculate(amount);
    }
}

// 调用示例
class StrategyTest {
    public static void main(String[] args) {
        OrderContext order = new OrderContext();
        double originalAmount = 100;

        // 使用满减策略
        order.setStrategy(new FullReduceStrategy());
        System.out.println("满减后金额:" + order.calculateFinalAmount(originalAmount)); // 80.0

        // 切换为折扣策略(无需修改OrderContext)
        order.setStrategy(new DiscountStrategyImpl());
        System.out.println("折扣后金额:" + order.calculateFinalAmount(originalAmount)); // 90.0
    }
}

3. 模板方法模式(Template Method)

  • 原理:定义一个算法的骨架,将算法中可变的步骤延迟到子类实现,固定不变的步骤在父类中实现。
  • 适用场景:多个子类有通用流程,仅部分步骤不同(如登录流程、支付流程)。
  • Java实现(登录流程)
// 1. 抽象父类:定义登录流程骨架
public abstract class AbstractLoginTemplate {
    // 模板方法:固定登录流程(final防止子类重写)
    public final void login(String username, String password) {
        // 步骤1:验证参数(固定)
        if (!checkParams(username, password)) {
            System.out.println("参数为空,登录失败");
            return;
        }
        // 步骤2:验证账号密码(可变,子类实现)
        if (verify(username, password)) {
            // 步骤3:登录成功处理(固定)
            success();
        } else {
            // 步骤4:登录失败处理(固定)
            fail();
        }
    }

    // 固定步骤:验证参数
    private boolean checkParams(String username, String password) {
        return username != null && !username.isEmpty() 
                && password != null && !password.isEmpty();
    }

    // 可变步骤:验证账号密码(抽象方法,子类实现)
    protected abstract boolean verify(String username, String password);

    // 固定步骤:登录成功
    private void success() {
        System.out.println("登录成功,跳转到首页");
    }

    // 固定步骤:登录失败
    private void fail() {
        System.out.println("账号或密码错误,登录失败");
    }
}

// 2. 具体子类:密码登录
public class PasswordLogin extends AbstractLoginTemplate {
    @Override
    protected boolean verify(String username, String password) {
        // 模拟数据库验证
        return "admin".equals(username) && "123456".equals(password);
    }
}

// 3. 具体子类:短信验证码登录
public class SmsLogin extends AbstractLoginTemplate {
    @Override
    protected boolean verify(String username, String password) {
        // 模拟短信验证码验证
        return "admin".equals(username) && "666666".equals(password);
    }
}

// 调用示例
class TemplateMethodTest {
    public static void main(String[] args) {
        // 密码登录
        AbstractLoginTemplate passwordLogin = new PasswordLogin();
        passwordLogin.login("admin", "123456"); // 登录成功
        
        // 短信登录
        AbstractLoginTemplate smsLogin = new SmsLogin();
        smsLogin.login("admin", "666666"); // 登录成功
    }
}

三、设计模式使用技巧与避坑

1. 核心使用原则

  • 按需选择:不要为了用模式而用模式,简单场景直接写代码(如只有一个产品的工厂模式无意义);
  • 结合设计原则:所有模式都遵循SOLID原则(如策略模式符合开闭原则,单例模式符合单一职责);
  • 优先组合:设计模式中大量使用组合/聚合(如装饰器、观察者),而非继承。

2. 常见坑点

  • 过度设计:用简单代码能解决的问题,不要强行套模式(如小项目用抽象工厂);
  • 模式混淆
    • 代理vs装饰器:代理控制访问,装饰器增强功能;
    • 适配器vs装饰器:适配器解决接口不兼容,装饰器增强功能;
  • 忽略性能:如单例模式的懒汉式加锁、装饰器多层嵌套可能影响性能。

四、总结

核心要点回顾

  1. 三大分类核心:创建型封装对象创建,结构型优化对象组合,行为型优化对象交互;
  2. 高频模式重点:单例(唯一实例)、工厂方法(延迟创建)、代理(控制访问)、观察者(发布订阅)、策略(算法替换)是开发中最常用的5种模式;
  3. 使用核心:设计模式是“工具”,不是“规则”,结合场景选择,优先保证代码简洁、易维护。

落地建议

  • 新手先掌握上述10种模式,再逐步学习抽象工厂、桥接、命令等进阶模式;
  • 阅读JDK源码(如java.util.Observerjava.io包),理解设计模式的实际应用;
  • 项目中重构时,用设计模式优化“臃肿、难扩展”的代码(如用策略模式替代大量if-else)。

如果需要某一种模式的进阶用法(如动态代理、抽象工厂)或源码分析(如Spring中的设计模式),可以告诉我,我会补充对应的代码和解析。

posted @ 2026-03-12 15:56  七星6609  阅读(1)  评论(0)    收藏  举报