七大设计原则
- 单一职责原则
- 接口隔离原则
- 依赖倒转(置)原则
- 里氏替换原则
- 开闭原则
- 迪米特法则(最少知道原则)
- 合成复用原则
单一职责原则
- 定义
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责,应该仅有一个引起它变化的原因
- 优点
- 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多
- 提高类的可读性,提高系统的可维护性
- 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
接口隔离原则
如果接口不同的实现类只需要用到接口部分方法,但不得不实现接口所有方法,这时候可以将该接口不同实现类所需要的方法迁移到不同新的接口,实现接口隔离
和单一职责的区别:单一职责主要约束的类,其次才是接口和方法,针对的是实现和细节;而接口隔离原则主要约束的是接口,针对的是抽象和程序整体框架的构建
依赖倒转原则
依赖倒转的中心思想是面向接口编程,高层次的模块不应该依赖低层模块,二者都应该依赖抽象;抽象不依赖细节,细节依赖抽象。
interface A{
void function();
}
class AImpl implements A{
public void function(){
//do something
}
}
class B{
private A a;
public void setA(A a){
this.a = a;
}
public void function(){
// do something
a.function();
// do something
}
}
上面的代码可以理解为面向接口A的编程,对A的依赖可以理解成依赖倒转的倒转方式——程序B本身不直接将A的实现耦合依赖到代码中,而是依赖A的抽象,然后通过setA方法由客户端自己设置依赖A的实现,这样客户端对不同的A的实现都可以通过setA方法设置,而B不需要做修改,通过对接口A的依赖解耦了B对A不同实现的依赖关系
里氏替换原则
- 针对的是父类(非接口)、子类之间的原则,子类尽量不去重写父类已经实现的方法,如果迫不得已需要重写,可以让进行聚合,组合,依赖或者抽象更基础的父类来解决问题。假如不遵循该原则,会造成子类调用重写方法时语意不明的情况(以为仍然执行的是父类逻辑)
- 所谓替换:客户端调用父类的地方一定可以被其子类替换,且程序的行为不会发生变化
- 里氏替换包含的四层含义
- 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法
- 子类中可以实现自己特有的方法
- 子类重载父类方法时,方法的前置条件(方法形参)要比父类方法输入参数更宽松
- 子类实现父类抽象方法时,方法的返回值要比父类方法返回值更严格
开闭原则
对扩展开放对修改关闭,是所有设计模式的基石和目标,所有的原则都是围绕开闭原则展开
迪米特法则(最少知道原则)
一个对象应该对其他对象保持最少了解,复杂的逻辑有功能提供者隐藏,不对外暴露
- 该原则的指导方针:就任何对象而言,该对象的方法内,我们只应该调用属于以下范围的方法
- 该对象本身
- 被当做方法的参数而传递进来的对象
- 此方法所创建或实例化的任何对象
- 对象的任何组件(即成员变量的方法)
- 代码示例
public class Car {
private Engine engine;
public void start(Key key) {
Door door = new Door();
//满足第二点:key被当做方法的参数而传递进来的对象,所以允许调用其方法,满足最少知道原则
boolean authorized = key.turns();
if (authorized) {
// 满足第4点:engine是对象的组件,所以允许调用其方法
engine.start();
// 满足第一点:该对象本身的方法允许被调用
updateDashboardDisplay();
//满足第三点:door是此方法所创建的对象,允许调用其方法
door.lock();
}
}
private void updateDashboardDisplay() { ... }
}
- 优缺点:减少了对象之间的依赖是优点,但是采用这个原则也会导致更多的“包装”类被制造出来,以处理和其他组件的沟通,这可能会导致复杂度和开发时间的增加,并降低运行时的性能
组合复用原则
尽量使用聚合和合成,而不是继承关系达到复用目的
- 继承是强耦合的关系(白箱复用),父类方法的实现细节暴露到子类,破坏了包装,并且继承是静态的依赖,在运行时不能够发生变化
- 合成/聚合(黑箱复用),依赖对象的内部实现对调用方是不可见的
参考
Java的七大设计原则
《Head First 设计模式》