Java设计模式六大原则

ops/2024/10/22 14:42:52/

        Java设计模式的六大原则是面向对象设计中的基本准则,帮助开发人员构建更灵活、可维护和可扩展的系统。这些原则包括单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP)以及迪米特法则(LoD)。

目录

单一职责原则(Single Responsibility Principle,SRP)

开闭原则(Open/Closed Principle,OCP)

里氏替换原则(Liskov Substitution Principle,LSP)

依赖倒置原则(Dependency Inversion Principle,DIP)

接口隔离原则(Interface Segregation Principle,ISP)

迪米特法则(Law of Demeter,LoD)

单一职责原则(Single Responsibility Principle,SRP)

定义:一个类应该只有一个引起它变化的原因,换句话说,一个类只负责一个职责。

优点:

  • 职责明确:每个类只做一件事情,使系统模块化、结构清晰。

  • 易维护:当某个功能发生变化时,只需修改对应的类,而不会影响其他功能。

  • 增强复用:职责单一的类容易在其他地方复用,减少代码冗余。

缺点:

  • 类的数量增多:如果每个功能都分解到单独的类,可能会导致类数量增加,增加管理复杂度。

  • 可能过度设计:对于小型项目,严格遵守单一职责原则有时会导致不必要的复杂性。

 例子: 假设我们有一个User类,它负责用户的所有操作,包括用户信息的获取、更新和删除。如果我们将这些操作分散到不同的类中,每个类只负责一个职责,那么代码将更加清晰和易于维护。

// 原始设计
class User {void getUserInfo() {}void updateUser() {}void deleteUser() {}
}// 遵循单一职责原则
class UserInfoService {void getUserInfo() {}
}class UserService {void updateUser() {}void deleteUser() {}
}

开闭原则(Open/Closed Principle,OCP)

定义:类应该对扩展开放,对修改关闭。即通过扩展功能来修改行为,而不是直接修改代码。

优点

  • 提高扩展性:系统可以通过新增类来扩展功能,而不需要修改原有代码,降低引入新bug的风险。

  • 稳定性更强:修改现有代码时对已有系统的影响较小,特别是在代码经过多次测试的情况下。

缺点

  • 设计复杂度增加:为了让系统对扩展开放,可能需要引入更多抽象和接口,导致系统结构复杂。

  • 过度设计的风险:过度使用可能导致不必要的抽象和接口,尤其是当系统规模较小时。

 例子: 假设我们有一个Shape接口和一个Circle类实现该接口。如果需要添加新的形状(如Square),我们可以通过扩展Shape接口和添加新的实现类来实现,而不需要修改现有的代码。

interface Shape {double calculateArea();
}class Circle implements Shape {private double radius;public Circle(double radius) { this.radius = radius; }@Overridepublic double calculateArea() { return Math.PI * radius * radius; }
}// 添加新的形状
class Square implements Shape {private double side;public Square(double side) { this.side = side; }@Overridepublic double calculateArea() { return side * side; }
}

里氏替换原则(Liskov Substitution Principle,LSP)

定义:子类对象必须能够替换其父类对象,并且保证程序行为一致。子类应当可以扩展父类的行为,而不是破坏它。

优点:

  • 确保继承的正确性:遵循里氏替换原则可以确保子类可以替代父类,继承关系合理。

  • 提高代码的复用性:当子类替代父类时,系统的可扩展性增强,同时也确保了子类的行为不会引发新的错误。

缺点:

  • 设计时的约束:在设计继承关系时,必须小心处理,确保子类不会违反父类的行为。

  • 对不合适的继承敏感:如果继承关系不合理,强行遵守里氏替换原则可能导致代码复杂化。

例子: 假设我们有一个Bird类和一个Ostrich类继承自BirdOstrichBird的一种特殊类型,但它不能飞行。如果我们在代码中使用了Bird类型的变量,那么它可以指向Ostrich对象,而不会影响程序的正确性。 

class Bird {void fly() { System.out.println("Flying"); }
}class Ostrich extends Bird {@Overridevoid fly() { throw new UnsupportedOperationException("Ostrich cannot fly"); }
}// 使用里氏替换原则
Bird bird = new Ostrich();
bird.fly(); // 这里会抛出异常

依赖倒置原则(Dependency Inversion Principle,DIP)

定义:高层模块不应该依赖低层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

优点:

  • 降低模块间的耦合度:高层模块与低层模块通过接口进行通信,减少了直接依赖。

  • 提高系统的灵活性:可以轻松替换底层实现,而不需要修改高层逻辑。

缺点:

  • 增加代码复杂度:需要定义更多的接口或抽象类,可能使系统的复杂性增加。

  • 性能开销:依赖倒置原则可能引入额外的抽象层,导致性能下降,尤其是在大型系统中。

例子: 假设我们有一个OrderService类依赖于PaymentProcessor类。我们可以将PaymentProcessor抽象为一个接口,并让具体的支付处理器实现该接口,从而降低耦合度。

interface PaymentProcessor {void processPayment(double amount);
}class CreditCardPayment implements PaymentProcessor {@Overridepublic void processPayment(double amount) {}
}class OrderService {private PaymentProcessor paymentProcessor;public OrderService(PaymentProcessor paymentProcessor) {this.paymentProcessor = paymentProcessor;}public void processOrder(double amount) {paymentProcessor.processPayment(amount);}
}

