面向对象设计模式详解:从汽车工厂看设计模式的实际应用
引言
设计模式是面向对象软件开发中的"最佳实践",如同汽车制造中的标准化生产流程,它们提供了经过验证的解决方案来应对常见的设计挑战。正如"java进阶.md"中所述,设计模式的概念最初源于建筑领域,1995年由"四人组"(Gang of Four, GoF)引入软件领域,收录了23个经典模式。
学习设计模式就像学习汽车制造的标准化流程——掌握它们可以帮助开发者构建更可靠、更灵活、更易于维护的软件系统。本文将以汽车制造为核心案例,详细解释9种常用设计模式,让复杂概念变得直观易懂。
设计模式分类
设计模式主要分为三大类,如同汽车制造中的不同环节:
- 创建型模式:处理对象创建机制,如同汽车工厂中的生产线设计
- 结构型模式:关注类和对象的组合,如同汽车零部件的组装方式
- 行为型模式:关注对象之间的通信,如同汽车各系统间的协作机制
一、创建型设计模式
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种常用设计模式:
创建型模式
- 单例模式:确保系统中只有一个实例,如汽车工厂的总控中心
- 工厂方法模式:每个产品由专门的工厂生产,如不同品牌的汽车工厂
- 抽象工厂模式:生产完整的产品族,如同一品牌的全系列车型
结构型模式
- 适配器模式:解决接口不兼容问题,如不同品牌汽车的充电接口适配
- 装饰器模式:动态添加功能,如汽车选装配置系统
- 代理模式:控制对象访问,如汽车销售代理商
行为型模式
- 观察者模式:对象间的一对多通知,如汽车仪表盘系统
- 策略模式:封装不同算法,如汽车驾驶模式选择
- 模板方法模式:定义算法骨架,如汽车组装流程
如何选择设计模式
- 根据问题类型:创建对象问题用创建型模式,组合对象问题用结构型模式,对象交互问题用行为型模式
- 根据设计原则:优先考虑开闭原则、单一职责原则
- 避免过度设计:不要为了使用模式而使用模式,简单问题用简单方案
- 结合实际场景:理解业务需求,选择最适合的模式
设计模式虽然平时感觉不怎么用到,但它们是解决常见设计问题的经过验证的方案。通过的汽车制造案例,希望您能更直观地理解这些模式的本质和应用场景,在实际开发中灵活运用,构建出更优雅、更灵活、更易于维护的软件系统。