软件设计原则
1、开闭原则(Open-Close Principle)
指的是一个软件实体(类、软件、模块)应该对扩展开放、对修改关闭。这里的开闭,指的就是对扩展和修改的两个行为的一个原则。强调的是使用抽象建立框架,用实现扩展细节,可以提高程序的可复用性和可维护性。开闭原则的主要思想为在不修改原来的代码的情况下扩展新的功能。
以下为举例说明:
使用一个商店的商品为例,商品有三个属性:id、name、pric。当商品进行促销降价的时候,如何在不修改源代码的情况下完成降价。
以下为商品的接口
interface MyCoures{ Integer getId(); String getName(); Double getPrice(); }
以下Wie原本的商品的实现类
public class MyCouresImpl implements MyCoures { private int id; private String name; private Double price; @Override public Integer getId() { return id; } public MyCouresImpl(int id, String name, Double price) { this.id = id; this.name = name; this.price = price; } @Override public String getName() { return name; } @Override public Double getPrice() { return price; } }
现在要进行的是对商品进行打折促销,如果在源代码上进行修改,会存在风险,也是我们要注意开闭原则的原因。
所以在此处重写一个类继承这个类,并重写其中获取价格的方法。代码如下所示
public class MyDiscountCoureesImpl extends MyCouresImpl{ public MyDiscountCoureesImpl(int id, String name, Double price) { super(id, name, price); } @Override public Double getPrice() { return super.getPrice() * 0.8; } }
类结构图如下所示
2、依赖倒置原则(Dependence Inversion Principle)
指的是在设计代码结构的时候,高层模块不应该依赖于底层模块,应该都依赖于抽象。抽象不应该依赖于细节,应该依赖于接口,通过依赖倒置,可以减少类与类之间的耦合度,提高程序的稳定性,提高代码的可读性和可维护性。
以下为使用依赖倒置原则的例子
以下是keys类,其中定义了学习java和JavaScript方法
public class Keys { public void studyJavaCourse(){ System.out.println("keys正在学java"); } public void studyJavaScriptCourse(){ System.out.println("keys正在学javaScript"); } }
调用代码:
public static void main(String[] args) { Keys keys = new Keys(); keys.studyJavaCourse(); keys.studyJavaScriptCourse(); }
此时假如需求改变了,keys的学性大发,想要学习更多的新知识,就需要在keys的类上添加新的方法,这对于一个已经上线的项目来说是存在风险的。不符合依赖倒置原则。理想的状态是不修改原来的代码,添加新的类就可以进行扩展。
以下为优化代码
将课程提升为接口,里面有一个study方法。
public interface MyCourse { void study(); }
keys也有一个study的方法
public class KeysPlus { public void study(MyCourse myCourse){ myCourse.study(); } }
学习的方法实现接口
public class JavaCoures implements MyCourse{ @Override public void study() { System.out.println("keys正在学java"); } }
public class JavaScriptCoures implements MyCourse{ @Override public void study() { System.out.println("keys正在学javaScript"); } }
学习的实现
public static void main(String[] args) { KeysPlus keys = new KeysPlus(); keys.study(new JavaCoures()); keys.study(new JavaScriptCoures()); }
如果这个时候想要学习新的知识,只需要新添加一个MyCourse的实现类,并实现方法,代码如下
public class GoCoures implements MyCourse{ @Override public void study() { System.out.println("keys正在学go"); } }
新的测试如下
public class Test { public static void main(String[] args) { KeysPlus keys = new KeysPlus(); keys.study(new JavaCoures()); keys.study(new JavaScriptCoures()); keys.study(new GoCoures()); } }
所以我们在编程的过程中,需要以抽象为基准搭建架构,因为它比以细节为架构的要稳健的多,所以需要我们面向接口编程。
3、单一职责原则(Simple Responsibility Principle)
单一职责指的是不要存在多于 一个导致一个类发生变更的原因(只有一个原因导致类发生变化)。
需要使用单一职责的原因:假设现在的一个类负责 两个职能,一旦需求发生变化,修改其中的一个职责的逻辑代码,有可能导致另一个职能的的功能发生变化。所以需要对着一个类的两个只能进行解耦合。将这个类写成两个类。也是一种解耦合的方式的体现,核心的思想在于如果到了不得不修改类的局面,要尽可能的修改较为少的代码。提高程序的可维护性。
言而总之就是要使得一个类、接口方法只负责一项职能。
4、接口隔离原则(Interface Segregation Principle)
定义 接口隔离原则指的是我们应该使用更为细则化的接口,,而不应该使用单一的臃肿的接口,客户端不应该依赖于他不需要的接口,这个原则有三个基本要求:
一个类对另一个类的依赖应该建立在最小的接口之上
建立单一化接口,不要使用一个臃肿的接口
尽量细化接口,接口中的方法尽量少(不是越少越好)
目的接口隔离原则符合我们常说的高内聚、低耦合的思想,可以使得类有更高的扩展性、可读性和可维护性。
举例说明:下面是描述动物行为的接口
没有使用接口隔离原则:使用单一的接口
public class Brid implements IAnimals { @Override public void eat() { System.out.println("鸟为食亡"); } @Override public void fly() { System.out.println("沙鸥翔集"); } @Override public void swim() { } }
public class Fish implements IAnimals { @Override public void eat() { System.out.println("愿者上钩"); } @Override public void fly() { } @Override public void swim() { System.out.println("锦鳞游泳"); } }
可以看到:brid中的swim方法和Fish中的fly方法没有实现具体的功能(而且也不应该出现在实现类中),这就是单一接口所造成的问题
以下是优化过的代码:也就是使用了单一接口原则后的代码
三个方法的接口
public interface IEatAnimals { /** * 民以食为天 */ void eat(); }
public interface IEatAnimals { /** * 民以食为天 */ void eat(); }
public interface ISwimAnimals { /** * 锦鳞游泳 */ void swim(); }
两个实现类
public class Brid implements IEatAnimals, IFlyAnimals { @Override public void eat() { System.out.println("鸟为食亡"); } @Override public void fly() { System.out.println("沙鸥翔集"); } }
public class Fish implements IEatAnimals, ISwimAnimals { @Override public void eat() { System.out.println("愿者上钩"); } @Override public void swim() { System.out.println("锦鳞游泳"); } }
可以看到,这次使用了对应的原则之后,实现类中没有出现多于的方法,在后期维护也是大大减少了代码的修改量。
5、迪米特原则(Law of Demeter)
是指一个对象应该对其他对象保持最少的了解。迪米特原则强调纸盒朋友交流,不和陌生人说话,出现在成员变量、方法的输入、输出参数中的对象都可以成为朋友类,而出现在方法体内部的类不属于朋友类。
6、里氏替换原则(Liskov Substitution Principle)
如果一个软件的实体类适用于一个父类,那么他一定适用于其子类,所有可以引用这个父类的地方都必须能透明的应用这个子类,而且程序的逻辑不变。引申含义为:子类可以扩展父类的功能,但是不能改变父类原有的功能。
子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法
子类可以添加自己的特有的功能
当子类重载父类的方法时,方法的参数输出的限制条件应该比父类的更加宽松
当子类实现父类的抽象方法时,输出的参数(返回值)应该比父类的更加严格或者一致
里氏替换原则的优点
约束继承泛滥,是开闭原则的一种体现
加强程序的健壮性,同时变更时做到了非常好的兼容性,提高程序的可维护性和扩展性,降低需求变更所带来的风险
7、合成复用原则
合成复用原则是指尽量使用对象组合,而不是使用继承的关系达到软件复用的目的。可以使得系统更加的灵活,降低类与类的耦合性一个类的变化。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix