1. 开闭原则:
一个软件实体如类、模块和函数应该对外扩展开放,对修改关闭
强调用抽象构建框架,用实现扩展细节
/**
* 面向接口编程
* 接口不应该经常变化,应该是稳定且可靠的
*/
public interface ICourse {
Integer getId();
String getName();
Double getPrice();
}
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 id;
}
@Override
public String getName() {
return name;
}
@Override
public Double getPrice() {
return price;
}
}
public class Test {
public static void main(String[] args) {
ICourse javaCourse = new JavaCourse(2333, "Java从零开始", 666d);
System.out.println("课程ID:" + javaCourse.getId() + " 课程名称:" + javaCourse.getName() + " 课程价格:" + javaCourse.getPrice());
}
}
那如果有促销,是要在加一个属性?
这里开闭原则可以根据JavaCourse实现一个打折的类,扩展性和复用性更高
/**
* 这里能有效避免在接口中添加新的字段,或则在原来的JavaCourse中处理逻辑
*/
public class JavaDiscountCourse extends JavaCourse{
public JavaDiscountCourse(Integer id, String name, Double price) {
super(id, name, price);
}
// 获得原价
public Double getOriginPrice() {
return super.getPrice();
}
@Override
public Double getPrice() {
// 处理打折的业务逻辑
return super.getPrice() * 0.8;
}
}
变化的都是应用层的子模块
优点:提高软件系统的可复用性及可维护性
2. 依赖倒置原则
定义:高层模块不应该依赖低层模块,二者都应依赖其抽象
补充:抽象不应该依赖细节;细节应该依赖抽象
针对接口编程,不要针对实现编程
首先先看面向实现类编程的一个例子:
public class Archer {
public void javaCourse() {
System.out.println("学习Java课程");
}
public void algorithmCourse() {
System.out.println("学习J算法课程");
}
// public void pythonCourse(){
// System.out.println("学习Python课程");
// }
}
```java
public class Test {
public static void main(String[] args) {
Archer archer = new Archer();
archer.javaCourse();
archer.algorithmCourse();
//archer.pythonCourse();
}
}
那么如果想要增加一个学习Python的需求,那难道还要在Archer.java(低层模块)中添加方法,扩展性比较差Test(应用层,高层模块)中的修改是依赖于底层实现的
引出接口抽象:
public interface ICourse {
void studyCourse();
}
低层模块: 具体的实现类交给高层次模块
public class Archer {
public void studyCourse(ICourse iCourse) {
iCourse.studyCourse();
}
}
也可以通过构造器的形式传参
public class Archer {
private ICourse iCourse;
public Archer(ICourse iCourse) {
this.iCourse = iCourse;
}
public void studyCourse() {
iCourse.studyCourse();
}
}
当然为了减少多次注入(防止每次注入都要新创建Archer)
public class Archer {
private ICourse iCourse;
public void setICourse(ICourse iCourse) {
this.iCourse = iCourse;
}
public void studyCourse() {
iCourse.studyCourse();
}
}
实现类: 如果还想扩展,那么就可以独立编写实现类来实现
public class JavaCourse implements ICourse{
@Override
public void studyCourse() {
System.out.println("学习Java课程");
}
}
public class AlgorithmCourse implements ICourse{
@Override
public void studyCourse() {
System.out.println("学习算法课程");
}
}
Test中
```java
public class Test {
// v1
// public static void main(String[] args) {
// Archer archer = new Archer();
// archer.javaCourse();
// archer.algorithmCourse();
// archer.pythonCourse();
// }
// v2
// public static void main(String[] args) {
// Archer archer = new Archer();
// archer.studyCourse(new JavaCourse());
// archer.studyCourse(new AlgorithmCourse());
// }
// v3
// public static void main(String[] args) {
// Archer archer = new Archer(new JavaCourse());
// archer.studyCourse();
// }
// v4
public static void main(String[] args) {
Archer archer = new Archer();
archer.setICourse(new JavaCourse());
archer.studyCourse();
archer.setICourse(new AlgorithmCourse());
archer.studyCourse();
}
}
以抽象为基础搭建起来的架构要比以细节为基础的架构更加稳定
优点:可以减少类之间的耦合性、提高系统稳定性,提高代码可读性和可维护性,可降低修改程序所造成的风险
3.单一职责原则
不要存在多于一个导致类变更的原因
体现在一个类/接口/方法只负责一项职责
优点:降低类的复杂度、提高类的可读性,提高系统的可维护性,降低变更引起的风险
例如类:
public class Bird {
public void mainWay(String name) {
if (name.equals("鸵鸟"))
System.out.println(name + "用脚走");
else
System.out.println("用翅膀飞");
}
}
如果需要增加特殊的鸟类,那么需要写更多的if else,对原来的bird类也有影响
所以可以将类分离,作用更加单一
public class WalkBird {
public void mainWay(String name) {
System.out.println(name + "用脚走");
}
}
public class FlyBird {
public void mainWay(String name) {
System.out.println("用翅膀飞");
}
}
对于接口:
/***
* ICourse有两个职责
*
* 获得课程信息
* 管理课程
* 管理课程可能会影响课程这个接口的课程信息的内容
*/
public interface ICourse {
String getCourseName();
byte[] getCourseVideo();
void studyCourse();
void refundCourse();
}
对其进行拆分
public interface ICourseContent {
String getCourseName();
byte[] getCourseVideo();
}
public interface ICourseManager {
void studyCourse();
void refundCourse();
}
对于方法,尽可能是方法的作用更加明确和单一,便于扩展供其他方法调用
实际开发中,为了防止类的数量过多,一般接口和方法执行单一变量原则,类看实际情况
4.接口隔离原则
定义:用多个专门的接口,而不是用单一的总接口,客户端不应该依赖它不需要的端口
一个类对一个类的依赖应该建立在最小的接口上
建立单一接口,不要建立庞大臃肿的接口
最应该注意的原则:
注意适度原则,一定要适度
优点:符合高内聚低耦合的设计思想,从而使类具有更好可读性,可扩展性和可维护性
public interface AnimalAction {
void eat();
void fly();
void swim();
}
这样设计,例如Dog这个实现类,它如果实现这个接口,那么就会产生fly()这个空实现
然后接口隔离原则分别实现eat()方法的接口IEat,fly()方法的接口IFly,swim()方法的接口ISwim,那么最终Dog这个类只用实现IEat和ISwim
单一职责原则强调的是职责,接口隔离原则强调的是接口
5.迪米特原则(最少知道原则)
一个对象应该对其他对象保持最少的了解
强调:尽量降低类与类之间的耦合
只和朋友交流,不和陌生人说话
朋友:出现在成员变量、方法的输入输出参数中的类称为成员朋友类
而出现在方法体内部的类不属于朋友类
优点:降低类与类之间的耦合(弱耦合)
6. 里式替换原则
如果对每一个类型为T1的对象o1,都有类型为T2的对象02,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。
定义扩展:
一个软件实体如果适用一个父类的话,那一定适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。(反对子类重写父类)
子类可以扩展父类的功能,但不能改变父类原有的功能。
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松。(入参宽松)
- 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或相等。(出参严谨)
约定子类最好不要重写父类的方法,如果一定要重写的话,可以使用组合聚合等方法实现(没找到合适的例子)
人可以生娃,非洲人欧洲人都可以生娃。反例:变形金刚机器人不可以生娃(入参宽松,出参严谨)
入参宽松
public class Base {
public void method(HashMap hashMap) {
System.out.println("执行父类的HashMap方法");
}
/**
* 反例
*/
public void method(Map map) {
System.out.println("执行父类的map方法");
}
}
public class Child extends Base {
/*@Override
public void method(HashMap hashMap) {
System.out.println("执行子类的HashMap方法");
}*/
/**
* 使用重载
* 重载是入参Map会比HashMap更加宽松,此时执行的时候会执行父类,不执行重载的类
* 入参更加宽松,可以不引起逻辑的混乱
*/
public void method(Map map) {
System.out.println("执行之类的Map方法");
}
/**
* 反例
*/
public void method(HashMap hashMap) {
System.out.println("执行子类的HashMap方法");
}
}
public class Test {
public static void main(String[] args) {
Child child = new Child();
HashMap hashMap = new HashMap();
child.method(hashMap);
}
}
出参严谨
public abstract class Base {
public abstract Map method();
}
/**
* 出参Object包含Map直接报错
*/
public class Child extends Base{
/*@Override
public Object method() {
return null;
}*/
@Override
public Map method() {
return null;
}
}
优点:
约束了继承泛滥,很多非子类父类关系的类,没必要使用继承关系;
加强程序的可维护性,降低需求变更时引起的风险