面向对象设计模式详解

发布于:2025-07-14 ⋅ 阅读:(30) ⋅ 点赞:(0)

面向对象设计模式详解:从汽车工厂看设计模式的实际应用

引言

设计模式是面向对象软件开发中的"最佳实践",如同汽车制造中的标准化生产流程,它们提供了经过验证的解决方案来应对常见的设计挑战。正如"java进阶.md"中所述,设计模式的概念最初源于建筑领域,1995年由"四人组"(Gang of Four, GoF)引入软件领域,收录了23个经典模式。

在这里插入图片描述

学习设计模式就像学习汽车制造的标准化流程——掌握它们可以帮助开发者构建更可靠、更灵活、更易于维护的软件系统。本文将以汽车制造为核心案例,详细解释9种常用设计模式,让复杂概念变得直观易懂。

设计模式分类

设计模式主要分为三大类,如同汽车制造中的不同环节:

  1. 创建型模式:处理对象创建机制,如同汽车工厂中的生产线设计
  2. 结构型模式:关注类和对象的组合,如同汽车零部件的组装方式
  3. 行为型模式:关注对象之间的通信,如同汽车各系统间的协作机制

一、创建型设计模式

1.1 单例模式(Singleton Pattern)

单例模式确保一个类只有一个实例,并提供一个全局访问点,如同汽车工厂中的中央控制系统——整个工厂只需要一个总控中心。

生活中的单例:汽车工厂的总控中心

在汽车制造中,整个工厂的生产调度系统只能有一个实例,否则会出现调度混乱。单例模式确保无论多少生产线请求调度,都只会与同一个总控中心交互。

实现方式

1. 饿汉式(立即初始化)

// 汽车工厂总控中心(饿汉式单例)
public class ProductionControlCenter {
    // 类加载时立即初始化实例
    private static final ProductionControlCenter INSTANCE = new ProductionControlCenter();
    
    // 私有构造器,防止外部实例化
    private ProductionControlCenter() {
        System.out.println("初始化生产总控中心");
    }
    
    // 全局访问点
    public static ProductionControlCenter getInstance() {
        return INSTANCE;
    }
    
    // 调度生产
    public void scheduleProduction(String carModel) {
        System.out.println("调度生产: " + carModel);
    }
}

2. 懒汉式(延迟初始化)

// 汽车工厂总控中心(懒汉式单例)
public class LazyProductionControlCenter {
    private static LazyProductionControlCenter instance;

    private LazyProductionControlCenter() {
        System.out.println("延迟初始化生产总控中心");
    }

    // 同步方法确保线程安全
    public static synchronized LazyProductionControlCenter getInstance() {
        if (instance == null) {
            instance = new LazyProductionControlCenter();
        }
        return instance;
    }
}

3. 双重检查锁定(高效线程安全)

public class DoubleCheckedLockingControlCenter {
    // volatile确保多线程下的可见性
    private static volatile DoubleCheckedLockingControlCenter instance;

    private DoubleCheckedLockingControlCenter() {}

    public static DoubleCheckedLockingControlCenter getInstance() {
        if (instance == null) { // 第一次检查:避免不必要的同步
            synchronized (DoubleCheckedLockingControlCenter.class) {
                if (instance == null) { // 第二次检查:确保只初始化一次
                    instance = new DoubleCheckedLockingControlCenter();
                }
            }
        }
        return instance;
    }
}

4. 静态内部类(推荐方式)

public class StaticInnerClassControlCenter {
    private StaticInnerClassControlCenter() {}

    // 静态内部类,只有在调用getInstance时才会加载
    private static class SingletonHolder {
        private static final StaticInnerClassControlCenter INSTANCE = 
            new StaticInnerClassControlCenter();
    }

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

5. 枚举(最简洁安全的方式)

public enum EnumControlCenter {
    INSTANCE;
    
    public void scheduleProduction(String carModel) {
        System.out.println("调度生产: " + carModel);
    }
}
线程安全性对比
实现方式 是否线程安全 懒加载 优点 缺点
饿汉式 实现简单,绝对安全 可能浪费资源
懒汉式(同步方法) 实现简单,按需加载 性能较差
双重检查锁定 性能好,按需加载 实现复杂
静态内部类 实现简洁,性能好 无法传参
枚举 绝对安全,防反射 无法懒加载
应用场景
  • 配置管理:应用的配置类,确保配置只加载一次
  • 资源池:数据库连接池、线程池,控制资源数量
  • 日志系统:全局日志对象,确保日志输出顺序
  • 设备驱动:如打印机驱动,避免多个驱动实例冲突

1.2 工厂方法模式(Factory Method Pattern)

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,如同汽车制造中,每个品牌有自己的工厂,生产自己品牌的汽车。

汽车工厂案例:不同品牌的汽车生产线

假设我们要创建一个汽车制造系统,支持不同品牌的汽车生产。使用工厂方法模式,我们可以为每个品牌创建一个具体工厂,负责生产该品牌的汽车。

UML类图
┌───────────────────┐     ┌───────────────────┐
│     Car           │     │   CarFactory      │
├───────────────────┤     ├───────────────────┤
│ +drive()          │     │ +createCar()      │
└───────────────────┘     └───────────────────┘
        ▲                             ▲
        │                             │
┌───────────────────┐     ┌───────────────────┐
│   BMWCar          │     │   BMWFactory      │
├───────────────────┤     ├───────────────────┤
│ +drive()          │     │ +createCar()      │
└───────────────────┘     └───────────────────┘
        ▲                             ▲
        │                             │
┌───────────────────┐     ┌───────────────────┐
│  BenzCar          │     │  BenzFactory      │
├───────────────────┤     ├───────────────────┤
│ +drive()          │     │ +createCar()      │
└───────────────────┘     └───────────────────┘
代码示例:汽车品牌工厂

1. 产品接口(汽车)

// 汽车接口
public interface Car {
    void drive();  // 汽车可以行驶
    String getBrand();  // 获取品牌
}

2. 具体产品(各品牌汽车)

// 宝马汽车
public class BMWCar implements Car {
    @Override
    public void drive() {
        System.out.println("驾驶宝马汽车,体验纯粹驾驶乐趣");
    }
    
    @Override
    public String getBrand() {
        return "BMW";
    }
}

// 奔驰汽车
public class BenzCar implements Car {
    @Override
    public void drive() {
        System.out.println("驾驶奔驰汽车,感受豪华舒适");
    }
    
    @Override
    public String getBrand() {
        return "Benz";
    }
}

3. 抽象工厂(汽车工厂)

// 汽车工厂接口
public abstract class CarFactory {
    // 工厂方法:创建汽车
    public abstract Car createCar();
    
    // 生产汽车的通用流程
    public Car produceCar() {
        Car car = createCar();
        System.out.println("生产" + car.getBrand() + "汽车");
        return car;
    }
}

4. 具体工厂(各品牌工厂)

// 宝马工厂
public class BMWFactory extends CarFactory {
    @Override
    public Car createCar() {
        // 宝马汽车的具体生产过程
        return new BMWCar();
    }
}

// 奔驰工厂
public class BenzFactory extends CarFactory {
    @Override
    public Car createCar() {
        // 奔驰汽车的具体生产过程
        return new BenzCar();
    }
}

5. 客户端代码

public class CarManufacturer {
    public static void main(String[] args) {
        // 创建宝马工厂并生产宝马汽车
        CarFactory bmwFactory = new BMWFactory();
        Car bmw = bmwFactory.produceCar();
        bmw.drive();
        
        // 创建奔驰工厂并生产奔驰汽车
        CarFactory benzFactory = new BenzFactory();
        Car benz = benzFactory.produceCar();
        benz.drive();
        
        // 如果要增加奥迪品牌,只需添加AudiCar和AudiFactory
        // 无需修改现有代码,符合开闭原则
    }
}
优点
  • 符合开闭原则:新增产品只需添加新的工厂类,无需修改现有代码
  • 单一职责:每个工厂只负责生产一种类型的产品
  • 解耦:客户端无需知道具体产品的创建细节,只需关心工厂接口
应用场景
  • 框架设计:如Spring中的BeanFactory,允许子类决定实例化哪个Bean
  • 日志系统:不同日志实现(文件日志、数据库日志)对应不同工厂
  • UI组件库:不同平台的UI组件(Windows、Mac)由不同工厂创建

1.3 抽象工厂模式(Abstract Factory Pattern)

抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,如同汽车制造中,每个品牌不仅生产轿车,还生产SUV、跑车等不同系列,形成完整的产品族。

汽车工厂案例:品牌产品族

假设宝马和奔驰不仅生产轿车,还生产SUV。抽象工厂模式可以创建一个抽象工厂,定义生产不同类型汽车的接口,每个品牌工厂实现这个接口,生产完整的产品系列。

UML类图
┌─────────────────────────────────┐
│         AbstractCarFactory      │
├─────────────────────────────────┤
│ +createSedan(): Sedan           │  ← 产品族1:轿车系列
│ +createSUV(): SUV               │  ← 产品族2:SUV系列
└─────────────────────────────────┘
               ▲
               │
       ┌───────┴───────┐
┌─────────────┐   ┌─────────────┐
│ BMWFactory  │   │ BenzFactory │
├─────────────┤   ├─────────────┤
│ +createSedan()   │ +createSedan()
│ +createSUV()     │ +createSUV()
└─────────────┘   └─────────────┘
       │                 │
       ▼                 ▼
┌─────────────────────────────────┐
│           具体产品族            │
├─────────────┬──────────────────┤
│  BMW系列    │  Benz系列         │
├─────────────┼──────────────────┤
│ BMWSedan    │ BenzSedan        │
│ BMWSuv      │ BenzSuv          │
└─────────────┴──────────────────┘
代码示例:汽车品牌产品族

1. 产品接口(不同类型汽车)

// 轿车接口
public interface Sedan {
    void drive();
    String getBrand();
}

// SUV接口
public interface SUV {
    void drive();
    String getBrand();
    void offroad();  // SUV特有方法:越野
}

2. 具体产品(各品牌的不同车型)

// 宝马轿车
public class BMWSedan implements Sedan {
    @Override
    public void drive() {
        System.out.println("驾驶宝马轿车,体验运动操控");
    }
    