接口隔离原则(Interface Segregation Principle,ISP)

定义:客户端不应该依赖它不需要的接口。即,一个类不应强迫实现不相关的接口,接口应该尽可能小且专注。

优点:

  • 避免臃肿的接口:通过多个小接口,确保每个接口只提供类真正需要的方法。

  • 提高灵活性:客户端只需要实现相关的接口方法,避免实现不必要的功能。

缺点:

  • 接口数量增加:拆分接口可能导致接口数量增多,增加了系统的复杂性。

  • 管理难度加大:需要管理更多的接口,可能导致设计时的困扰。

例子: 假设我们有一个Animal接口,包含eat()fly()方法。如果某些动物(如Dog)不需要飞行能力,那么它们不应该实现fly()方法。

interface Animal {void eat();void fly(); // 这个方法对某些动物来说是多余的
}// 遵循接口隔离原则
interface Eater {void eat();
}interface Flyer {void fly();
}class Dog implements Eater {@Overridepublic void eat() {}
}class Bird implements Eater, Flyer {@Overridepublic void eat() {}@Overridepublic void fly() {}
}

迪米特法则(Law of Demeter,LoD)

定义:一个对象应尽量少地了解其他对象的内部细节。即,一个类不应直接访问另一个类的内部成员,而应该通过其公开接口进行交互。

优点:

  • 降低对象之间的耦合:迪米特法则减少了类与类之间的直接依赖关系,提高了代码的模块化。

  • 增强可维护性:类的内部变化不会直接影响其他类,使系统更加稳定和可维护。

缺点:

  • 可能导致过度封装:在某些情况下,过度使用迪米特法则可能导致不必要的复杂性,产生大量的中间方法和接口。

  • 性能开销:通过中间接口进行交互可能增加系统的开销,尤其是当调用链条较长时。

 例子: 假设我们有一个Department类和一个Employee类。Department类不应该直接访问Employee类的内部细节,而是应该通过公共接口进行交互。

class Employee {private String name;public String getName() { return name; }
}class Department {private List<Employee> employees = new ArrayList<>();public void addEmployee(Employee employee) { employees.add(employee); }public void printEmployeeNames() {for (Employee employee : employees) {System.out.println(employee.getName());}}
}

 


http://www.ppmy.cn/ops/127592.html

相关文章

无人机之自主飞行关键技术篇

无人机自主飞行指的是无人机利用先进的算法和传感器&#xff0c;实现自我导航、路径规划、环境感知和自动避障等能力。这种飞行模式大大提升了无人机的智能化水平和操作的自动化程度。 一、传感器技术 传感器是无人机实现自主飞行和数据采集的关键组件&#xff0c;主要包括&a…

Linux基础项目开发day06:量产工具——业务系统

文章目录 前言一、流程代码框架1、业务系统框架流程2、主页面流程图3、main.c实现流程 二、处理配置文件1、配置文件是啥&#xff1f;config.h 2、怎么处理配置文件&#xff1f;config.c 三、生成界面1、计算每个按钮的Region2、逐个生成按钮画面->生成页面 四、读取输入事件…

微信小程序——消息订阅

首先用到的就是wx.requestSubscribeMessage接口。 注意&#xff1a;用户发生点击行为或者发起支付回调后&#xff0c;才可以调起订阅消息界面 requestSubscribeMessage() {uni.requestSubscribeMessage({tmplIds: [],//需要订阅的消息模板的id的集合&#xff0c;一次调用最多可…

vscode连接远端docker高效开发的方法

1 前言 目前项目源码部署在远端服务器的docker上&#xff0c;但是我习惯在vscode中修改源码并验证。所以需要通过vscode连接远端容器进行代码开发。vscode通过remote ssh连接服务器是比较常见的用法了&#xff0c;但是连接远端容器还是第一次使用。摸索了一阵后发现一个方便快…

laravel 查询数据库

数据库准备 插入 三行 不同的数据 自行搭建 laravel 工程 参考 工程创建点击此处 laravel 配置 数据库信息 DB_CONNECTIONmysql #连接什么数据库 DB_HOST127.0.0.1 # 连接 哪个电脑的 ip &#xff08;决定 电脑 本机&#xff09; DB_PORT3306 # 端口 DB_DATABASEyanyu…

在服务器启动docker容器卡住、无启动成功信息,docker ps一a状态码137

在服务器启动docker容器卡住、无启动成功信息&#xff0c;docker ps 一a状态码137 docker、ubuntu 20.04、emqx 5.8.0背景 想从移动安卓设备往服务器发点数据&#xff0c;因为服务器有固定IP&#xff0c;想起来之前看过的这个mqtt&#xff0c;感觉比较合适&#xff0c;但是启…

【贪心算法】(第二篇)

目录 最⼤数&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 摆动序列&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 最⼤数&#xff08;medium&#xff09; 题目解析 1.题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xf…

淘宝客服自动回复机器人-千牛工作台自动回复机器人-集成RPA+知识库+大模型AI工具

淘宝客服自动回复-千牛工作台接待中心自动回复机器人 影刀通过集成RPA知识库大模型AI工具&#xff0c;帮助商家打造24小时在线响应的AI客服 现在已经实现 llike620