开闭原则
单一职责原则
里氏替换原则
依赖倒置原则
接口隔离原则
迪米特法则
开闭原则
OCP,open-closed principle,是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统,开闭原则只定义了对修改关闭,对扩展开放。
其实只要遵循下面5中设计模式,设计出来的软件就是符合开闭原则的。
开闭原则是目标,其他5个是实现方法。
类,方法可以扩展,不能修改。面对新需求,增加代码,不是修改代码。
关键就在于“抽象”。把系统的所有可能的行为抽象成一个抽象底层,要预见所有可能的扩展
参数类型、引用对象尽量使用接口或者抽象类,而不是实现类
里氏替换原则
继承有一些优点:
1.子类拥有父类的所有方法和属性,提高了代码的重用性。
2.还可以添加自己的功能,提高了代码的扩展性。
但又有点也同样存在缺点:
1.继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法。
2.降低了代码的灵活性。因为继承时,父类会对子类有一种约束。
3.增强了耦合性。当需要对父类的代码进行修改时,必须考虑到对子类产生的影响。有时修改了一点点代码都有可能需要对打断程序进行重构。
如何扬长避短呢?方法是引入里氏替换原则
LSP,Liskov Substitution Principle
只要有父类出现的地方,都可以用子类来替代,而且不会出现任何错误和异常
约束主要体现在四个方面:
1.子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
public class Test {
public static void main(String[] args) {
System.out.println("父类的运行结果");
A a = new A();
a.fun(1, 2);
//父类存在的地方,可以用子类替代
//子类B替代父类A
System.out.println("子类替代父类后的运行结果");
B b = new B();
b.fun(1, 2);
}
}
class A {
public void fun(int a, int b) {
System.out.println(a + "+" + b + "=" + (a + b));
}
}
class B extends A {
@Override
public void fun(int a, int b) {
System.out.println(a + "-" + b + "=" + (a - b));
}
}
父类的运行结果
1+2=3
子类替代父类后的运行结果
1-2=-1
上面覆盖了父类的非抽象方法,出了问题,违反了里氏替换原则
2.子类中可以增加自己特有的方法。
3.当子类重载父类的非抽象方法时,方法的形参要比父类方法的形参更宽松,即是父类方法形参的父类或原来的类
public class Test {
public static void main(String[] args) {
System.out.println("父类的运行结果");
A a = new A();
HashMap<Object, Object> map = new HashMap<>();
a.fun(map);
//父类存在的地方,可以用子类替代
//子类B替代父类A
System.out.println("子类替代父类后的运行结果");
B b = new B();
b.fun(map);
}
}
class A {
public void fun(Map map) {
System.out.println("父类被执行...");
}
}
class B extends A {
public void fun(HashMap map) {
System.out.println("子类被执行...");
}
}
父类的运行结果
父类被执行…
子类替代父类后的运行结果
子类被执行…
执行结果不一样,违反了里氏替换原则
4.当子类的方法实现父类的抽象方法时,即方法的返回值要比父类更严格,即是父类方法返回值的子类或原来的类
否则,编译就会报错
设计模式体现:策略模式,代理模式
依赖倒转原则
(Dependency Inversion Principle, DIP):抽象不应该依赖于细节,细节应当依赖于抽象。
换言之,要针对抽象(接口)编程,而不是针对实现细节编程
List list = new Vector();
变量的静态类型是List,真实类型是Vector
只要对象有抽象类型,任何地方都使用抽象类型
对象的创建,使用具体类型的构造函数,违背了开闭原则和依赖倒置原则
使用工厂模式,将这一违反原则的做法孤立到易于控制的地方
抽象类智能继承一个,接口可以实现多个。接口定义混合类型好
接口定义类型,抽象类提供默认实现,其实缺省默认模式,命名规范:Abstract+接口名
接口隔离原则
Interface Segregation Principle,ISP
使用多个专门的接口,比使用一个总接口好
包含三层含义:
一个类对另一个类的依赖应该建立在最小的接口上;
一个接口代表一个角色,不应该将不同的角色都交给一个接口,因为这样可能会形成一个臃肿的大接口;
不应该强迫客户依赖它们从来不用的方法。
迪米特法则
迪米特法则(Law of Demeter),英文简写为: LoD。
又叫作最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。英文
迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。
迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。
有兴趣可以研究一下设计模式的门面模式(Facade)和中介模式(Mediator),都是迪米特法则应用的例子。
单一职能原则
single responsibilty principle, SRP
一个类应该有且只有一个变化的原因。
把职责定义为变化的原因。
当需求变化时,将通过更改职责相关的类来体现。如果一个类拥有多于一个的职责,则多个职责耦合在一起,会有多于一个原因来导致这个类发生变化。一个职责的变化可能会影响到其他的职责,另外,把多个职责耦合在一起,影响复用性。
单一职责原则的优点
(1)降低类的复杂度;
(2)提高类的可读性,提高系统的可维护性;
(3)降低变更引起的风险(降低对其他功能的影响)。
要求接口的职责单一,从而实现该接口的类的职责单一