软件设计模式_期末复习
熬夜整理的期末复习资料,整理过程真心不易,如果觉得有用的话,动动小手点个赞吧~
七大原则
🧩 一、单一职责原则(Single Responsibility Principle, SRP)
核心思想:一个类只负责一个功能领域的职责(即只有一个引起变化的原因)。
作用:降低类的复杂度,提高可读性和可维护性,减少修改带来的风险。
案例:
违反 SRP:一个 CustomerDataChart 类同时处理数据库连接、数据查询和图表生成。
遵循 SRP:拆分为 DBUtil(数据库连接)、CustomerDAO(数据操作)、CustomerDataChart(图表生成)三个独立类。
设计模式应用:代理模式(分离核心逻辑与辅助功能)。
🔄 二、开闭原则(Open-Closed Principle, OCP)
核心思想:软件实体(类、模块)应对扩展开放,对修改关闭。
实现方式:通过抽象化(接口/抽象类)和多态机制扩展功能,避免修改原有代码。
案例:
违反 OCP:ChartDisplay 类通过 if-else 判断图表类型,新增图表需修改源码。
遵循 OCP:定义抽象类 AbstractChart,具体图表(如 PieChart、BarChart)继承并实现其方法,客户端通过抽象类调用。
设计模式应用:工厂模式(扩展新产品无需修改工厂类)、策略模式。
🔀 三、里氏替换原则(Liskov Substitution Principle, LSP)
核心思想:子类必须能透明替换父类,且不破坏程序逻辑。
关键要求:子类不重写父类非抽象方法,不修改父类行为(仅扩展)。
案例:
图形系统中,Circle 和 Square 子类替换父类 Shape 时,drawAllShapes() 函数仍能正确绘制。
反例:正方形继承长方形时,修改长宽会破坏面积计算逻辑(正方形长宽需相等)。
设计模式应用:模板方法模式(子类扩展算法步骤不改变骨架)。
🧪 四、依赖倒置原则(Dependency Inversion Principle, DIP)
核心思想:
高层模块不依赖低层模块,二者依赖抽象(接口/抽象类)。
抽象不依赖细节,细节依赖抽象。
实现方式:依赖注入(DI)或面向接口编程。
案例:
计算器程序中,CalculatorApp 依赖抽象接口 Calculator,而非具体实现类(如 Add、Subtract),通过注入方式使用功能。
设计模式应用:依赖注入框架(如 Spring)、适配器模式。
🧱 五、接口隔离原则(Interface Segregation Principle, ISP)
核心思想:客户端不应被迫依赖它不需要的接口方法。
实践:将臃肿接口拆分为多个专用小接口。
案例:
违反 ISP:Shape 接口包含 select()、move()、resize(),但线段 Line 无法缩放。
遵循 ISP:拆分为 Selectable、Movable、Resizable 接口,Line 仅实现前两者。
设计模式应用:适配器模式(委托未实现的方法)。
🛡️ 六、迪米特法则(Law of Demeter, LoD)/ 最少知识原则
核心思想:一个对象应尽可能少地与其他对象交互(只与直接朋友通信)。
作用:降低耦合度,提高模块独立性。
实现方式:
通过中介类(如门面模式)协调对象间通信。
封装内部细节,仅暴露必要方法。
案例:
游戏系统中,角色 Player 不直接管理武器 Weapon 的细节,而是通过 WeaponController 中介类调用。
设计模式应用:门面模式、中介者模式。
♻️ 七、合成复用原则(Composite Reuse Principle, CRP)
核心思想:优先使用组合(has-a)或聚合,而非继承(is-a)。
优势:避免继承的强耦合性,更灵活地动态替换组件。
案例:
游戏角色技能系统:Character 类通过组合 Skill 对象实现技能复用,而非继承多个技能子类。
UI 组件:通过聚合 Button、TextBox 等对象构建复杂界面。
设计模式应用:组合模式、装饰器模式。
💎 七大原则的关联与综合应用
原则 核心目标 关键实现技术
SRP 职责单一化 类/方法拆分
OCP 扩展性 抽象化、多态
LSP 继承安全 子类行为一致性
DIP 解耦依赖 依赖注入、接口编程
ISP 接口精简 接口拆分
LoD 降低耦合 封装、中介类
CRP 灵活复用 对象组合
💎 总结
七大原则是设计模式的基石:
开闭原则(OCP) 是总目标,强调扩展而非修改;
里氏替换(LSP) 和 依赖倒置(DIP) 是实现 OCP 的关键机制;
单一职责(SRP) 和 接口隔离(ISP) 保证模块的内聚性;
迪米特法则(LoD) 和 合成复用(CRP) 降低系统耦合度。
实际设计中需权衡取舍:过度拆分接口(ISP)可能导致接口泛滥,严格遵循迪米特法则可能增加中介方法。理解原则背后的高内聚、低耦合、可扩展核心理念,比机械套用更重要。
创建型模式:本文只讲简单工厂模式,工厂方法模式,原型模式,单例模式
创建型模式核心目标:封装对象创建过程,解耦对象使用与实例化逻辑,提升系统灵活性和可维护性.
🔧 一、简单工厂模式(Simple Factory Pattern)
模式定义
通过一个工厂类静态方法,根据输入参数决定创建哪种具体产品类的实例,隐藏对象创建细节。
模式角色
工厂类(Factory):包含静态创建方法(如 createProduct())
抽象产品(Product):定义产品接口(如 Shape)
具体产品(Concrete Product):实现接口的具体类(如 Circle, Rectangle)。
模式类图

核心代码示例
抽象产品
abstract class Product
{//所有产品类的公共业务方法(不一定都需要公共方法)
public void MethodSame()
{//公共方法在这里实现
}//声明抽象业务方法
public abstract void MethodDiff();
}
具体产品
class ConcreteProductA : Product
{//实现业务方法public override void MethodDiff(){//业务方法的实现}
}
工厂类
class Factory
{//静态工厂方法public static Product GetProduct(string arg){Product product = null;if (arg.Equals("A")){product = new ConcreteProductA();//初始化设置product}else if (arg.Equals("B")){product = new ConcreteProductB();//初始化设置product}return product;}
}
优点
客户端与具体产品解耦
简化对象创建流程。
缺点
违反开闭原则(新增产品需修改工厂逻辑)
工厂类职责过重,易成上帝类。
适用环境
产品类型少且固定(如支付方式、数据库驱动)
客户端只需参数无需创建细节。
举例应用
某软件公司要基于JAVA语言开发一套图表库,该图表库可以为应用系统提供多种不同外观的图表,例如柱状图(HistogramChart)、饼状图(PieChart)、折线图(LineChart)等。该软件公司图表库设计人员希望为应用系统开发人员提供一套灵活易用的图表库,通过设置不同的参数即可得到不同类型的图表,而且可以较为方便地对图表库进行扩展,以便能够在将来增加一些新类型的图表。
现使用简单工厂模式来设计该图表库。
1、新建解决方案(绘制类图)
2、新建一个控制台应用程序,编写简单工厂模式类实现代码,实现图标库案例,要求编写为控制台应用程序,并能调试运行。
🏭 二、工厂方法模式(Factory Method Pattern)
通过上面的简单工厂模式,我们可以发现当新增具体产品类时,同时也要修改工厂类的代码,这就违反了七大原则中的开闭原则,因此有了工厂方法模式,在新增具体产品类时,只需新增工厂即可,符合开闭原则.
模式定义
定义创建对象的接口,由子类决定实例化哪个类,将实例化延迟到子类。
模式角色
抽象工厂(Creator):声明工厂方法(如 createProduct())
具体工厂(Concrete Creator):实现工厂方法(如 ConcreteFactoryA)
抽象产品(Product)
具体产品(Concrete Product)。
模式类图
核心代码示例
抽象工厂
interface Factory
{Product FactoryMethod();
}
具体工厂
class ConcreteFactory : Factory
{public Product FactoryMethod() {return new ConcreteProduct();}
}
举例应用
某系统运行日志记录器(Logger)可以通过多种途径保存系统的运行日志,例如通过文件记录或数据库记录,用户可以通过修改配置文件灵活地更换日志记录方式。在设计各类日志记录器时,开发人员发现需要对日志记录器进行一些初始化工作,初始化参数的设置过程较为复杂,而且某些参数的设置有严格的先后次序,否则可能会发生记录失败。为了更好地封装记录器的初始化过程并保证多种记录器切换的灵活性,现使用工厂方法模式设计该系统。
1、新建解决方案(绘制类图)
2、新建一个控制台应用程序,编写工厂方法模式类实现代码,实现以上需求的案例,要求编写为控制台应用程序,并能调试运行。
优点
符合开闭原则(扩展新产品只需新增工厂)
解耦创建与使用,支持多态。
缺点
类数量膨胀(每新增一产品需对应工厂)
增加系统复杂度。
适用环境
客户端不知道它所需要的对象的类(客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体产品对象由具体工厂类创建)
抽象工厂类通过其子类来指定创建哪个对象
🧬 三、原型模式(Prototype Pattern)
模式定义
通过复制现有对象(原型)创建新对象,避免重复初始化开销。
模式角色
原型接口(Prototype):声明克隆方法(如 clone())
具体原型(Concrete Prototype):实现克隆逻辑(如 User)
客户端(Client):调用克隆方法。
模式类图
举例应用
1.
在使用某OA系统时,有些岗位的员工发现他们每周的工作都大同小异,因此在填写工作周报时很多内容都是重复的,为了提高工作周报的创建效率,大家迫切地希望有一种机制能够快速创建相同或者相似的周报,包括创建周报的附件。试使用原型模式对该OA系统中的工作周报创建模块进行改进。且需要能查询比较最近几周的周报的变化情况。
1、新建解决方案(绘制类图)
2、新建一个控制台应用程序,编写原型模式类实现代码,实现以上需求的案例,要求编写为控制台应用程序,并能调试运行。
核心代码示例
抽象原型类
优点
提升性能(避免重复初始化)
动态创建对象,规避构造函数约束。
缺点
深克隆实现复杂(需处理嵌套引用)
需显式实现 Cloneable 接口。
适用环境
对象创建成本高(如数据库查询结果)
需快速生成相似对象(如游戏角色克隆)。
深克隆与浅克隆
代码示例解析
场景定义
假设有Person
类,包含基本类型字段id
、引用类型字段Address
:
class Address {String city;public Address(String city) { this.city = city; }
}class Person implements Cloneable {int id;Address address;public Person(int id, Address address) {this.id = id;this.address = address;}
}
1.浅克隆实现与问题
@Override
public Person clone() throws CloneNotSupportedException {return (Person) super.clone(); // 仅复制id和address引用
}
测试代码:
Person p1 = new Person(1, new Address("北京"));
Person p2 = p1.clone();p2.id = 2; // 修改基本类型 → 不影响p1
p2.address.city = "上海"; // 修改引用类型 → p1的address同步被修改!System.out.println(p1.address.city); // 输出:上海
System.out.println(p2.address.city); // 输出:上海
2. 深克隆实现方案
方案1:递归调用clone()
(手动深克隆)
class Address implements Cloneable {String city;@Overrideprotected Address clone() throws CloneNotSupportedException {return (Address) super.clone(); // Address也支持克隆}
}class Person implements Cloneable {int id;Address address;@Overridepublic Person clone() throws CloneNotSupportedException {Person cloned = (Person) super.clone();cloned.address = this.address.clone(); // 关键:递归克隆引用对象return cloned;}
}
测试结果:
p2.address.city = "上海";
System.out.println(p1.address.city); // 输出:北京(未受影响)
System.out.println(p2.address.city); // 输出:上海
方案2:序列化/反序列化(通用深克隆):
import java.io.*;public class DeepCloneUtil {public static <T extends Serializable> T deepClone(T obj) {try (ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos)) {oos.writeObject(obj);try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis)) {return (T) ois.readObject();}} catch (Exception e) {throw new RuntimeException("深克隆失败", e);}}
}// 使用示例
Person p2 = DeepCloneUtil.deepClone(p1); // 无需手动实现clone()
优势:
无需修改原有类的代码(只需实现Serializable接口);
自动处理所有嵌套引用,避免递归逻辑遗漏。
⚙️ 四、单例模式(Singleton Pattern)
模式定义
确保一个类仅有一个实例,并提供全局访问点。
模式角色
单例类(Singleton):私有构造 + 静态实例 + 全局访问方法。
模式类图
核心代码实例
class Singleton
{private static Singleton instance=null; //静态私有成员变量,保证全局访问性//私有构造函数,防止外部实例化private Singleton(){ }//静态公有工厂方法,返回唯一实例public static Singleton GetInstance() {if(instance==null)instance=new Singleton(); return instance;}
}
饿汉式单例模式
核心思想:类加载时立即创建实例,以空间换时间。
代码实现:
public class EagerSingleton {// 类加载时初始化实例private static final EagerSingleton INSTANCE = new EagerSingleton();// 私有构造方法,防止外部创建private EagerSingleton() {}public static EagerSingleton getInstance() {return INSTANCE; // 直接返回已创建的实例}
}
特点:
✅ 线程安全:实例在类加载时创建,JVM保证线程安全。
⚠️ 资源占用:即使未使用也会占用内存,可能造成资源浪费。
懒汉式单例模式
核心思想:首次调用getInstance()
时创建实例,以时间换空间。
基础实现(非线程安全):
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {//步骤1 检查实例是否为空instance = new LazySingleton(); // 步骤2 延迟初始化}return instance;}
}
一、普通懒汉式为什么线程不安全?
问题场景:
假设线程A和线程B同时调用 getInstance():
线程A执行到步骤1,发现 instance == null,准备执行步骤2;
此时线程B也执行到步骤1,同样发现 instance == null(因为A尚未完成创建);
结果:两个线程都会执行 new Singleton(),生成两个实例,违反单例原则
二、线程安全改进1:同步方法(synchronized method)
public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;
}
改进原理:
在方法签名加 synchronized,确保同一时间只有一个线程能进入该方法。
优点:简单直接,彻底解决线程安全问题。
缺点:
性能差:每次调用 getInstance() 都需获取锁,即使实例已创建(99%的情况无需同步)。
不推荐高并发场景:锁竞争会导致线程阻塞,影响系统吞吐量。
⚡ 三、线程安全改进2:双重检查锁定(DCL)
public class Singleton {private static volatile Singleton instance; // 必须用volatile!private Singleton() {}public static Singleton getInstance() {if (instance == null) { // 第一次检查(无锁)synchronized (Singleton.class) { // 加锁if (instance == null) { // 第二次检查(有锁)instance = new Singleton(); // 创建实例}}}return instance;}
}
💎 四、两种改进方案对比总结
方案 | 原理 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
同步方法 | 整个方法加锁 | 实现简单 | 性能差(频繁锁竞争) | 低并发或对性能不敏感的场景 |
双重检查锁定 | 两次判空 + volatile + 同步块 | 高性能、线程安全 | 需注意volatile和代码严谨性 | 高并发场景(主流方案) |
改进原理:
双重判空:
第一次检查(无锁):如果实例已存在,直接返回,避免不必要的锁竞争。
第二次检查(加锁后):防止多个线程同时通过第一次检查后,重复创建实例。
volatile关键字:
禁止JVM指令重排序,确保 new Singleton() 的步骤按顺序执行:
① 分配内存 → ② 初始化对象 → ③ 将引用赋给 instance。
若无volatile:若步骤③重排到步骤②之前,其他线程可能拿到未初始化的半成品对象(instance 非null但内部数据为默认值)。
优点:
高性能:实例创建后,后续调用无需同步。
线程安全:兼顾效率与安全性。
缺点:
实现稍复杂,需严格注意 volatile 的使用。
优点
减少内存开销(唯一实例)
全局统一访问(如配置管理)。
缺点
违反单一职责原则(兼具业务与实例控制)
多线程需额外同步(除IoDH外)。
适用环境
需全局唯一对象(如线程池、日志管理器)
频繁访问资源类(如数据库连接池)。
举例应用
某软件公司承接了一个服务器负载均衡(Load Balance)软件的开发工作,该软件运行在一台负载均衡服务器上,可以将并发访问和数据流量分发到服务器集群中的多台设备上进行并发处理,提高了系统的整体处理能力,缩短了响应时间。由于集群中的服务器需要动态删减,且客户端请求需要统一分发,因此需要确保负载均衡器的唯一性,只能有一个负载均衡器来负责服务器的管理和请求的分发,否则将会带来服务器状态的不一致以及请求分配冲突等问题。如何确保负载均衡器的唯一性是该软件成功的关键,试使用单例模式设计服务器负载均衡器。
1、新建解决方案(绘制类图)
2、新建一个控制台应用程序,编写单例模式类实现代码,实现以上需求的案例,要求编写为控制台应用程序,并能调试运行。
💎 四种模式对比总结
模式 | 核心目标 | 最佳场景 | 典型缺陷 |
---|---|---|---|
简单工厂模式 | 参数化创建对象 | 固定产品类型的快速实例化 | 违反开闭原则 |
工厂方法模式 | 子类决定实例化 | 可扩展产品族的动态创建 | 类爆炸问题 |
原型模式 | 克隆代替新建 | 高成本对象的快速复制 | 深克隆实现复杂 |
单例模式 | 全局唯一实例 | 资源共享与访问控制 | 测试困难,扩展性差 |
💡 设计启示:
- 解耦优先:工厂系列分离创建逻辑,提升代码可维护性;
- 性能权衡:原型模式以空间换时间,单例模式以控制换资源;
- 避免滥用:单例易导致隐藏耦合,工厂类需防过度复杂化。
结构型模式:本文只讲桥接模式,适配器模式,装饰模式,代理模式
核心目标:通过组合类或对象形成更大结构,优化系统层次关系与接口兼容性
类结构型模式 :关心类的组合,由多个类组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系
对象结构型模式 :关心类与对象的组合,通过关联关系,在一个类中定义另一个类的实例对象,然后通过该对象调用相应的方法
🌉 一、桥接模式 (Bridge Pattern)
模式定义
将抽象部分与其实现部分分离,使它们可独立变化。通过组合替代继承,解决多维变化导致的类爆炸问题。
模式角色
角色 | 说明 |
---|---|
Abstraction | 定义高层抽象接口,持有Implementor 引用(抽象类) |
RefinedAbstraction | 扩展Abstraction 的功能(扩充抽象类,具体类) |
Implementor | 定义底层操作接口(实现类接口,业务接口) |
ConcreteImplementor | 实现Implementor 接口的具体类(具体实现类) |
模式类图

