1.1开闭原则
- 开闭原则(open-closed principle,OCP)是指一个软件实体(如类,模块和函数)应该对扩展开放,对修改关闭。所谓的开闭,也正是对扩展和修改两个行为的一个原则。
- 强调用抽象构建框架,用实现扩展细节,可以提高软件系统的可复用性及可维护性;例如版本更新时,我们尽可能不修改源代码就可以添加新功能。
首先创建一个课程接口JavaCourse
| |
| |
| |
| |
| public interface ICourse { |
| Integer getId(); |
| String getName(); |
| Double getPrice(); |
| } |
整个课程有许多分类,Java、Python、Js等等......
紧接着我们创建一个Java课程类,该类实现课程接口
| |
| |
| |
| |
| |
| |
| |
| public class JavaCourse implements ICourse{ |
| private Integer id; |
| private String name; |
| private Double price; |
| |
| public JavaCourse(Integer id, String name, Double price) { |
| this.id = id; |
| this.name = name; |
| this.price = price; |
| } |
| |
| @Override |
| public Integer getId() { |
| return this.id; |
| } |
| |
| @Override |
| public String getName() { |
| return this.name; |
| } |
| |
| @Override |
| public Double getPrice() { |
| return this.price; |
| } |
| } |
| |
突然有一天,老板说把Java课程的价格做一下优惠,但是如果修改JavaCourse中的getPrice()方法,则存在一定的风险,可能影响其他地方的调用结果,为了避免此类事件发生 ,我们就需要在不修改原有代码的前提下,实现这个价格优惠的功能
- 解决方案:创建一个处理优惠逻辑的类JavaDiscountCourse
| |
| |
| |
| |
| |
| |
| |
| public class JavaDiscountCourse extends JavaCourse{ |
| public JavaDiscountCourse(Integer id, String name, Double price) { |
| super(id, name, price); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| public Double getOriginPrice() { |
| System.out.println("原商品价格"); |
| return super.getPrice(); |
| } |
| |
| public Double getPrice(){ |
| System.out.println("降价后的商品价格"); |
| return super.getPrice() * 0.618; |
| } |
| |
| } |
1.2依赖倒置原则
- 依赖倒置原则(Dependence Inversion Principle,DIP)是指设计代码结构时,高层模块不应该依赖低层模块,二者都应该依赖其抽象。
- 抽象不应该依赖与细节,细节应该依赖抽象。
- 通过依赖倒置,可以减少类与类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,并且能降低修改程序所造成的风险。
创建一个Tom类:
| |
| |
| |
| |
| |
| |
| |
| public class Tom { |
| public void studyJavaCourse(){ |
| System.out.println("studyJavaCourse"); |
| } |
| public void studyPythonCourse(){ |
| System.out.println("studyPythonCourse studyPythonCourse"); |
| } |
| |
| public static void main(String[] args) { |
| Tom tom = new Tom(); |
| tom.studyJavaCourse(); |
| tom.studyPythonCourse(); |
| } |
| } |
目前Tom是在学习Java和Python,但是后续想学习其他课程的时候,就需要添加一个方法,没多学习一个就得多添加一个方法,这个时候,因为业务扩展,要从低层到高层(调用层)依次修改代码,这样极其不方便。
- 创建一个课程接口ICourse:
| |
| |
| |
| |
| public interface ICourse { |
| void study(); |
| } |
- 编写JavaCourse类:
| |
| |
| |
| |
| |
| |
| |
| public class JavaCourse implements ICourse{ |
| @Override |
| public void study() { |
| System.out.println("Java课程....."); |
| } |
| } |
- 编写PythonCourse类:
| |
| |
| |
| |
| |
| |
| |
| public class PythonCourse implements ICourse{ |
| @Override |
| public void study() { |
| |
| System.out.println("Python课程学习......"); |
| } |
| } |
- 修改Tom类:
| public class Tom { |
| |
| |
| |
| |
| public void study(ICourse course){ |
| course.study(); |
| } |
| } |
- 新的调用方式:
| public static void main(String[] args) { |
| Tom tom = new Tom(); |
| |
| tom.study(new JavaCourse()); |
| tom.study(new JavaCourse()); |
| tom.study(new PythonCourse()); |
| |
| |
| } |
- DIP构造器注入方式
在使用构造器注入时,在调用的时候,每次都要创建实例
| public class Tom { |
| private ICourse course; |
| |
| |
| |
| |
| |
| public Tom(ICourse course) { |
| this.course = course; |
| } |
| public void study(){ |
| course.study(); |
| } |
| } |
调用方式:
| public static void main(String[] args) { |
| Tom tom = new Tom(new JavaCourse()); |
| tom.study(); |
| } |
- 当Tom时全局单例,只能选择用Setter方式来注入
| public class Tom { |
| private ICourse course; |
| |
| |
| |
| |
| |
| public void setCourse(ICourse course) { |
| this.course = course; |
| } |
| public void study(){ |
| course.study(); |
| } |
| } |
调用代码:
| public static void main(String[] args) { |
| Tom tom = new Tom(); |
| tom.setCourse(new JavaCourse()); |
| tom.study(); |
| tom.setCourse(new PythonCourse()); |
| tom.study(); |
| } |
单一职责原则
- 单一职责(Simple Responsibility Pinciple,SRP)是指不要存在多于一个导致类变更的原因
- 假设我们有一个类负责两个职责,一旦发生需求变更,修改其中一个职责的逻辑代码,有可能导致另一个职责的功能发生故障,这样一来,这个类就存在两个导致类变更的原因。
- 为了避免上述情况的出现,我们将两个职责用两个类来实现,进行解耦。后期需求变更维护互不影响。
- 总体来说,就是一个类、接口或方法只负责一项职责
课程有直播课和录播课两类;直播课不能快进和快退,录播课可以任意地反复观看,功能职责不一样。
首先创建一个Course类:
| |
| |
| |
| |
| public class Course { |
| public void study(String courseName){ |
| if ("直播课".equals(courseName)){ |
| System.out.println("不能快进==>" + courseName); |
| }else if ("录播课".equals(courseName)){ |
| System.out.println("可以快进==>"+courseName); |
| } |
| } |
| |
| |
| public static void main(String[] args) { |
| Course course = new Course(); |
| course.study("直播课"); |
| course.study("录播课"); |
| } |
| } |
从上面的代码,我们可以看出Course承担了两种处理逻辑。后续如果对其中一种课程进行加密,由于两种课程的加密逻辑一定不相同,必须修改代码。而修改代码的逻辑势必会相互影响,容易带来不可控的风险。
- 首先创建两个类LiveCourse和ReplayCourse
| |
| |
| |
| |
| |
| |
| |
| public class LiveCourse { |
| public void study(String courseName){ |
| System.out.println("我是直播课程==>" + courseName); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| public class ReplayCourse { |
| public void study(String courseName){ |
| |
| System.out.println("我是录播课程==>"+courseName); |
| } |
| } |
- 调用过程
| public static void main(String[] args) { |
| LiveCourse liveCourse = new LiveCourse(); |
| liveCourse.study("直播课"); |
| ReplayCourse recordCourse = new ReplayCourse(); |
| recordCourse.study("录播课"); |
| } |
通过创建两个单独的类,就实现了将两个职责从原先的Course中拆分的操作
接口隔离原则
接口隔离原则(Interface Segregation Pinciple,ISP)是指用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口。
符合高内聚、低耦合的设计思想
- 一个类对另一个类的依赖应该建立在最小的接口之上。
- 建立单一接口,不要监理庞大臃肿的接口。
- 尽量细化接口,接口中的方法尽量少(适度即可)。
1创建一个IAnimal接口:
| |
| |
| |
| |
| public interface IAnimal { |
| void eat(); |
| void fly(); |
| void swim(); |
| }`在这里插入代码片` |
2创建Bird类:
| |
| |
| |
| |
| |
| |
| |
| public class Bird implements IAnimal{ |
| @Override |
| public void eat() { |
| System.out.println("bird eat"); |
| } |
| |
| @Override |
| public void fly() { |
| System.out.println("bird fly"); |
| } |
| |
| @Override |
| public void swim() { |
| |
| } |
| } |
3创建Dog类:
| |
| |
| |
| |
| |
| |
| |
| public class Dog implements IAnimal{ |
| @Override |
| public void eat() { |
| System.out.println("I am a dog 吧唧吧唧"); |
| } |
| |
| @Override |
| public void fly() { |
| |
| } |
| |
| @Override |
| public void swim() { |
| System.out.println("Dog swim...."); |
| } |
| } |
通过2、3可以看出,Bird的swim方法只能空着(因为鸟不能游泳。。。。),而且Dog的fly方法也是实现不了的。
这个时候我们针对不同的动物行为来设计不同的接口,分别设计IEatAnimal、IFlyAnimal、ISwimAnimal接口:
| |
| |
| |
| |
| |
| |
| |
| public interface IEatAnimal { |
| void eat(); |
| } |
| |
| |
| |
| |
| public interface IFlyAnimal { |
| void fly(); |
| } |
| |
| |
| |
| public interface ISwimAnimal { |
| void swim(); |
| } |
实现上面三个接口之后,修改dog类如下:
| |
| |
| |
| |
| |
| |
| |
| public class Dog implements IEatAnimal, ISwimAnimal { |
| |
| @Override |
| public void eat() { |
| System.out.println("dog --> eat"); |
| } |
| |
| @Override |
| public void swim() { |
| System.out.println("dog --> swim"); |
| } |
| } |
这样一来就不会存在实现类中不需要的方法了,防止客户端使用了它不需要的接口。
迪米特原则
迪米特原则(Law Of Demeter LoD)是指一个对象应该对其他对象保持最少的了解,又叫最少知道原则(Least Knowledge Pinciple,LKP),尽量降低类与类之间的耦合度。
- 只和朋友交流,不和陌生人说话
1、首先创建一个Course类
2、创建一个TeamLeader,用于统计Course信息
| public class TeamLeader { |
| public void checkNumberOfCourses(){ |
| List<Course> courseList = new ArrayList<>(); |
| for (int i = 0; i < 20; i++) { |
| courseList.add(new Course()); |
| } |
| System.out.println("目前已发布的课程数量是:"+courseList.size()); |
| } |
| } |
3、创建一个Boss,老板想看课程数量:
| public class Boss { |
| public void commandCheckNumber(TeamLeader teamLeader){ |
| |
| teamLeader.checkNumberOfCourses(); |
| } |
| } |
4、调用方法:
| public static void main(String[] args) { |
| Boss boss = new Boss(); |
| |
| boss.commandCheckNumber(new TeamLeader()); |
| } |
这样一来,Boss就和Course分离开来了,boss不用去关心Course的具体实现,他只想知道有多少个课程数量
里氏替换原则
里氏替换原则(Liskov Substitution Princple,LSP)是指如果对每一个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都替换成O2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。
- 一个软件实体如果适用于一个父类,那么一定适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。
- 子类可以扩展父类的功能,但不能改变父类原有的功能
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
- 子类可以增加自己持有的方法
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松
- 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或与父类一样。(上转型或者转同型)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南