    @Override
    public String getBrand() {
        return "BMW";
    }
}

// 宝马SUV
public class BMWSuv implements SUV {
    @Override
    public void drive() {
        System.out.println("驾驶宝马SUV,兼顾舒适与通过性");
    }
    
    @Override
    public String getBrand() {
        return "BMW";
    }
    
    @Override
    public void offroad() {
        System.out.println("宝马SUV越野模式启动");
    }
}

// 奔驰轿车
public class BenzSedan implements Sedan {
    @Override
    public void drive() {
        System.out.println("驾驶奔驰轿车,享受豪华舒适");
    }
    
    @Override
    public String getBrand() {
        return "Benz";
    }
}

// 奔驰SUV
public class BenzSuv implements SUV {
    @Override
    public void drive() {
        System.out.println("驾驶奔驰SUV,体验顶级越野性能");
    }
    
    @Override
    public String getBrand() {
        return "Benz";
    }
    
    @Override
    public void offroad() {
        System.out.println("奔驰SUV全地形模式启动");
    }
}

3. 抽象工厂(汽车品牌工厂)

// 汽车品牌工厂接口,定义完整产品族
public interface AbstractCarFactory {
    Sedan createSedan();  // 生产轿车
    SUV createSUV();      // 生产SUV
}

4. 具体工厂(各品牌工厂)

// 宝马工厂,生产宝马全系列车型
public class BMWFactory implements AbstractCarFactory {
    @Override
    public Sedan createSedan() {
        return new BMWSedan();
    }
    
    @Override
    public SUV createSUV() {
        return new BMWSuv();
    }
}

// 奔驰工厂,生产奔驰全系列车型
public class BenzFactory implements AbstractCarFactory {
    @Override
    public Sedan createSedan() {
        return new BenzSedan();
    }
    
    @Override
    public SUV createSUV() {
        return new BenzSuv();
    }
}

5. 客户端代码

public class LuxuryCarManufacturer {
    public static void main(String[] args) {
        // 创建宝马工厂,生产宝马全系列
        AbstractCarFactory bmwFactory = new BMWFactory();
        Sedan bmwSedan = bmwFactory.createSedan();
        SUV bmwSuv = bmwFactory.createSUV();
        
        bmwSedan.drive();  // 驾驶宝马轿车
        bmwSuv.offroad();  // 宝马SUV越野
        
        // 创建奔驰工厂,生产奔驰全系列
        AbstractCarFactory benzFactory = new BenzFactory();
        Sedan benzSedan = benzFactory.createSedan();
        SUV benzSuv = benzFactory.createSUV();
        
        benzSedan.drive();  // 驾驶奔驰轿车
        benzSuv.offroad();  // 奔驰SUV越野
    }
}
抽象工厂 vs 工厂方法
特性 工厂方法模式 抽象工厂模式
产品数量 单一产品等级结构 多个产品等级结构(产品族)
核心思想 定义创建单个产品的接口 定义创建一系列相关产品的接口
灵活性 易于扩展单个产品 易于替换整个产品族
复杂度 较低 较高
典型案例 不同品牌的单一车型 同一品牌的全系列车型
优点
  • 产品族一致性:确保同一品牌的不同产品(如宝马轿车和SUV)能够协同工作
  • 易于切换产品族:只需更换工厂即可切换整套产品(如从宝马产品线切换到奔驰产品线)
  • 隔离具体实现:客户端只与抽象接口交互,不依赖具体产品实现
缺点
  • 扩展困难:新增产品类型(如添加跑车系列)需要修改抽象工厂接口和所有具体工厂
  • 复杂度高:需要理解多个产品等级结构和产品族的概念
应用场景
  • 跨平台应用:为不同平台(Windows、Mac、Linux)提供整套UI组件
  • 主题系统:为应用提供不同主题(浅色、深色)的全套界面元素
  • 数据库访问:为不同数据库(MySQL、Oracle)提供整套访问接口

二、结构型设计模式

2.1 适配器模式(Adapter Pattern)

适配器模式将一个类的接口转换成客户希望的另一个接口,如同不同国家的电源插座需要适配器才能通用,汽车领域中不同品牌的零件接口也需要适配器来兼容。

汽车案例:充电接口适配器

随着电动汽车的发展,不同品牌可能采用不同的充电接口标准。为了让特斯拉充电桩能给宝马电动车充电,我们需要一个充电接口适配器。

类适配器UML
┌───────────────────┐            ┌───────────────────┐
│   Target (宝马接口) │            │ Adaptee (特斯拉接口)│
├───────────────────┤            ├───────────────────┤
│ +chargeWithBMW()  │            │ +chargeWithTesla()│
└───────────────────┘            └───────────────────┘
         △                              ▲
         │ 实现                         │ 继承
┌───────────────────────────────────────┘
│           Adapter (充电适配器)
├───────────────────┤
│ +chargeWithBMW()  │
└───────────────────┘
代码示例:充电接口适配器

1. 目标接口(宝马充电接口)

// 宝马充电接口标准
public interface BMWChargeInterface {
    void chargeWithBMW();
}

2. 被适配者(特斯拉充电接口)

// 特斯拉充电接口标准
public class TeslaChargeInterface {
    // 特斯拉特有充电方法
    public void chargeWithTesla() {
        System.out.println("使用特斯拉充电接口充电");
    }
}

3. 类适配器(充电适配器)

// 充电适配器:将特斯拉接口适配为宝马接口
public class ChargeAdapter extends TeslaChargeInterface implements BMWChargeInterface {
    @Override
    public void chargeWithBMW() {
        // 适配过程:调用特斯拉的充电方法
        System.out.println("使用适配器转换接口...");
        super.chargeWithTesla();
        System.out.println("适配器转换完成,宝马汽车充电中...");
    }
}

4. 对象适配器(更灵活的方式)

// 对象适配器:通过组合方式实现适配
public class ChargeObjectAdapter implements BMWChargeInterface {
    private TeslaChargeInterface teslaInterface;
    
