设计原则
核心思想
- 找出应用中的变化之处,把它们独立出来,不要和固定的代码混在一起。
- 针对接口编程,而不是针对实现编程。
- 为了交互对象之间的松耦合设计而努力。
七大设计原则
1. 单一职责原则
定义:每个方法、每个类、每个框架只负责一项职责。
目的:
- 降低复杂度,降低变更引起的风险。
- 提高可读性、可维护性。
2. 接口隔离原则
定义:客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小接口上。
反例

正例

3. 开闭原则(Open Closed Principle,OCP)
定义:
- 开闭原则是最基础、最重要的设计原则。
- 软件中的实体(类,模块,函数等等)对扩展是开放的(对提供方),但对修改是封闭的(对使用方)。
- 当软件有需求变化时,是通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
- 编程中遵循其他原则,以及使用设计模式的目的,就是为了遵循开闭原则。
举例:原本有一个汽车类,现在需求是要给汽车的价格打8折。
public class Car{
private BigDecimal price;
public BigDecimal getPrice(){
return price;
}
}
反例:直接修改源代码。
public class Car{
private BigDecimal price;
public BigDecimal getPrice(){
return price * 0.8;
}
}
正例:扩展一个子类实现需求。
public class DiscountCar extends Car{
@Override
public BigDecimal getPrice(){
return price * 0.8;
}
}
4. 依赖倒转(置)原则
定义:
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
- 依赖倒转的中心思想是面向接口编程。
反例

正例

5. 里氏替换原则(Liskov Substitution Principle)
定义:
- 子类可以替换父类;任何父类可以出现的地方,子类一定可以出现。
- 里氏替换原则是继承复用的基石:只有当子类可以替换父类且软件单位的功能不受到影响时,基类才能真正被复用。
注意:
- 能否使用继承依据:是否有"is a"关系?子类替换父类后,业务逻辑是否变化?如果业务逻辑发生变化则不能使用继承(经典问题:正方形非长方形)。
- 继承带来便利也带来了弊端。如:使用继承给程序带来侵入性,程序可移植性降低,增加对象间的耦合性。修改一个父类,必须考虑所有子类,修改父类可能会导致子类功能产生故障。
- 优先使用组合或者聚合来解决问题。
6. 迪米特法则(Law of Demeter)
定义:
- 又叫“最少知道原则”:即一个类对自己依赖的类知道的越少越好。
- 更简单的定义:只与朋友通信。(朋友:出现在成员变量的类,方法参数的类,方法返回值中的类,自己new出来的类;非朋友:出现在局部变量的类。)
反例

正例

注意:
- 迪米特法则的核心是降低类之间的耦合;只是要求降低类间耦合,并不是要求完全没有依赖关系(这是不可能的)。
- 缺点:要完全符合迪米特法则,会在系统里造出大量的小方法,这些方法仅仅是传递间接的调用,与系统的业务逻辑无关。
- 结论:可以适当违反。
7. 组合优于继承
定义:在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
JDK中的反例:JDK中的Stack,继承了Vector,仅仅为了复用一些方法,搞得Stack拥有不属于栈特性的方法;只能说Stack像一个栈,而不是一个纯粹的栈。
注意:
- 如果父类作者与子类作者不是同一个人,就别用继承;因为父类不知道未来子类会重写自己的哪个方法;子类也不知道未来父类会新增哪个方法。
- 如果父类作者与子类作者是同一个人,就放心使用继承;因为作者同时控制父类和子类。
- 如果仅仅为了复用代码而继承别人的类,难免会出现“沟通”上的问题。