核心代码示例
// 实现化接口
interface TV {void on();void setChannel(int channel);
}// 具体实现:海信电视
class HisenseTV implements TV {public void on() { System.out.println("海信TV启动"); }public void setChannel(int ch) { System.out.println("海信频道:" + ch); }
}// 抽象化角色
abstract class RemoteControl {protected TV tv;public RemoteControl(TV tv) { this.tv = tv; }abstract void powerOn();
}// 扩展抽象:具体遥控器
class ConcreteRemote extends RemoteControl {public ConcreteRemote(TV tv) { super(tv); }public void powerOn() { tv.on(); }
}
优点
✅ 解耦抽象与实现,支持独立扩展
✅ 避免继承导致的子类爆炸
✅ 符合开闭原则和合成复用原则
缺点
⚠️ 增加系统复杂度
⚠️ 需预先设计抽象层,增加理解成本
适用环境
多维度变化的系统(如不同品牌电视+不同类型遥控器)
需隐藏实现细节的场景(如JDBC驱动与API解耦)
举例应用
某软件公司要开发一个跨平台图像浏览系统,要求该系统能够显示BMP、JPG、GIF、PNG等多种格式的文件,并且能够在Windows、Linux、UNIX等多个操作系统上运行。系统首先将各种格式的文件解析为像素矩阵(Matrix),然后将像素矩阵显示在屏幕上,在不同的操作系统中可以调用不同的绘制函数来绘制像素矩阵(变化最大,可考虑成业务实现部分)。另外,系统需具有较好的扩展性,以便在将来支持新的文件格式和操作系统。试使用桥接模式设计该跨平台图像浏览系统。
1、新建解决方案(绘制类图)
2、新建一个控制台应用程序,编写桥接模式类实现代码,实现以上需求的案例,要求编写为控制台应用程序,并能调试运行。
🔌 二、适配器模式 (Adapter Pattern)
模式定义
将不兼容接口转换为可兼容接口,使原本无法协同工作的类可协作。
模式角色
角色 | 说明 |
---|---|
Target | 客户端期望的接口(如欧洲插座)(目标抽象类,客户希望的接口) |
Adaptee | 需适配的现存类(如美国电器)(适配者类,适配目标) |
Adapter | 包装Adaptee 并实现Target 接口(适配器类,中间类) |
模式类图
⚡ 1. 类适配器(Class Adapter)
核心机制:通过继承被适配者(Adaptee)并实现目标接口(Target)实现适配。
代码示例:
// 目标接口:5V直流电
interface IVoltage5V {int output5V();
}// 被适配者:220V交流电
class Voltage220V {public int output220V() {System.out.println("输出220V交流电");return 220;}
}// 类适配器:继承被适配者 + 实现目标接口
class VoltageAdapter extends Voltage220V implements IVoltage5V {@Overridepublic int output5V() {int srcV = super.output220V(); // 调用父类方法int targetV = srcV / 44; // 电压转换逻辑System.out.println("适配为5V直流电");return targetV;}
}// 客户端调用
public class Client {public static void main(String[] args) {IVoltage5V adapter = new VoltageAdapter();adapter.output5V(); // 输出:220V → 5V}
}
特点:
✅ 直接复用父类方法:通过继承直接调用Adaptee的方法,代码简洁。
⚠️ 单继承限制:Java不支持多继承,若需适配多个类则无法实现。
⚠️ 强耦合:适配器与被适配者绑定,修改可能影响父类行为。
🔗 2. 对象适配器(Object Adapter)
核心机制:通过组合(持有Adaptee实例)实现目标接口,解耦适配逻辑。
代码示例:
// 目标接口:同上(IVoltage5V)
// 被适配者:同上(Voltage220V)// 对象适配器:持有被适配者实例 + 实现目标接口
class VoltageAdapter implements IVoltage5V {private Voltage220V voltage220V; // 组合关系public VoltageAdapter(Voltage220V voltage220V) {this.voltage220V = voltage220V;}@Overridepublic int output5V() {int srcV = voltage220V.output220V(); // 调用实例方法int targetV = srcV / 44;System.out.println("适配为5V直流电");return targetV;}
}// 客户端调用
public class Client {public static void main(String[] args) {Voltage220V src = new Voltage220V();IVoltage5V adapter = new VoltageAdapter(src); // 注入被适配者adapter.output5V();}
}
特点:
✅ 灵活解耦:适配器与Adaptee通过组合关联,可动态替换被适配对象。
✅ 支持多适配:适配器可同时持有多个Adaptee实例,扩展性强(如同时兼容220V/110V)。
✅ 符合合成复用原则:优先组合而非继承,降低系统耦合度。
3. 关键对比与选择建议
维度 | 类适配器 | 对象适配器 |
---|---|---|
实现方式 | 继承 + 接口实现 | 组合 + 接口实现 |
灵活性 | ❌ 受限(单继承约束) | ✅ 高(可适配多个类) |
耦合度 | 高(与父类绑定) | 低(依赖接口而非具体类) |
适用场景 | Adaptee方法可直接复用且无需多适配 | 需适配多个类或需动态替换Adaptee |
⚙️ 4. 开发中的决策逻辑
是否需要适配多个类?
是 → 对象适配器(如同时兼容数据库Oracle/MySQL驱动)。
否 → 进入下一步。
被适配类是否稳定且无需重写?
是 → 类适配器(代码更简洁)。
否 → 对象适配器(避免继承污染)。
优点
✅ 复用遗留代码,无需修改原类
✅ 灵活支持类适配器(继承)和对象适配器(组合)
缺点
⚠️ 过度使用降低代码可读性
⚠️ 增加额外类层级
适用环境
集成第三方库或遗留系统(如旧支付接口接入新系统)
统一多个类接口(如多款敏感词过滤系统整合)
🎨 三、装饰模式 (Decorator Pattern)
模式定义
动态添加功能到对象,避免继承导致的子类膨胀。通过嵌套包装实现功能叠加。
模式角色
角色 | 说明 |
---|---|
Component | 抽象组件(如咖啡)(抽象构件) |
ConcreteComponent | 基础实现(如浓缩咖啡)(具体构件) |
Decorator | 持有Component 引用,定义装饰接口(抽象装饰类) |
ConcreteDecorator | 具体装饰(如加糖)(具体装饰类) |
模式类图
核心代码示例
// 1. 抽象组件:咖啡接口
public interface Coffee {String getDescription();double getCost();
}// 2. 具体组件:基础咖啡
public class Espresso implements Coffee {@Overridepublic String getDescription() { return "Espresso"; }@Overridepublic double getCost() { return 1.5; }
}// 3. 抽象装饰器:持有一个Coffee对象
public abstract class CoffeeDecorator implements Coffee {protected final Coffee decoratedCoffee;public CoffeeDecorator(Coffee coffee) {this.decoratedCoffee = coffee;}// 默认直接转发请求public String getDescription() {return decoratedCoffee.getDescription();}public double getCost() {return decoratedCoffee.getCost();}
}// 4. 具体装饰器:添加牛奶
public class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return super.getDescription() + ", Milk"; // 扩展描述}@Overridepublic double getCost() {return super.getCost() + 0.5; // 增加价格}
}// 5. 客户端调用
public class CoffeeShop {public static void main(String[] args) {Coffee order = new Espresso();order = new MilkDecorator(order); // 动态添加牛奶System.out.println(order.getDescription() + " - $" + order.getCost());// 输出: Espresso, Milk - $2.0}
}
优点
✅ 动态扩展功能,无需修改原类
✅ 支持多层嵌套装饰(如咖啡+糖+奶油)
✅ 符合开闭原则
缺点
⚠️ 嵌套过多增加调试难度
⚠️ 设计复杂易产生过多小对象
适用环境
需动态添加/撤销功能的场景(如游戏装备系统)
无法使用继承时(如Java final类)
举例应用
某软件公司基于面向对象技术开发了一套图形界面构件库——VisualComponent,该构件库提供了大量基本构件,如窗体、文本框、列表框等,由于在使用该构件库时,用户经常要求定制一些特殊的显示效果,如带滚动条的窗体、带黑色边框的文本框、既带滚动条又带黑色边框的列表框等等,因此经常需要对该构件库进行扩展以增强其功能。
现使用装饰模式来设计该图形界面构件库。
1、新建解决方案(绘制类图)
2、新建一个控制台应用程序,编写装饰模式类实现代码,实现以上需求的案例,要求编写为控制台应用程序,并能调试运行。

🔒 四、代理模式 (Proxy Pattern)
模式定义
为其他对象提供代理以控制访问,常用于延迟加载、权限控制等场景。
模式角色
角色 | 说明 |
---|---|
Subject | 抽象主题(如文档接口) |
RealSubject | 真实业务对象(如大型文档)(真实主题) |
Proxy | 代理类,控制对RealSubject 的访问 |
模式类图
核心代码示例
// 抽象主题
interface Document {void display();
}// 真实主题(资源密集型)
class HeavyDocument implements Document {public void display() { System.out.println("渲染大型文档..."); }
}// 代理类
class DocumentProxy implements Document {private HeavyDocument doc;public void display() {if (doc == null) { // 延迟初始化doc = new HeavyDocument(); }doc.display();}
}
客户端调用
Document doc = new DocumentProxy();
doc.display(); // 首次调用时才创建真实对象[3,5](@ref)
举例应用
某软件公司承接了某信息咨询公司的收费商务信息查询系统的开发任务,该系统的基本需求如下:
(1) 在进行商务信息查询之前用户需要通过身份验证,只有合法用户才能够使用该查询系统;
(2) 在进行商务信息查询时系统需要记录查询日志,以便根据查询次数收取查询费用。
该软件公司开发人员已完成了商务信息查询模块的开发任务,现希望能够以一种松耦合的方式向原有系统增加身份验证和日志记录功能,客户端代码可以无区别地对待原始的商务信息查询模块和增加新功能之后的商务信息查询模块,而且可能在将来还要在该信息查询模块中增加一些新的功能。
现使用代理模式设计并实现该收费商务信息查询系统。
1、新建解决方案(绘制类图)
2、新建一个控制台应用程序,编写代理模式实现代码,实现以上需求的案例,要求编写为控制台应用程序,并能调试运行。
优点
✅ 隔离客户端与真实对象,增强安全性
✅ 优化性能(如延迟加载、缓存结果)
✅ 职责清晰,符合单一职责原则
缺点
⚠️ 增加请求响应时间(需代理转发)
⚠️ 需额外维护代理类
适用环境
远程服务调用(如RPC客户端代理)
敏感操作控制(如权限校验代理)
资源消耗大的对象(如图片懒加载)
💎 四模式核心对比
特性 | 桥接模式 | 适配器模式 | 装饰模式 | 代理模式 |
---|---|---|---|---|
核心目标 | 解耦抽象与实现 | 接口转换 | 动态增强功能 | 控制对象访问 |
关系重点 | 组合替代继承 | 兼容不兼容接口 | 嵌套叠加功能 | 隔离客户端与真实对象 |
应用阶段 | 设计期 | 重构/集成期 | 运行时 | 运行时 |
典型场景 | 多维度变化(如遥控器+电视) | 系统整合(如旧系统接入API) | 功能扩展(如Java I/O流) | 权限控制/延迟加载(如Spring AOP) |
💡 设计启示:
- 桥接与适配器:桥接是预先设计的抽象/实现分离,适配器是事后补救的接口转换;
- 装饰与代理:装饰关注增强功能(如咖啡加糖),代理关注访问控制(如权限校验);
- 性能权衡:代理可能增加延迟,装饰可能创建过多对象,需根据场景选择。
行为型模式:本文只讲命令模式和观察者模式
核心目标:优化对象间职责分配与通信机制,降低耦合度
📜 一、命令模式(Command Pattern)
模式定义
将请求封装为对象,使请求的发起者(Invoker)与执行者(Receiver)解耦,支持请求排队、日志记录、撤销/重做等操作。
模式角色
角色 | 职责 |
---|---|
Command | 声明执行操作的接口(如 execute() )(抽象命令类) |
ConcreteCommand | 实现命令接口,绑定接收者并调用其方法(如 BuyStock )(具体命令类) |
Receiver | 实际执行命令的对象(如 Stock 处理买卖逻辑)(接收者类) |
Invoker | 触发命令执行(如 Broker 管理命令队列)(调用者类) |
Client | 创建命令对象并设置接收者(客户端类) |
模式类图
核心代码示例
// 命令接口
interface Command {void execute();
}// 接收者(实际执行者)
class Stock {public void buy() { System.out.println("买入股票"); }public void sell() { System.out.println("卖出股票"); }
}// 具体命令
class BuyStock implements Command {private Stock stock;public BuyStock(Stock stock) { this.stock = stock; }@Overridepublic void execute() { stock.buy(); }
}// 调用者(管理命令)
class Broker {private List<Command> commands = new ArrayList<>();public void takeOrder(Command cmd) { commands.add(cmd); }public void placeOrders() {for (Command cmd : commands) { cmd.execute(); }commands.clear();}
}// 客户端
public class Client {public static void main(String[] args) {Stock stock = new Stock();Broker broker = new Broker();broker.takeOrder(new BuyStock(stock));broker.placeOrders(); // 输出:买入股票}
}
优点
✅ 解耦请求与执行:调用者(Broker)不依赖具体执行者(Stock)。
✅ 支持扩展:新增命令(如 SellStock)无需修改现有代码。
✅ 支持撤销/重做:通过命令队列记录操作历史(如 Broker 可撤销命令)。
缺点
⚠️ 类膨胀:每个命令需单独实现类,增加系统复杂度。
⚠️ 调试困难:多层命令嵌套可能导致调用链冗长。
适用环境
GUI按钮/菜单操作(点击即触发命令)。
事务管理(如数据库操作回滚)。
消息队列(请求排队执行)。
举例应用:
为了用户使用方便,某系统提供了一系列功能键,用户可以自定义功能键的功能,例如功能键FunctionButton 可以用于退出系统(由SystemExitClass类来实现),也可以用于显示帮助文档(由DisplayHelpClass类来实现)。
用户可以通过修改配置文件来改变功能键的用途,现使用命令模式来设计改系统,使得功能键类与功能类之间解耦,可为同一个功能键设置不同的功能。
1、新建解决方案(绘制类图)
2、新建一个控制台应用程序,编写责任模式实现代码,实现以上需求的案例,要求编写为控制台应用程序,并能调试运行
🔍 二、观察者模式(Observer Pattern)
模式定义
定义对象间一对多依赖关系,当一个对象(Subject)状态改变时,所有依赖它的对象(Observers)自动更新。
模式角色
角色 | 职责 |
---|---|
Subject | 维护观察者列表,提供注册/注销接口(如 attach() , detach() )(目标) |
Observer | 定义更新接口(如 update() )(观察者) |
ConcreteSubject | 存储状态,状态变化时通知观察者(如 Sensor 数据更新)(具体目标) |
ConcreteObserver | 实现更新逻辑(如 Display 刷新显示)(具体观察者) |
模式类图

核心代码示例
// 主题接口
interface Subject {void attach(Observer o);void notifyObservers();
}// 具体主题(传感器)
class Sensor implements Subject {private List<Observer> observers = new ArrayList<>();private int temperature;public void setTemperature(int temp) {this.temperature = temp;notifyObservers();}@Overridepublic void attach(Observer o) { observers.add(o); }@Overridepublic void notifyObservers() {for (Observer o : observers) { o.update(temperature); }}
}// 观察者接口
interface Observer {void update(int value);
}// 具体观察者(显示屏)
class Display implements Observer {@Overridepublic void update(int value) {System.out.println("当前温度: " + value);}
}// 客户端
public class Client {public static void main(String[] args) {Sensor sensor = new Sensor();Display display = new Display();sensor.attach(display);sensor.setTemperature(25); // 输出:当前温度: 25}
}
优点
✅ 解耦主题与观察者:主题无需知道观察者细节。
✅ 动态订阅:可运行时增减观察者(如 Sensor 动态绑定 Display)。
✅ 广播通知:一次状态更新可触发多个响应(如温度同时更新显示和日志)。
缺点
⚠️ 性能问题:观察者过多时通知耗时(如高频传感器数据)。
⚠️ 循环依赖:观察者链可能引发死锁(如A更新触发B,B又触发A)。
适用环境
事件驱动系统(如GUI按钮点击事件)。
实时数据监控(如股票价格推送)。
分布式消息中间件(如MQTT发布-订阅模型)。
举例应用
在某多人联机对战游戏中,多个玩家可以加入同一战队组成联盟,当战队中的某一成员受到敌人攻击时将给所有其他盟友发送通知,盟友收到通知后将做出响应。
现使用观察者模式设计并实现该过程,以实现战队成员之间的联动。
1、新建解决方案(绘制类图)
2、新建一个控制台应用程序,编写观察者模式实现代码,实现以上需求的案例,要求编写为控制台应用程序,并能调试运行。
💎 三、命令模式 vs 观察者模式对比
维度 | 命令模式 | 观察者模式 |
---|---|---|
核心目标 | 封装请求为对象,解耦调用与执行 | 状态变化时自动通知依赖对象 |
交互方式 | 单向:调用者→命令→接收者 | 双向:主题↔观察者(互相依赖) |
典型场景 | 事务管理、操作队列 | 事件通知、数据同步 |
复杂度来源 | 命令类膨胀 | 观察者链过长或循环依赖 |
扩展性 | 易新增命令 | 易新增观察者 |
💡 设计启示:
- 命令模式:适用于需记录操作历史(如编辑器撤销栈)或异步执行请求的场景。
- 观察者模式:适合状态变化触发多级响应(如订单状态更新通知库存、物流)。
好喽,设计模式的复习就到这里吧,创作不易,大家的点赞是对我的鼓励,谢谢大家!!!