    // 注入被适配对象
    public ChargeObjectAdapter(TeslaChargeInterface teslaInterface) {
        this.teslaInterface = teslaInterface;
    }
    
    @Override
    public void chargeWithBMW() {
        System.out.println("对象适配器:转换接口...");
        teslaInterface.chargeWithTesla();
        System.out.println("对象适配器:宝马汽车充电中...");
    }
}

5. 客户端代码

public class ElectricCar {
    public static void main(String[] args) {
        // 类适配器使用
        BMWChargeInterface classAdapter = new ChargeAdapter();
        classAdapter.chargeWithBMW();
        
        // 对象适配器使用
        TeslaChargeInterface teslaInterface = new TeslaChargeInterface();
        BMWChargeInterface objectAdapter = new ChargeObjectAdapter(teslaInterface);
        objectAdapter.chargeWithBMW();
    }
}
类适配器 vs 对象适配器
类型 实现方式 优点 缺点
类适配器 继承被适配者,实现目标接口 结构简单,可重写被适配者方法 受单继承限制,耦合度高
对象适配器 持有被适配者引用,实现目标接口 更灵活,可适配多个被适配者 结构稍复杂,需要额外对象
应用场景
  • 系统集成:整合不同厂商的API接口
  • 版本兼容:为旧系统提供新接口适配
  • 第三方库使用:适配第三方库以符合项目接口标准

2.2 装饰器模式(Decorator Pattern)

装饰器模式动态地给对象添加一些额外的职责,如同汽车在基础配置上选装不同配件(导航、真皮座椅、全景天窗等),每个选装件都是一个装饰器。

汽车案例:汽车配置选装系统

假设我们要设计一个汽车配置系统,客户可以在基础车型上添加各种选装配置。使用装饰器模式,每个选装件都是一个装饰器,可以动态组合。

代码示例:汽车配置选装

1. 抽象组件(汽车)

// 汽车接口
public interface Car {
    String getDescription();  // 获取配置描述
    double getPrice();        // 获取价格
}

2. 具体组件(基础车型)

// 基础款宝马
public class BasicBMW implements Car {
    @Override
    public String getDescription() {
        return "宝马3系基础款";
    }
    
    @Override
    public double getPrice() {
        return 350000;  // 基础价格35万
    }
}

// 基础款奔驰
public class BasicBenz implements Car {
    @Override
    public String getDescription() {
        return "奔驰C级基础款";
    }
    
    @Override
    public double getPrice() {
        return 380000;  // 基础价格38万
    }
}

3. 抽象装饰器(选装配置)

// 汽车配置装饰器
public abstract class CarDecorator implements Car {
    protected Car decoratedCar;  // 被装饰的汽车
    
    public CarDecorator(Car car) {
        this.decoratedCar = car;
    }
    
    @Override
    public String getDescription() {
        return decoratedCar.getDescription();
    }
    
    @Override
    public double getPrice() {
        return decoratedCar.getPrice();
    }
}

4. 具体装饰器(各种选装件)

// 导航系统
public class NavigationSystem extends CarDecorator {
    public NavigationSystem(Car car) {
        super(car);
    }
    
    @Override
    public String getDescription() {
        return super.getDescription() + " + 高级导航系统";
    }
    
    @Override
    public double getPrice() {
        return super.getPrice() + 8000;  // 导航系统加价8000
    }
}

// 真皮座椅
public class LeatherSeats extends CarDecorator {
    public LeatherSeats(Car car) {
        super(car);
    }
    
    @Override
    public String getDescription() {
        return super.getDescription() + " + 真皮座椅";
    }
    
    @Override
    public double getPrice() {
        return super.getPrice() + 15000;  // 真皮座椅加价15000
    }
}

// 全景天窗
public class PanoramicSunroof extends CarDecorator {
    public PanoramicSunroof(Car car) {
        super(car);
    }
    
