设计模式-6大设计原则
一、单一职责原则(SRP)
定义:应该有且只有一个原因引起类的变更
二、里氏替换原则(LSP)
定义:所有引用基类的地方必须能透明地使用其子类的对象。
即只要父类出现的地方子类就可以出现,而且替换为子类也不会产生任何错误和异常,使用者可能根本不需要知道是父类还是子类,但是反过来不可以,有子类出现的地方,父类未必可以使用
这个定义包含了4层含义
1.子类必须完全实现父类的方法
子类必须完全实现父类方法,不然会导致父类对象调用是产生异常,如
interface IHandle{
void method1();
void method2();
}
class ImpHandleA implements IHandle {
@Override
public void method1() {
System.out.println("ImpHandleA method1");
}
@Override
public void method2() {
System.out.println("ImpHandleA method2");
}
}
class ImpHandleB implements IHandle {
@Override
public void method1() {
System.out.println("ImpHandleB method1");
}
@Override
public void method2() {
throw new IllegalStateException("不允许的方法调用");
}
}
public static void main(String[] args) {
IHandle handle = new ImpHandleA();
handle.method1();
handle.method2();
handle = new ImpHandleB();
handle.method1();
//此时调用method2出错
handle.method2();
}
如上代码,ImpHandleB未完全实现IHandle接口,导致ImpHandleB的实例调用method2时报错,或者ImpHandleB只做了空实现如
@Override
public void method2() {
}
这样调用代码虽然不再报错,但却无法达到需要的执行效果,如果在刚方法执行后面有依赖于该方法执行结果的代码,则会产生未知的错误;
所以,如果子类不能完整地实现父类的方法,或者父类的某些方法在子类已经发生“畸变”,则建议断开父子继承关系,采用依赖、组合、聚集等你关系代替。
2.子类可以有自己的个性
子类可以有自己的方法和属性,所以里氏替换原则可以正着用,不能反着用,如
class SupperClass {
public void method() {
System.out.println("SupperClass method1");
}
}
class SubClass extends SupperClass {
@Override
public void method() {
System.out.println("SubClass method");
}
public void otherMethod() {
System.out.println("SubClass otherMethod");
}
}
public static void main(String[] args) {
SupperClass supperClass = new SupperClass();
supperClass.method();
//此处运行报错
((SubClass) supperClass).otherMethod();
}
不能将父类实例转为子类实例
3.覆盖或实现父类的方法时输入参数可以被放大
即子类的方法 参数类型可以为父类的方法 参数类型 的父类
class Father {
public void method(HashMap map) {
System.out.println("父类方法被执行");
}
}
class Son extends Father {
public void method(Map map) {
System.out.println("子类方法被执行");
}
}
public static void main(String[] args) {
Father father=new Father;
HashMap map=new HashMap();
father.method(map);
// 输出:父类方法被执行
Son son=new Son();
son.method(map);
//同样输出:父类方法被执行
//符合里氏替换原则,所有引用基类的地方必须能透明地使用其子类的对象
}
而如果反过来
class Father {
public void method(Map map) {
System.out.println("父类方法被执行");
}
}
class Son extends Father {
public void method(HashMap map) {
System.out.println("子类方法被执行");
}
}
public static void main(String[] args) {
Father father=new Father;
HashMap map=new HashMap();
father.method(map);
// 输出:父类方法被执行
Son son=new Son();
son.method(map);
//输出:子类方法被执行
//不再符合里氏替换原则,子类在没有复写父类方法的前提下,子类方法被执行了,会引起逻辑混乱
}
4.覆写或实现父类的方法时输出结果可以被缩小
即子类方法返回的类型 可以是父类方法返回类型的 子类型
三、依赖倒置原则(DIP)
定义:
-
高层模块不应该依赖底层模块,两者都应该依赖其抽象
-
抽象不应该依赖细节;
-
细节应该依赖抽象
更加精简的定义为:面向接口编程
依赖倒置原则的本质就是通过抽象使各个类或模块的实现彼此独立,不互相影响,实现模块的松耦合。
要想实现上述规则,只要遵循以下几个原则
-
每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备
-
变量的表面类型尽量都是接口或者是抽象类
-
任何类都不应该从具体类派生
-
尽量不要覆写基类的方法
-
综合里氏替换原则使用
四、接口隔离原则
定义:客户端不应该依赖他不需要的接口, 类间的依赖关系应该建立在最小的接口上。
可以将上面概括为:建立单一接口,不要建立臃肿庞大的接口。接口要尽量细化,同时接口中的方法尽量少(想一下单一职责原则)
使用原则
- 一个接口只服务与一个子模块或业务逻辑
- 通过业务逻辑压缩接口中的public方法,接口时常去回顾,尽量不要让借口有一大堆方法
- 已经被污染了的接口,尽量去修改,若变更的风险较大,则采用适配器模式进行转化处理
- 了解环境,拒绝盲从。具体问题具体分析,不照搬照抄
五、迪米特法则(LOD)=最少知识原则(LKP)
定义:一个对象应该对其他对象有最少的了解。
一个类应该对自己需要耦合或调用的类知道得最少,被调用或耦合的类 的内部是如何复杂都与调用者没有关系,调用类只要知道,调用输入什么,调用完返回什么就可以了
使用原则:
- 只和朋友(有耦合的类)交流,不关系其他的类
- 朋友间也是有距离的。不需要知道朋友的所有细节
- 是自己的就是自己。如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响,那就放在本类中。
- 谨慎使用Serializable。
六、开闭原则
定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
理解:当类完成编写后,后续的功能添加等应该尽量用继承之类的方式而不是在已经完成的类里修改