Java设计模式是什么?核心设计原则有哪些?
文章目录
- 什么是设计模式?
- 为什么使用设计模式?
- 设计模式的核心设计原则是什么?
- 1. 开闭原则(Open-Closed Principle, OCP)
- 2. 里氏替换原则(Liskov Substitution Principle, LSP)
- 3. 依赖倒置原则(Dependency Inversion Principle, DIP)
- 4. 单一职责原则(Single Responsibility Principle, SRP)
- 5. 接口隔离原则(Interface Segregation Principle, ISP)
- 6. 迪米特法则(Law of Demeter, LoD)
- 7. 合成/聚合复用原则(Composite Reuse Principle, CRP)
- 七大原则的核心价值总结
- 常见的设计模式有哪些?
作为一个Java开发程序员,设计模式就像是习武之人的内功心法,直接少走20年弯路,其重要性显而易见。通过掌握7大设计原则、23种设计模式,可以编写出 高内聚、低耦合、易扩展、易维护 的高质量代码,应对复杂软件系统的设计。
什么是设计模式?
-
设计模式,是一套被反复使用、多数人知晓的、经过分类编目的代码设计经验的总结。
-
他描述了在软件设计过程中的一些不断重复出现的问题,以及该问题的解决方案。也就是说,他是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。
为什么使用设计模式?
- 设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
- 正确使用设计模式具有以下优点:
- 可以提高程序员的思维能力、编程能力和设计能来。
- 使程序设计更加标准化、代码编程更加工程化,使软件开发效率大大提高,从而缩短软件开发的周期。
- 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
设计模式的核心设计原则是什么?
1. 开闭原则(Open-Closed Principle, OCP)
-
定义:软件实体(类、模块、函数等)应对 扩展开放,对 修改关闭。
- 扩展开放:通过新增代码(如新类、新模块)来实现功能扩展。
- 修改关闭:不修改已有代码即可满足新需求。
-
核心思想
- 抽象约束:通过接口或抽象类构建稳定的抽象层,隔离变化点。
- 封装变化:将可变因素封装在具体实现类中,需求变更时只需新增或调整实现类。
-
示例
// 抽象接口(稳定层) interface Shape {void draw(); }// 具体实现类(可扩展) class Circle implements Shape {public void draw() {System.out.println("Drawing Circle");} }class Rectangle implements Shape {public void draw() {System.out.println("Drawing Rectangle");} }// 客户端代码(无需修改) public class Main {public static void main(String[] args) {Shape shape = new Circle();shape.draw(); // 输出:Drawing Circle} }
-
好处
- 降低维护成本:无需修改已有代码,减少引入新错误的风险。
- 提高扩展性:通过新增实现类即可支持新功能(如新增
Triangle
类)。 - 增强稳定性:抽象层保持不变,系统架构更稳定。
2. 里氏替换原则(Liskov Substitution Principle, LSP)
-
定义:所有引用基类的地方必须能透明地使用其子类的对象。
- 子类替换父类:子类对象应能完全替代父类对象,且程序行为和结果不受影响。
-
核心思想
- 行为一致性:子类应继承父类的契约,不破坏父类定义的规范。
- 避免副作用:子类不应强制改变父类的行为预期。
-
示例
// 父类 abstract class Vehicle {abstract void start(); }// 子类(符合 LSP) class Car extends Vehicle {public void start() {System.out.println("Car starts");} }// 错误示例(违反 LSP) class BrokenCar extends Vehicle {public void start() {System.out.println("Car explodes"); // 破坏父类行为预期} }
-
好处
- 代码复用性:子类可安全复用父类逻辑。
- 可靠性:程序行为更可预测,避免因子类错误导致异常。
- 解耦:高层模块无需关注具体子类实现。
3. 依赖倒置原则(Dependency Inversion Principle, DIP)
-
定义
- 高层模块不应该依赖低层模块,二者都应该依赖 抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
-
核心思想
- 面向接口编程:通过抽象(接口或抽象类)解耦模块间的依赖关系。
- 减少耦合:高层模块不直接依赖底层实现,而是通过抽象接口间接调用。
-
示例
// 抽象接口(高层依赖) interface Database {void save(); }// 具体实现(低层模块) class MySQL implements Database {public void save() {System.out.println("Saving to MySQL");} }// 高层模块(依赖抽象) class UserService {private Database database;public UserService(Database database) {this.database = database;}public void saveUser() {database.save();} }
-
好处
- 灵活性:可轻松切换底层实现(如从 MySQL 改为 PostgreSQL)。
- 测试性:通过 Mock 抽象接口,方便单元测试。
- 解耦:模块间依赖关系更清晰,降低维护成本。
4. 单一职责原则(Single Responsibility Principle, SRP)
-
定义:一个类应该只有一个引起它变化的原因。
- 职责分离:一个类只负责一项功能,避免功能耦合。
-
核心思想
- 高内聚:将相关功能集中在一个类中。
- 低耦合:不同职责分离到独立类中,减少相互影响。
-
示例
// 错误示例(违反 SRP) class User {private String name;private String email;// 职责1:用户信息管理public void setName(String name) { this.name = name; }// 职责2:邮件发送public void sendEmail(String message) {System.out.println("Sending email to " + email + ": " + message);} }// 改进方案(职责分离) class User {private String name;private String email;public void setName(String name) { this.name = name; } }class EmailService {public void sendEmail(String email, String message) {System.out.println("Sending email to " + email + ": " + message);} }
-
好处
- 易维护:修改一个功能时,不会影响其他职责。
- 复用性:单一职责的类更容易被其他模块复用。
- 可测试性:职责清晰的类更易编写单元测试。
5. 接口隔离原则(Interface Segregation Principle, ISP)
-
定义:客户端不应该依赖它不需要的接口。
- 接口小型化:提供多个细粒度的接口,避免“胖接口”。
-
核心思想
- 按需依赖:客户端仅依赖其实际需要的方法。
- 避免冗余:减少接口中不必要方法的暴露。
-
示例
// 错误示例(“胖”接口) interface Animal {void eat(); // 所有动物都需要void fly(); // 仅鸟类需要void swim(); // 仅鱼类需要 }// 改进方案(接口隔离) interface Eatable {void eat(); }interface Flyable {void fly(); }interface Swimmable {void swim(); }class Bird implements Eatable, Flyable {public void eat() { System.out.println("Bird eats"); }public void fly() { System.out.println("Bird flies"); } }class Fish implements Eatable, Swimmable {public void eat() { System.out.println("Fish eats"); }public void swim() { System.out.println("Fish swims"); } }
-
好处
- 减少依赖:客户端仅需关注所需接口。
- 灵活性:接口组合更灵活,适应不同需求。
- 可扩展性:新增功能时,只需扩展特定接口。
6. 迪米特法则(Law of Demeter, LoD)
-
定义:一个对象应尽可能少地了解其他对象。
- 最少知识原则:只与直接朋友通信,避免跨层依赖。
-
核心思想
- 降低耦合:对象之间交互仅限于必要的依赖。
- 封装细节:隐藏内部实现,通过接口暴露行为。
-
示例
// 错误示例(违反 LoD) class Manager {public void manage(Employee employee) {System.out.println("Manager manages employee: " + employee.getName());} }class Employee {private String name;public String getName() { return name; } }class Client {public void doWork() {Employee employee = new Employee();Manager manager = new Manager();manager.manage(employee); // 正确:Manager 直接依赖 Employee} }// 更复杂的错误示例(跨层依赖) class Client {public void doWork() {Department department = new Department();Employee employee = department.getEmployee(0);Manager manager = new Manager();manager.manage(employee); // 错误:Client 间接依赖 Employee} }
-
好处
- 松耦合:模块间依赖更清晰,减少连锁修改。
- 可维护性:代码结构更简洁,易于理解和调试。
- 稳定性:减少因依赖变更导致的连锁反应。
7. 合成/聚合复用原则(Composite Reuse Principle, CRP)
-
定义:尽量使用 对象组合/聚合,而不是继承来达到复用目的。
- 组合优先于继承:通过组合实现功能扩展,避免继承的强耦合。
-
核心思想
- 灵活性:组合允许动态替换实现,继承是静态的。
- 减少继承层级:避免多层继承导致的复杂性和脆弱性。
-
示例
// 错误示例(继承) class Car {void start() { System.out.println("Car starts"); } }class SportsCar extends Car {void start() { System.out.println("SportsCar starts with V8 engine"); } }// 改进方案(组合) interface Engine {void start(); }class V8Engine implements Engine {public void start() { System.out.println("V8 Engine starts"); } }class Car {private Engine engine;public Car(Engine engine) {this.engine = engine;}void start() {engine.start(); // 通过组合调用} }
-
好处
- 灵活性:可动态切换实现(如
Car
支持多种Engine
)。 - 降低耦合:避免继承导致的强依赖关系。
- 代码复用性:通过组合复用多个独立组件。
- 灵活性:可动态切换实现(如
七大原则的核心价值总结
原则名称 | 核心目标 | 关键实践 |
---|---|---|
开闭原则 | 对扩展开放,对修改关闭 | 抽象层封装变化 |
里氏替换 | 子类替换父类不影响程序行为 | 遵循父类契约 |
依赖倒置 | 高层依赖抽象,低层实现细节 | 面向接口编程 |
单一职责 | 一个类只负责一项职责 | 职责分离,高内聚 |
接口隔离 | 提供最小接口,避免冗余 | 细粒度接口,按需依赖 |
迪米特法则 | 减少对象间直接交互 | 封装细节,最少知识 |
合成复用 | 优先组合而非继承 | 通过组合实现灵活扩展 |
常见的设计模式有哪些?
设计模式类型 | 设计模式名称 | 核心作用 | 适用场景 |
---|---|---|---|
创建型 | 单例模式 | 保证唯一实例 | 资源管理、全局访问 |
创建型 | 工厂方法 | 解耦对象创建 | 动态选择实现 |
创建型 | 抽象工厂 | 创建相关对象族 | 跨平台UI、产品族生成 |
创建型 | 建造者 | 分阶段构建复杂对象 | 配置复杂对象 |
创建型 | 原型 | 复制现有对象 | 高性能对象创建 |
结构型 | 适配器 | 兼容接口 | 集成遗留系统 |
结构型 | 装饰器 | 动态扩展功能 | 功能组合 |
结构型 | 代理 | 控制访问 | 权限控制、延迟加载 |
结构型 | 组合 | 树形结构 | 文件系统、菜单 |
结构型 | 桥接 | 解耦抽象与实现 | 多维变化系统 |
结构型 | 外观 | 简化接口 | 复杂系统简化 |
结构型 | 享元 | 共享对象 | 内存优化 |
行为型 | 策略 | 动态切换算法 | 支付方式、排序算法 |
行为型 | 观察者 | 事件通知 | 消息订阅、GUI事件 |
行为型 | 命令 | 封装请求 | 撤销/重做、任务队列 |
行为型 | 模板方法 | 定义算法骨架 | 测试框架、流程固定 |
行为型 | 迭代器 | 遍历集合 | 统一访问不同数据结构 |
行为型 | 责任链 | 传递请求 | 审批流程、过滤器链 |
行为型 | 备忘录 | 恢复状态 | 撤销操作、状态快照 |
行为型 | 状态 | 状态驱动行为 | 订单状态机、游戏状态 |
行为型 | 访问者 | 分离操作与数据结构 | 编译器、数据分析 |
行为型 | 中介者 | 减少对象间依赖 | 聊天室、协调复杂交互 |
行为型 | 解释器 | 解析语言 | 正则表达式、自定义DSL |
关于设计模式的详细内容将在后续专门介绍,如需了解,可以关注一下后续文章。