    @Override
    public String getDescription() {
        return super.getDescription() + " + 全景天窗";
    }
    
    @Override
    public double getPrice() {
        return super.getPrice() + 12000;  // 全景天窗加价12000
    }
}

5. 客户端代码

public class CarConfiguration {
    public static void main(String[] args) {
        // 创建基础款宝马
        Car bmw = new BasicBMW();
        System.out.println(bmw.getDescription() + ",价格:" + bmw.getPrice());
        
        // 添加导航系统
        bmw = new NavigationSystem(bmw);
        System.out.println(bmw.getDescription() + ",价格:" + bmw.getPrice());
        
        // 添加真皮座椅
        bmw = new LeatherSeats(bmw);
        System.out.println(bmw.getDescription() + ",价格:" + bmw.getPrice());
        
        // 添加全景天窗
        bmw = new PanoramicSunroof(bmw);
        System.out.println(bmw.getDescription() + ",价格:" + bmw.getPrice());
        
        // 创建一个高配奔驰
        Car benz = new PanoramicSunroof(
            new LeatherSeats(
                new NavigationSystem(new BasicBenz())
            )
        );
        System.out.println(benz.getDescription() + ",价格:" + benz.getPrice());
    }
}
优点
  • 动态扩展:可以在运行时动态添加或移除功能
  • 组合灵活:不同装饰器可以任意组合,实现多种配置
  • 遵循开闭原则:新增装饰器无需修改现有代码
  • 避免类爆炸:无需为每种组合创建单独的类
应用场景
  • IO流:Java中的InputStream、OutputStream使用装饰器模式
  • GUI组件:如Swing中的JScrollPane装饰JTextArea
  • 权限控制:动态添加不同级别的权限检查
  • 日志记录:动态添加日志记录功能

2.3 代理模式(Proxy Pattern)

代理模式为其他对象提供一种代理以控制对这个对象的访问,如同汽车销售代理商,客户通过代理商购买汽车,而不是直接与汽车工厂打交道。

汽车案例:汽车销售代理

汽车制造商通常不直接向消费者销售汽车,而是通过4S店代理商。代理商可以提供额外服务(如贷款、保险、上牌等),同时控制对制造商的访问。

代码示例:汽车销售代理

1. 抽象主题(汽车销售)

// 汽车销售接口
public interface CarSales {
    void sellCar(String model);  // 销售汽车
    void serviceCar(String model);  // 汽车维修
}

2. 真实主题(汽车制造商)

// 汽车制造商
public class CarManufacturer implements CarSales {
    private String name;
    
    public CarManufacturer(String name) {
        this.name = name;
    }
    
    @Override
    public void sellCar(String model) {
        System.out.println(name + "工厂生产并销售" + model + "汽车");
    }
    
    @Override
    public void serviceCar(String model) {
        System.out.println(name + "工厂提供" + model + "汽车维修服务");
    }
}

3. 代理(4S店代理商)

// 汽车4S店代理
public class CarDealerProxy implements CarSales {
    private CarManufacturer manufacturer;  // 被代理的制造商
    private String dealerName;  // 代理商名称
    
    public CarDealerProxy(String manufacturerName, String dealerName) {
        this.manufacturer = new CarManufacturer(manufacturerName);
        this.dealerName = dealerName;
    }
    
    @Override
    public void sellCar(String model) {
        // 代理前:提供额外服务
        System.out.println(dealerName + "提供贷款咨询服务");
        System.out.println(dealerName + "提供保险服务");
        
        // 调用真实主题的方法
        manufacturer.sellCar(model);
        
        // 代理后:提供售后服务
        System.out.println(dealerName + "提供上牌服务");
        System.out.println(dealerName + "提供免费保养券");
    }
    
    @Override
    public void serviceCar(String model) {
        // 控制对真实主题的访问
        System.out.println(dealerName + "检查车辆故障");
        
        // 只有复杂问题才交给工厂处理
        if (isComplexIssue(model)) {
            manufacturer.serviceCar(model);
        } else {
            System.out.println(dealerName + "直接提供维修服务");
        }
    }
    
