七大设计原则
开闭原则
开闭原则是设计模式中的总原则。
开闭原则就是说:对扩展开放,对修改关闭。
模块应该在不修改原有代码的前提下进行拓展,这就需要使用接口和抽象类来实现预期效果。
开闭原则要求我们尽可能通过拓展来实现变化,尽可能少地改变已有模块,特别是底层模块。
假设我们时4S店汽车销售,类图如图所示
这是一个Java工程,依赖了Lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
public interface Car {
String getName();
int getPrice();
}
@AllArgsConstructor
public class BenzCar implements Car {
private String name;
private int price;
@Override
public String getName() {
return this.name;
}
@Override
public int getPrice() {
return this.price;
}
@Override
public String toString() {
return "车名:" + this.getName() + "\t价格:" + this.getPrice() + "万元";
}
}
模拟一下销售记录
public class OpenCloseTest {
private final static List<Car> CARS = new ArrayList<>();
static {
CARS.add(new BenzCar("梅赛德斯-迈巴赫S级轿车",138));
CARS.add(new BenzCar("梅赛德斯-AMG S 63 L 4MATIC+", 230));
CARS.add(new BenzCar("梅赛德斯-奔驰V级", 50));
}
public static void main(String[] args) {
System.out.println("4s店售车记录(不含金融服务费):");
CARS.forEach(System.out::println);
}
}
4s店售车记录(不含金融服务费):
车名:梅赛德斯-迈巴赫S级轿车 价格:138万元
车名:梅赛德斯-AMG S 63 L 4MATIC+ 价格:230万元
车名:梅赛德斯-奔驰V级 价格:50万元
如果老板要求添加金融服务费,100w收取5%,50~100w收取2%,50w一下没有。我们要怎么做???
- 修改Car接口,新增一个获取金融服务费之后的方法。这样做实现Car接口的所有子类都有改变,不可取。
- 修改BenzCar类,这样做就改变了原有的逻辑了,也不可取。
- 新增BenzCar子类,重写getPrice方法。推荐使用。
public class FinanceBenzCar extends BenzCar {
public FinanceBenzCar(String name, int price) {
super(name, price);
}
@Override
public int getPrice() {
int selfPrice = super.getPrice();
if (selfPrice >= 100) {
// 收取5%的金融服务费
return selfPrice + selfPrice * 5 / 100;
} else if (selfPrice >= 50) {
// 收取2%的金融服务费
return selfPrice + selfPrice * 2 / 100;
}
return selfPrice;
}
@Override
public String toString() {
return "车名:" + super.getName() + "\t价格:" + this.getPrice() + "万元";
}
}
测试类修改如下
public class OpenCloseTest {
private final static List<Car> CARS = new ArrayList<>();
private final static List<Car> FINANCECARS = new ArrayList<>();
static {
CARS.add(new BenzCar("梅赛德斯-迈巴赫S级轿车",138));
CARS.add(new BenzCar("梅赛德斯-AMG S 63 L 4MATIC+", 230));
CARS.add(new BenzCar("梅赛德斯-奔驰V级", 50));
FINANCECARS.add(new FinanceBenzCar("梅赛德斯-迈巴赫S级轿车",138));
FINANCECARS.add(new FinanceBenzCar("梅赛德斯-AMG S 63 L 4MATIC+", 230));
FINANCECARS.add(new FinanceBenzCar("梅赛德斯-奔驰V级", 50));
}
public static void main(String[] args) {
System.out.println("4s店售车记录(不含金融服务费):");
CARS.forEach(System.out::println);
System.out.println("\n4s店售车记录(包含金融服务费):");
FINANCECARS.forEach(System.out::println);
}
}
4s店售车记录(不含金融服务费):
车名:梅赛德斯-迈巴赫S级轿车 价格:138万元
车名:梅赛德斯-AMG S 63 L 4MATIC+ 价格:230万元
车名:梅赛德斯-奔驰V级 价格:50万元
4s店售车记录(包含金融服务费):
车名:梅赛德斯-迈巴赫S级轿车 价格:144万元
车名:梅赛德斯-AMG S 63 L 4MATIC+ 价格:241万元
车名:梅赛德斯-奔驰V级 价格:51万元
这样做没有修改如何已有类,就实现了逻辑。面向扩展开放,面向修改关闭
开闭原则总结:
-
提高代码复用性
-
提高代码可重用性
单一职责原则
简单来说就是保证设计类、接口、方法时功能做到单一,权责明确。单一职责原则指的是一个类且只有一个可改变的原因。
如果模块或类承担的职责过多,就等于这些职责耦合在一起,这样一个模块可能会削弱或抑制其他模块的能力,这样的耦合很脆弱
假设我们修改用户名密码,我们需要为修改密码单独划分一个接口,而不是在修改全部信息中判断是否为null去拼接动态SQL
单一职责原则总结
- 降低类的复杂性,提高可读性、可扩展性
- 但是用 “职责” 或 “变化原因” 来衡量接口或类设计得是否优良,但是 “职责” 和 “变化原因” 都是不可度量的,因项目、环境而异;指责划分稍微不当,很容易造成资源浪费,代码量增多,好比微服务时服务边界拆分不清
里氏替换原则
所有使用父类的地方必须能透明的使用其子类对象。
里氏替换原则是开闭原则的实现基础,他告诉我们设计程序的时候尽可能使用父类进行对象的定义和引用,运行时在确定对应的子类。
里氏替换原则总结
- 提高代码复用性,子类继承父类自然也继承了父类的方法和属性
- 提高代码扩展性,子类通过实现父类方法进行功能扩展
- 继承有侵入性,继承就必然有父类的属性和方法
- 增加代码耦合性。父类方法变更需要考虑子类方法是否变更
依赖倒置原则
程序要依赖于抽象接口,不要依赖于具体实现。对抽象进行编程,不要对实现编程。
依赖倒置原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。
其核心思想是:要面向接口编程,不要面向实现编程。
依赖倒置原则总结
- 高层模块不应该依赖低层模块,应该依赖抽象
- 接口或抽象不应该依赖于实现
- 实现类应该依赖于接口或抽象类
接口隔离原则
客户端不应该依赖它不需要对接口,类间的依赖关系应该建立在最小接口上。简单来说就是接口尽量细化,方法尽量少。
我们所讲的接口主要分为两大类,一是实例接口,比如使用 new 关键字产生一种实例,被 new 的类就是实例类的接口。从这个角度出发的话,java 中的类其实也是一种接口。二是类接口,java 中常常使用 interface 关键字定义。
接口隔离原则总结
- 接口尽量粒度化,保持接口纯洁性
- 接口要高内聚,即减少对外交互
迪米特法则
也叫最少知识原则。定时是一个软件实体应该尽可能少的于其他实体发挥相互作用。迪米特法则的初衷在于降低类之间的聚合。
迪米特法则总结
- 类定义时尽量内敛,少用public,尽量用private、protected
合成复用原则
将已有的对象纳入新对象中,作为新对象的成员对象来实现。新对象可以调用已有对象的功能,从而达到复用。尽可能使用聚合的方式,而不是使用继承。
都知道,类之间有三种基本关系,分别是:关联(聚合和组合)、泛化(与继承同一概念)、依赖。
这里我们提一下关联关系,客观来讲,大千世界中的两个实体之间总是有着千丝万缕的关系,归纳到软件系统中就是两个类之间必然存在关联关系。如果一个类单向依赖另一个类,那么它们之间就是单向关联。如果彼此依赖,则为相互依赖,即双向关联。
关联关系包括两种特例:聚合和组合。聚合,用来表示整体与部分的关系或者 “拥有” 关系。其中,代表部分的对象可能会被代表多个整体的对象所拥有,但是并不一定会随着整体对象的销毁而销毁,部分的生命周期可能会超越整体。好比班级和学生,班级销毁或解散后学生还是存在的,学生可以继续存在某个培训机构或步入社会,生命周期不同于班级甚至大于班级。
合成,用来表示一种强得多的 “拥有” 关系。其中,部分和整体的生命周期是一致的,一个合成的新的对象完全拥有对其组成部分的支配权,包括创建和泯灭。好比人的各个器官组成人一样,一旦某个器官衰竭,人也不复存在,这是一种 “强” 关联。
合成复用原则总结
- 新对象可以调用已有对象功能,从而达到复用