当前位置: 首页 > news >正文

Java进阶--面向对象设计原则

设计模式

概念

        设计模式,又称软件设计模式,是一套被反复使用,经过分类编目的,代码设计经验的总结。描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方。它是解决特定问题的一系列套路,是前辈们代码设计经验的总结,具有一定普遍性,可以反复使用。

优点

        设计模式的本质是面型对象设计原则的实际运用,是对类的封装性,继承性和多态性以及类的关联关系和组合关系的充分理解。

        正确使用设计模式具有的优点:

可以提高程序员的思维能力,编程能力,和设计能力。

使程序设计更加规范,提高开发效率。

使设计的代码可重用性高,可读性强,可靠性高,灵活性好,可维护性强。

能够更好地理解源码架构。

面向对象设计原则

         在面向对象的设计过程中,首先要考虑的是如何提高软件系统的可维护性与可复用性。

单一职责原则

        单一职责原则,可以理解为一个类只负责一个功能领域的职责,不要什么都写在一个类中,否则导致类变得“杂乱”,耦合度过高,不利于扩展。

        以项目开发为例,如果项目组成员每个人的职责都很明确,可以专心开发自 己负责的模块,则项目成果的质量往往很高。相反,如果职责不清晰,分工就会混乱。

开闭原则

        开闭原则即对扩展开放,对修改封闭。程序扩展时,不建议修改原有代码,建议通过扩展一个新的类(方法)来实现新的功能。

以汽车工厂生产汽车举例:

反例(不使用开闭原则):

public class CarDemo {public static void main(String[] args) {//现在需要生产汽车new CarFactory().createCar(1);new CarFactory().createCar(2);new CarFactory().createCar(3);//扩展新品牌汽车业务}}class CarFactory{public void createCar(int type){if(type==1){System.out.println("生产宝马汽车"+new Car("宝马汽车"));}else if(type==2){System.out.println("生产奥迪汽车"+new Car("奥迪汽车"));}else if(type==3){System.out.println("生产大众汽车"+new Car("大众汽车"));}//如果需要生产新品牌的汽车,就需要再次修改代码}}class Car{String name;public Car(String name) {this.name = name;}
}

 正例(使用开闭原则):

        直接创建一个汽车类作为基类,不同品牌汽车继承此基类,当需要扩展新品牌的业务时,只需要扩展一个新的汽车类继承汽车基类并重写生产方法,再使用汽车工厂去调用子类方法即可,就不会修改原有代码,提高代码的可复用性与可维护性。

class CarDemo{public static void main(String[] args) {new CarFactory().carfactory(new BMW());new CarFactory().carfactory(new Aodi());new CarFactory().carfactory(new DaZhong());new CarFactory().carfactory(new BenChi());}}class CarFactory{void carfactory(Car car){car.createCar();}
}abstract  class Car{public abstract void createCar();
}class BMW extends Car{@Overridepublic void createCar() {System.out.println("生产宝马汽车");}
}class Aodi extends Car{@Overridepublic void createCar() {System.out.println("生产奥迪汽车");}
}class DaZhong extends Car{@Overridepublic void createCar() {System.out.println("生产大众汽车");}
}class BenChi extends Car{@Overridepublic void createCar() {System.out.println("生产奔驰汽车");}
}

里氏替换原则

        所有使用父类的地方必须能透明地使用其子类的对象。

        里氏替换原则表明,在软件中将一个基类对象替换成它的子类对象时程序将 不会产生任何错误和异常。

         任何基类可以出现的地 方,子类一定可以出现。所以,子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时除添加新的方法完成新增功能外尽量不要重写父类的方法。

        其核心思想是:子类对象必须能够替换父类对象,且替换后程序的行为不变。这意味着继承关系应确保子类在不改变父类原有功能的前提下进行扩展,而非修改。

反例:

父类Rectangle(矩形)
子类Square(正方形)

class Rectangle {protected int width;protected int height;public void setWidth(int width) {this.width = width;}public void setHeight(int height) {this.height = height;}public int getArea() {return width * height;}
}class Square extends Rectangle {// 正方形要求宽高相等,强制重写set方法@Overridepublic void setWidth(int width) {super.setWidth(width);super.setHeight(width); // 修改父类行为}@Overridepublic void setHeight(int height) {super.setHeight(height);super.setWidth(height); // 修改父类行为}
}

问题