    // 判断是否是复杂问题
    private boolean isComplexIssue(String model) {
        // 简化逻辑:假设某些车型问题较复杂
        return model.contains("新能源");
    }
}

4. 客户端代码

public class Customer {
    public static void main(String[] args) {
        // 客户通过4S店代理购买汽车
        CarSales bmwDealer = new CarDealerProxy("宝马", "宝诚宝马4S店");
        bmwDealer.sellCar("宝马3系");
        
        System.out.println("\n--- 汽车维修 ---");
        bmwDealer.serviceCar("宝马3系");  // 普通车型,4S店直接维修
        bmwDealer.serviceCar("宝马iX3新能源");  // 新能源车型,需要工厂支持
    }
}
代理模式的类型
类型 用途 示例
远程代理 代表远程对象 分布式系统中的远程服务调用
虚拟代理 延迟初始化重量级对象 图片懒加载
保护代理 控制访问权限 权限验证
智能引用 提供额外服务 引用计数、缓存
优点
  • 职责分离:真实主题只需关注核心功能,代理处理辅助功能
  • 控制访问:可以限制或管理对真实主题的访问
  • 增强功能:可以在不修改真实主题的情况下添加额外功能
  • 远程代理:可以隐藏对象位于远程地址空间的事实

三、行为型设计模式

3.1 观察者模式(Observer Pattern)

观察者模式定义了对象之间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知并自动更新,如同汽车的仪表盘,发动机转速、车速等数据变化时,仪表盘上的指针会自动更新。

汽车案例:汽车仪表盘系统

汽车的仪表盘需要实时显示发动机转速、车速、油量等信息。当这些数据变化时,仪表盘上的相应指示器应立即更新。观察者模式非常适合这种场景。

代码示例:汽车仪表盘

1. 主题接口(被观察者)

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

// 汽车数据主题
public interface CarDataSubject {
    void registerObserver(Observer observer);  // 注册观察者
    void removeObserver(Observer observer);    // 移除观察者
    void notifyObservers();                    // 通知所有观察者
}

2. 观察者接口

// 观察者接口
public interface Observer {
    void update(float speed, float rpm, float fuelLevel);  // 更新数据
}

3. 具体主题(汽车传感器系统)

// 汽车传感器系统
public class CarSensorSystem implements CarDataSubject {
    private List<Observer> observers;
    private float speed;      // 车速(km/h)
    private float rpm;        // 发动机转速(rpm)
    private float fuelLevel;  // 油量(0-100%)
    
    public CarSensorSystem() {
        observers = new ArrayList<>();
    }
    
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
    
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(speed, rpm, fuelLevel);
        }
    }
    
    // 当传感器数据变化时调用
    public void sensorDataChanged() {
        notifyObservers();
    }
    
    // 设置传感器数据
    public void setSensorData(float speed, float rpm, float fuelLevel) {
        this.speed = speed;
        this.rpm = rpm;
        this.fuelLevel = fuelLevel;
        sensorDataChanged();  // 数据变化,通知观察者
    }
}

4. 具体观察者(仪表盘)

// 车速表
public class SpeedMeter implements Observer {
    private String name;
    
    public SpeedMeter(String name) {
        this.name = name;
    }
    
    @Override
    public void update(float speed, float rpm, float fuelLevel) {
        System.out.println(name + "显示车速: " + speed + " km/h");
    }
}

// 转速表
public class RpmMeter implements Observer {
    private String name;
    
    public RpmMeter(String name) {
        this.name = name;
    }
    
    @Override
    public void update(float speed, float rpm, float fuelLevel) {
        System.out.println(name + "显示转速: " + rpm + " rpm");
    }
}

// 油量表
public class FuelGauge implements Observer {
    private String name;
    
    public FuelGauge(String name) {
        this.name = name;
    }
    
    @Override
    public void update(float speed, float rpm, float fuelLevel) {
        System.out.println(name + "显示油量: " + fuelLevel + "%");
        if (fuelLevel < 10) {
            System.out.println("警告: 油量过低,请尽快加油!");
        }
    }
}

5. 客户端代码

public class CarDashboard {
    public static void main(String[] args) {
        // 创建汽车传感器系统
        CarSensorSystem sensorSystem = new CarSensorSystem();
        
        // 创建仪表盘组件
        Observer speedMeter = new SpeedMeter("车速表");
        Observer rpmMeter = new RpmMeter("转速表");
        Observer fuelGauge = new FuelGauge("油量表");
        
        // 注册观察者
        sensorSystem.registerObserver(speedMeter);
        sensorSystem.registerObserver(rpmMeter);
        sensorSystem.registerObserver(fuelGauge);
        
        // 模拟驾驶过程中的数据变化
        System.out.println("--- 启动汽车 ---");
        sensorSystem.setSensorData(0, 800, 80);
        
        System.out.println("\n--- 加速 ---");
        sensorSystem.setSensorData(60, 2500, 78);
        
        System.out.println("\n--- 高速行驶 ---");
        sensorSystem.setSensorData(120, 3500, 70);
        
        System.out.println("\n--- 油量不足 ---");
        sensorSystem.setSensorData(80, 2000, 8);
    }
}
优点
  • 松耦合:主题和观察者之间通过接口通信,互不依赖具体实现
  • 动态关联:可以随时添加或移除观察者,灵活性高
  • 广播通信:主题可以同时通知多个观察者,实现一对多通信
应用场景
  • 事件处理系统:如GUI中的按钮点击事件
  • 消息通知系统:如邮件订阅、消息推送
  • 监控系统:如服务器监控、设备状态监控
  • 数据绑定:如MVVM模式中的视图与数据模型绑定

3.2 策略模式(Strategy Pattern)

策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,如同汽车的驾驶模式选择(经济模式、运动模式、雪地模式等),每种模式对应不同的驾驶策略。

汽车案例:驾驶模式选择系统

现代汽车通常提供多种驾驶模式选择,不同模式下发动机、变速箱等工作方式不同。使用策略模式,可以将每种驾驶模式封装为一个策略,动态切换。

代码示例:驾驶模式策略

1. 策略接口(驾驶模式)

// 驾驶模式策略接口
public interface DrivingMode {
    void accelerate();    // 加速特性
    void decelerate();    // 减速特性
    void steer();         // 转向特性
    String getModeName(); // 获取模式名称
}

2. 具体策略(各种驾驶模式)

// 经济模式
public class EconomyMode implements DrivingMode {
    @Override
    public void accelerate() {
        System.out.println("经济模式:平缓加速,节省燃油");
    }
    
    @Override
    public void decelerate() {
        System.out.println("经济模式:提前减速,能量回收");
    }
    
    @Override
    public void steer() {
        System.out.println("经济模式:转向轻盈,适合城市驾驶");
    }
    
    @Override
    public String getModeName() {
        return "经济模式";
    }
}

// 运动模式
public class SportMode implements DrivingMode {
    @Override
    public void accelerate() {
        System.out.println("运动模式:快速加速,动力强劲");
    }
    
    @Override
    public void decelerate() {
        System.out.println("运动模式:延迟减速,保持动力");
    }
    
    @Override
    public void steer() {
        System.out.println("运动模式:转向沉稳,路感清晰");
    }
    
    @Override
    public String getModeName() {
        return "运动模式";
    }
}

// 雪地模式
public class SnowMode implements DrivingMode {
    @Override
    public void accelerate() {
        System.out.println("雪地模式:缓慢加速,防止打滑");
    }
    
    @Override
    public void decelerate() {
        System.out.println("雪地模式:轻柔减速,避免抱死");
    }
    
    @Override
    public void steer() {
        System.out.println("雪地模式:转向柔和,减少侧滑");
    }
    
    @Override
    public String getModeName() {
        return "雪地模式";
    }
}

3. 上下文(汽车控制系统)

// 汽车控制系统
public class CarControlSystem {
    private DrivingMode currentMode;
    
    // 默认使用经济模式
    public CarControlSystem() {
        this.currentMode = new EconomyMode();
    }
    
    // 设置驾驶模式
    public void setDrivingMode(DrivingMode mode) {
        this.currentMode = mode;
        System.out.println("\n切换到" + mode.getModeName());
    }
    
    // 执行加速
    public void accelerate() {
        currentMode.accelerate();
    }
    
    // 执行减速
    public void decelerate() {
        currentMode.decelerate();
    }
    
    // 执行转向
    public void steer() {
        currentMode.steer();
    }
}

4. 客户端代码

public class Driver {
    public static void main(String[] args) {
        CarControlSystem car = new CarControlSystem();
        
        // 默认经济模式驾驶
        System.out.println("--- 城市通勤 ---");
        car.accelerate();
        car.steer();
        car.decelerate();
        
        // 切换到运动模式
        car.setDrivingMode(new SportMode());
        System.out.println("--- 高速公路 ---");
        car.accelerate();
        car.steer();
        car.decelerate();
        
        // 切换到雪地模式
        car.setDrivingMode(new SnowMode());
        System.out.println("--- 雪地行驶 ---");
        car.accelerate();
        car.steer();
        car.decelerate();
    }
}
优点
  • 策略切换灵活:可以在运行时动态切换算法
  • 避免多重条件判断:用多态代替复杂的if-else或switch语句
  • 单一职责原则:每个策略只负责一种算法
  • 开闭原则:新增策略无需修改现有代码
应用场景
  • 排序算法:不同数据规模使用不同排序算法
  • 支付方式:支付宝、微信支付、银行卡支付等不同策略
  • 压缩算法:不同文件类型使用不同压缩算法
  • 校验规则:不同表单字段使用不同校验规则

3.3 模板方法模式(Template Method Pattern)

模板方法模式定义了一个算法的骨架,将某些步骤延迟到子类中实现,如同汽车生产的总装流程——总流程固定(底盘→发动机→车身→内饰),但各步骤的具体实现因车型而异。

汽车案例:汽车组装流程

汽车制造中的总装流程有固定的步骤顺序,但不同车型的具体组装细节不同。模板方法模式可以定义总流程骨架,具体车型实现细节步骤。

代码示例:汽车组装模板

1. 抽象类(组装模板)

// 汽车组装模板
public abstract class CarAssemblyTemplate {
    // 模板方法:定义组装流程骨架
    public final void assembleCar() {
        assembleChassis();    // 组装底盘
        installEngine();      // 安装发动机
        assembleBody();       // 组装车身
        installInterior();    // 安装内饰
        qualityCheck();       // 质量检查
        
        if (needsSpecialEquipment()) {  // 钩子方法:是否需要特殊设备
            installSpecialEquipment();  // 安装特殊设备
        }
    }
    
    // 具体方法:通用底盘组装
    protected void assembleChassis() {
        System.out.println("1. 组装通用底盘");
    }
    
    // 抽象方法:不同车型发动机安装不同
    protected abstract void installEngine();
    
    // 抽象方法:不同车型车身组装不同
    protected abstract void assembleBody();
    
    // 具体方法:通用内饰安装
    protected void installInterior() {
        System.out.println("4. 安装通用内饰");
    }
    
    // 钩子方法:质量检查,子类可重写
    protected void qualityCheck() {
        System.out.println("5. 执行标准质量检查");
    }
    
    // 钩子方法:是否需要特殊设备,默认不需要
    protected boolean needsSpecialEquipment() {
        return false;
    }
    
    // 钩子方法:安装特殊设备,默认空实现
    protected void installSpecialEquipment() {
        // 子类根据需要实现
    }
}

2. 具体子类(不同车型组装)

// 轿车组装
public class SedanAssembly extends CarAssemblyTemplate {
    @Override
    protected void installEngine() {
        System.out.println("2. 安装轿车专用发动机");
    }
    
    @Override
    protected void assembleBody() {
        System.out.println("3. 组装轿车车身");
    }
}

// SUV组装
public class SUVAssembly extends CarAssemblyTemplate {
    @Override
    protected void installEngine() {
        System.out.println("2. 安装SUV专用大功率发动机");
    }
    
    @Override
    protected void assembleBody() {
        System.out.println("3. 组装SUV高底盘车身");
    }
    
    // 重写钩子方法:SUV需要特殊设备
    @Override
    protected boolean needsSpecialEquipment() {
        return true;
    }
    
    // 实现特殊设备安装
    @Override
    protected void installSpecialEquipment() {
        System.out.println("6. 安装四驱系统和越野套件");
    }
}

// 新能源汽车组装
public class ElectricCarAssembly extends CarAssemblyTemplate {
    @Override
    protected void installEngine() {
        System.out.println("2. 安装电动马达和电池组");
    }
    
    @Override
    protected void assembleBody() {
        System.out.println("3. 组装轻量化电动车身");
    }
    
    // 重写质量检查方法
    @Override
    protected void qualityCheck() {
        super.qualityCheck();  // 调用父类标准检查
        System.out.println("5.1 执行电池安全专项检查");
        System.out.println("5.2 执行电动系统功能检查");
    }
}

3. 客户端代码

public class CarAssemblyLine {
    public static void main(String[] args) {
        System.out.println("=== 组装轿车 ===");
        CarAssemblyTemplate sedan = new SedanAssembly();
        sedan.assembleCar();
        
        System.out.println("\n=== 组装SUV ===");
        CarAssemblyTemplate suv = new SUVAssembly();
        suv.assembleCar();
        
        System.out.println("\n=== 组装新能源汽车 ===");
        CarAssemblyTemplate electricCar = new ElectricCarAssembly();
        electricCar.assembleCar();
    }
}
模板方法中的方法类型
方法类型 特点 作用
抽象方法 必须由子类实现 定义可变步骤
具体方法 父类提供实现 定义固定步骤
钩子方法 父类提供默认实现,子类可重写 定义可选步骤或条件步骤
优点
  • 代码复用:将公共步骤实现放在父类,避免重复
  • 控制流程:父类控制算法骨架,子类专注实现细节
  • 扩展性好:新增具体子类即可扩展新功能
  • 符合开闭原则:算法骨架固定,具体步骤可扩展
应用场景
  • 框架设计:如Spring中的HttpServlet,定义请求处理流程
  • 生命周期管理:如Android中的Activity生命周期
  • 报表生成:固定报表格式,不同数据来源实现细节
  • 测试流程:固定测试流程,不同测试对象实现测试细节

总结

设计模式是面向对象设计的"工具箱",掌握它们可以帮助开发者构建更优秀的软件系统。本文以汽车制造为核心案例,详细解释了9种常用设计模式:

创建型模式

  • 单例模式:确保系统中只有一个实例,如汽车工厂的总控中心
  • 工厂方法模式:每个产品由专门的工厂生产,如不同品牌的汽车工厂
  • 抽象工厂模式:生产完整的产品族,如同一品牌的全系列车型

结构型模式

  • 适配器模式:解决接口不兼容问题,如不同品牌汽车的充电接口适配
  • 装饰器模式:动态添加功能,如汽车选装配置系统
  • 代理模式:控制对象访问,如汽车销售代理商

行为型模式

  • 观察者模式:对象间的一对多通知,如汽车仪表盘系统
  • 策略模式:封装不同算法,如汽车驾驶模式选择
  • 模板方法模式:定义算法骨架,如汽车组装流程

如何选择设计模式

  1. 根据问题类型:创建对象问题用创建型模式,组合对象问题用结构型模式,对象交互问题用行为型模式
  2. 根据设计原则:优先考虑开闭原则、单一职责原则
  3. 避免过度设计:不要为了使用模式而使用模式,简单问题用简单方案
  4. 结合实际场景:理解业务需求,选择最适合的模式

设计模式虽然平时感觉不怎么用到,但它们是解决常见设计问题的经过验证的方案。通过的汽车制造案例,希望您能更直观地理解这些模式的本质和应用场景,在实际开发中灵活运用,构建出更优雅、更灵活、更易于维护的软件系统。


网站公告

今日签到

点亮在社区的每一天
去签到