设计原则
- 开闭原则
- 依赖倒置原则
- 单一职责原则
- 接口隔离原则
- 迪米特法则
- 里氏替换原则
- 合成复用原则
详解
1、开闭原则
- 对拓展开放,对修改关闭,用抽象构建框架,用实现拓展细节
简单来说,比如我们有一个实现用户登录的功能已经上线了,现在要对已经实现的功能进行拓展,这时应该要保证线上已有功能不进行修改,只是对功能进行拓展
/**
* 课程接口
*/
public interface Couser {
Integer getCourseId();
String getCouseName();
Double getCousePrice();
}
public class JavaCouser implements Couser {
private Integer couserId;
private String courseName;
private Double coursePrice;
@Override
public Integer getCourseId() {
return couserId;
}
@Override
public String getCouseName() {
return courseName;
}
@Override
public Double getCousePrice() {
return coursePrice;
}
public JavaCouser(Integer couserId,String courseName,Double coursePrice) {
this.couserId = couserId;
this.courseName = courseName;
this.coursePrice = coursePrice;
}
public class JavaDiscountCourse extends JavaCouser {
public JavaDiscountCourse(Integer couserId, String courseName, Double coursePrice) {
super(couserId, courseName, coursePrice);
}
public Double disCountCourse() {
return getCousePrice() * 0.7;
}
}
public class Test {
public static void main(String[] args) {
/**
* 理解为已上线的功能
* 展示课程和课程价格
*/
Couser couser = new JavaCouser(1,"Java",200D);
System.out.println("课程 :" + couser.getCouseName() + ", 价格是:" + couser.getCousePrice());
/**
* 需要拓展的功能
* 课程价格打七折
*/
Couser couser1 = new JavaDiscountCourse(1,"Java",200D);
couser1 = (JavaDiscountCourse) couser1;
System.out.println("课程 :" + couser1.getCouseName() + ", 折扣后的价格是:" + ((JavaDiscountCourse) couser1).disCountCourse());
}
}
简单来说:对以往已有功能保持不变,进行功能拓展,防止出现因修改之前代码而导致的系统异常等。。
2、依赖倒置原则
- 高层模块不应该依赖于低层模块,二者都应该依赖其抽象,抽象不应该依赖于细节,细节应该依赖于抽象
低层模块的设计应该从整体出发,先整体后细节,个人认为可以说是面向接口开发,Spring 的依赖注入便是此原则的实践
public class Mom {
public void say(Book book) {
book.context();
}
}
public class Book {
public void context() {
System.out.println("在很久很久之前有一个故事。。。。。");
}
}
public static void main(String[] args) {
Mom mom = new Mom();
mom.say(new Book());
}
有一个 Mom 对象负责给别人讲故事,但现在只能讲故事
在很久很久之前有一个故事。。。。。
如若我们要听新闻的话
public class NewPaper {
public void context() {
System.out.println("据最新报道。。。。");
}
}
这样的话,我们就需要对 Mom 类中的 say() 方法进行修改变动,此时,Mom类属于高层应用不应该依赖于底层应用,也就是 Book 和 NewPaper,应该讲底层模块进行抽象,依赖抽象进行实现
public interface IReader {
void context();
}
新增 IReader 接口,让 Book 和 NewPaper 分别实现接口,这样对功能进行拓展时,不需要对高层模块 Mom 类进行改动。 由类图可以看出,高层模块不依赖于低层模块,符合原则定义
3、单一职业原则
- 一个类,一个接口,一个方法只负责一项职责
简单来说就是一个类,一个方法只实现一个功能,通常我们将功能相似的方法放到一个类里,比如,用户的相关信息,我们放到一个 UserInfo 类中,那么这个类要实现查询用户列表,新增用户,删除用户。这时在这个类中应该有三个方法,完成这三个功能,而不是在一个方法或者两个方法中,通过参数判断等形式来实现不同的功能。
4、接口隔离原则
- 一个类对应一个类的依赖应该建立在最小的接口上,客户端不应该实现其不需要的方法
由图可见,有一个 Animal 接口,里面有 eat,run,fly 方法,它有两个实现分别是 Monkey,Bird,但是 Monkey 明显是不需要实现 fly 方法的,因为它根本不需要这个,除非它是孙大圣。这显然是不符合接口隔离原则的
修改一下,用多个接口去实现不同的功能,达到一个类对应一个类的依赖应该建立在最小的接口上,简单来说就是在实现接口方法时不需要去实现那些对于这个类中完全用不到的方法,可以采用多个接口去实现特定功能的方式
5、迪米特法则(最少知道原则)
- 一个对象应该对其他对象保持最少了解。又叫最少知道原则
简单来说就是引用最少的依赖,比如代码中 import java.util.*, 我们肯定不可能把 util 里面的包全都用了,举例来说,作者把文稿给编辑,编辑交给出版社,出版社进行印刷出版,那么在作者这个类中就不需要对出版者这个类进行 import ,只需要有一个 send 方法将文稿发给编辑即可,也就是在作者这个类中,我们只需要 impoer 编辑即可
6、里氏替换原则
- 如果对每一个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以T1定义的所有程序P在将所有o1对象替换为o2对象时,程序P的行为没有发生改变,那么类型T2是类型T1的子类型。
- 所有引用基类的地方必须能透明使用其子类对象
简单来说就是 子类在继承父类时,不要对父类中已有代码进行修改
- 子类可以实现父类的抽象方法,但是不能覆盖非抽象方法
- 子类对父类方法进行重载时,入参要求只能更宽松,
- 子类中可以新增自己的特有功能
- 当子类的方法去实现父类的方法时(重写、重载或实现其抽象方法)其返回值类型都应该更严格
7、合成复用原则
- 尽量使用对象组合,聚合、而不是继承关系达到功能复用的目的
组合:公司和部门的关系,公司破产部门也就不复存在
聚合:部门和员工的关系,部门解散,员工还可以存在生活