  • Square重写setWidthsetHeight,改变父类行为。

  • RectangleSquare替换时,预期面积计算逻辑可能出错。

 修正:

// 抽象基类定义公共行为
abstract class Shape {public abstract int getArea();
}class Rectangle extends Shape {private int width;private int height;public void setWidth(int width) {this.width = width;}public void setHeight(int height) {this.height = height;}@Overridepublic int getArea() {return width * height;}
}class Square extends Shape {private int side;public void setSide(int side) {this.side = side;}@Overridepublic int getArea() {return side * side;}
}

使用Shape的时候可以透明替换成RectangleSquare。

        小结:里氏替换原则强调子类对父类的透明替换,是保证多态性和继承合理性的基石。通过设计一致的接口、约束子类行为,可提升代码的健壮性和可维护性。在实践中,若发现子类需要颠覆父类逻辑,往往是设计缺陷的信号,需及时重构。 

依赖倒置原则

        上层模块不应该依赖底层模块,它们都应该依赖于抽象.

        抽象不应该依赖于细节,细节应该依赖于抽象.

        如果有多个同类型事物时,我们可以抽取一个共同抽象层,具体的实现细节应该依赖于抽象.

反例:订单服务直接依赖 MySQL 数据库操作。

// 低层模块:MySQL 数据库操作
class MySQLDatabase {public void saveOrder(String orderData) {System.out.println("保存订单到 MySQL: " + orderData);}
}// 高层模块:订单处理服务
class OrderService {private MySQLDatabase database = new MySQLDatabase();  // ❌ 直接依赖具体实现public void processOrder(String order) {database.saveOrder(order);}
}

         程序要依赖于抽象接口,不要依赖于具体实现。

  • 若需切换数据库(如改为 MongoDB),需修改 OrderService 代码,违反开闭原则

  • 高层模块与低层模块紧耦合,难以独立测试或扩展。

修正:

interface DatabaseService {void save(String data);
}
//低层模块实现接口
class MySQLDatabase implements DatabaseService {@Overridepublic void save(String data) {System.out.println("保存到 MySQL: " + data);}
}class MongoDBDatabase implements DatabaseService {@Overridepublic void save(String data) {System.out.println("保存到 MongoDB: " + data);}
}
//高层模块依赖抽象
class OrderService {private DatabaseService database;  // ✅ 依赖抽象接口// 通过构造函数注入依赖(依赖注入)public OrderService(DatabaseService database) {this.database = database;}public void processOrder(String order) {database.save(order);}
}

接口隔离原则

        客户端不应被迫依赖它们不使用的接口。通过定义细粒度的接口,减少类之间的耦合,避免“胖接口”导致的冗余实现和潜在风险。

反例:设计一个多功能设备接口,涵盖打印、扫描和传真功能

// ❌ 违反 ISP:臃肿接口,强制所有实现类支持所有方法
interface MultiFunctionDevice {void print(String document);void scan(String document);void fax(String document);
}// 普通打印机只能打印,但被迫实现空方法
class BasicPrinter implements MultiFunctionDevice {@Overridepublic void print(String document) {System.out.println("打印文档: " + document);}@Overridepublic void scan(String document) {// 不支持扫描,空实现(可能引发逻辑错误)throw new UnsupportedOperationException("不支持扫描");}@Overridepublic void fax(String document) {// 不支持传真,空实现throw new UnsupportedOperationException("不支持传真");}
}

问题

  • 冗余代码BasicPrinter 被迫实现无用的方法(如 scan() 和 fax())。

  • 脆弱性:接口变更(如新增方法)会强制所有实现类修改,违反开闭原则。

  • 误导性:接口声明支持扫描和传真,但实际实现可能抛出异常。

 修正:

1.拆分接口

// ✅ 拆分后的接口
interface Printer {void print(String document);
}interface Scanner {void scan(String document);
}interface FaxMachine {void fax(String document);
}

 2.按需实现接口

// 基础打印机仅实现打印功能
class BasicPrinter implements Printer {@Overridepublic void print(String document) {System.out.println("打印文档: " + document);}
}// 高级设备组合多个功能
class AdvancedMultiFunctionDevice implements Printer, Scanner, FaxMachine {@Overridepublic void print(String document) {System.out.println("高级打印: " + document);}@Overridepublic void scan(String document) {System.out.println("扫描文档: " + document);}@Overridepublic void fax(String document) {System.out.println("发送传真: " + document);}
}

3.按需使用接口

public class Office {// 仅依赖打印机接口public void printReport(Printer printer, String report) {printer.print(report);}// 依赖扫描和打印接口public void copyDocument(Printer printer, Scanner scanner, String doc) {scanner.scan(doc);printer.print(doc);}
}

 小结:接口隔离原则通过 细化接口职责 和 最小化依赖,有效提升系统的灵活性和可维护性。

迪米特原则

        又称最少知识原则,其核心是降低类之间的耦合度,通过限制对象间的交互范围,确保每个模块(类)只需了解与其直接相关的组件,从而提高系统的灵活性和可维护性。

核心规则

一个对象应仅与以下“直接朋友”交互

  1. 自身成员变量

  2. 方法参数

  3. 方法内部创建的对象

  4. 当前对象的直接组件(如集合中的元素)

禁止行为

  • 链式调用:如 a.getB().getC().doSomething()(通过中间对象访问间接依赖)。

  • 直接操作陌生对象:避免直接调用非直接朋友的方法或访问其属性。

 反例:

class Employee {private DepartmentManager manager;public void getCeoContact() {// ❌ 链式调用:employee -> manager -> ceoString ceoPhone = manager.getCeo().getPhone();System.out.println("CEO联系方式: " + ceoPhone);}
}class DepartmentManager {private CEO ceo;public CEO getCeo() {return ceo;}
}class CEO {private String phone;public String getPhone() {return phone;}
}

问题

  • Employee 类通过 DepartmentManager 间接依赖 CEO,形成耦合链。

  • 若 CEO 的联系方式获取逻辑变更(如改为邮箱),需修改所有链式调用处。

 修正:

class Employee {private DepartmentManager manager;public void getCeoContact() {// ✅ 仅与直接朋友(manager)交互,不感知CEO的存在String ceoContact = manager.getCeoContact();System.out.println("CEO联系方式: " + ceoContact);}
}class DepartmentManager {private CEO ceo;public String getCeoContact() {// 封装与CEO的交互细节return ceo.getContactInfo();}
}class CEO {private String phone;public String getContactInfo() {return phone;// 若逻辑变更,只需修改此处(如 return email;)}
}
  • 隐藏细节DepartmentManager 封装与 CEO 的交互,避免 Employee 直接依赖底层类。

  • 降低耦合CEO 的修改仅影响 DepartmentManager,无需改动 Employee

优势说明
降低耦合度模块间依赖关系简化,减少连锁修改风险。
提高可维护性封装交互细节,逻辑变更影响范围可控。
增强封装性对象内部结构对客户端隐藏,避免外部直接操作私有状态。
减少风险扩散局部故障不易扩散至整个系统。

小结:迪米特原则通过限制对象间的交互范围,推动系统向高内聚、低耦合的方向演进。 

组合/聚合复用原则

优先使用组合(Composition)或聚合(Aggregation)关系实现代码复用,而非通过继承
关系类型定义生命周期管理示例
组合强关联,整体与部分不可分割,部分不能独立于整体存在。整体负责部分的创建与销毁。汽车 与 发动机(汽车销毁,发动机随之销毁)
聚合弱关联,整体与部分可独立存在,部分可被多个整体共享。整体不管理部分的生命周期。学校 与 学生(学校关闭,学生仍存在)

继承虽然能实现代码复用,但存在以下问题:

  1. 破坏封装性:子类依赖父类实现细节,父类修改可能影响所有子类。

  2. 类膨胀:继承层级过深易导致类爆炸(Class Explosion)。

  3. 灵活性差:无法在运行时动态切换行为(需通过子类化实现)。

  4. 违反单一职责原则:子类可能被迫继承无关方法。

直接上代码:

1.继承

abstract class Notifier {public abstract void send(String message);
}class EmailNotifier extends Notifier {@Overridepublic void send(String message) {System.out.println("发送邮件: " + message);}
}class SMSNotifier extends Notifier {@Overridepublic void send(String message) {System.out.println("发送短信: " + message);}
}// ❌ 问题:若需同时支持邮件和短信,需创建新子类,导致类膨胀
class EmailAndSMSNotifier extends Notifier { /* 冗余代码 */ }

 2.组合/聚合

// 定义消息发送接口(抽象)
interface MessageSender {void send(String message);
}// 具体实现类
class EmailSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("发送邮件: " + message);}
}class SMSSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("发送短信: " + message);}
}// 通知服务通过组合复用发送功能
class NotificationService {private List<MessageSender> senders = new ArrayList<>();public void addSender(MessageSender sender) {senders.add(sender);}public void sendNotification(String message) {for (MessageSender sender : senders) {sender.send(message);}}
}// 使用示例
public class Client {public static void main(String[] args) {NotificationService service = new NotificationService();service.addSender(new EmailSender());service.addSender(new SMSSender());service.sendNotification("订单已支付"); // 同时发送邮件和短信}
}

组合/聚合复用原则通过对象间的协作而非继承层级,实现代码复用与功能扩展. 

总结

开闭原则:要求对扩展开放,对修改关闭
里氏替换原则:不要破坏继承体系
依赖倒置原则:要求面向接口编程
单一职责原则:实现类职责要单一
接口隔离原则:在设计接口的时候要精简单一
迪米特法则:只与直接的朋友的通信
合成复用原则:尽量使用聚合和组合的方式,而不是使用继承

设计原则的核心思想 

  1. 高内聚:模块内部元素紧密协作,职责单一。

  2. 低耦合:模块间依赖最小化,修改影响范围可控。

  3. 可维护性:代码易读、易扩展、易调试。

  4. 可测试性:通过解耦和依赖注入,便于单元测试和集成测试。

  5. 灵活性:适应需求变化,支持快速迭代。

http://www.xdnf.cn/news/153253.html

相关文章:

  • 基于html-css-js的尚有选页面源码详细
  • 如何解决IDE项目启动报错 error:0308010C:digital envelope routines::unsupported 问题
  • 图论---LCA(倍增法)
  • 从新手到高手:小程序开发进阶技巧分享
  • SQL 查询进阶:WHERE 子句与连接查询详解
  • Myweb项目——面试题总结
  • 多模态大语言模型arxiv论文略读(四十二)
  • ZYNQ笔记(十四):基于 BRAM 的 PS、PL 数据交互
  • Pygame字体与UI:打造游戏菜单和HUD界面
  • 【含文档+PPT+源码】基于Django的新闻推荐系统的设计与实现
  • 第八部分:缓解 RAG 中的幻觉
  • 认识哈希以及哈希表的模拟实现
  • 嵌入式硬件开发工具---万用表---示波器---仿真器
  • 解构与重构:“整体部分”视角下的软件开发思维范式
  • Dify框架面试内容整理-Dify框架
  • 学习设计模式《六》——抽象工厂方法模式
  • 大数据模型现状分析
  • 4.25test
  • 2025蓝桥省赛c++B组第二场题解
  • 在WSL2+Ubuntu22.04中通过conda pack导出一个conda环境包,然后尝试导入该环境包
  • WPF与C++ 动态库交互
  • 职业教育新形态数字教材的建设与应用:重构教育生态的数字化革命
  • 文件操作及读写-爪哇版
  • 一些常见的资源池管理、分布式管理和负载均衡的监控工具
  • c++ package_task
  • 10:00面试,10:08就出来了,面试问的问题太。。。
  • AMP混合精度训练 详细解析
  • 2025.04.26-美团春招笔试题-第三题
  • 基于OpenMV+STM32+OLED与YOLOv11+PaddleOCR的嵌入式车牌识别系统开发笔记
  • Unity任务系统笔记